mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-03-16 20:43:48 +00:00
18
CHANGELOG.md
18
CHANGELOG.md
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
|
## [4.5.1] - 2025-08-13
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1637](https://github.com/shlinkio/shlink-web-client/issues/1637) Fix brand color used in PWA
|
||||||
|
* [#1636](https://github.com/shlinkio/shlink-web-client/issues/1636) Make sure sidebar toggle is rendered only in sections where the sidebar exists.
|
||||||
|
|
||||||
|
|
||||||
## [4.5.0] - 2025-08-08
|
## [4.5.0] - 2025-08-08
|
||||||
### Added
|
### Added
|
||||||
* [shlink-web-component#755](https://github.com/shlinkio/shlink-web-component/issues/755) Add support for `any-value-query-param` and `valueless-query-param` redirect conditions when using Shlink >=4.5.0.
|
* [shlink-web-component#755](https://github.com/shlinkio/shlink-web-component/issues/755) Add support for `any-value-query-param` and `valueless-query-param` redirect conditions when using Shlink >=4.5.0.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:24.4-alpine AS node
|
FROM node:24.5-alpine AS node
|
||||||
COPY . /shlink-web-client
|
COPY . /shlink-web-client
|
||||||
ARG VERSION="latest"
|
ARG VERSION="latest"
|
||||||
ENV VERSION=${VERSION}
|
ENV VERSION=${VERSION}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<meta name="theme-color" content="#4696e5">
|
<meta name="theme-color" content="#2078CF" media="(prefers-color-scheme: light)">
|
||||||
|
<meta name="theme-color" content="#0B2D4E" media="(prefers-color-scheme: dark)">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is added to the
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import { BRAND_COLOR_LM } from '@shlinkio/shlink-frontend-kit';
|
||||||
import type { ManifestOptions } from 'vite-plugin-pwa';
|
import type { ManifestOptions } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
export const manifest: Partial<ManifestOptions> = {
|
export const manifest: Partial<ManifestOptions> = {
|
||||||
short_name: 'Shlink',
|
short_name: 'Shlink',
|
||||||
name: 'Shlink',
|
name: 'Shlink Web Client',
|
||||||
start_url: '/',
|
start_url: '/',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
theme_color: '#4696e5',
|
theme_color: BRAND_COLOR_LM, // Toolbar color
|
||||||
background_color: '#4696e5',
|
background_color: BRAND_COLOR_LM, // Splash screen background color
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: './icons/icon-16x16.png',
|
src: './icons/icon-16x16.png',
|
||||||
|
|||||||
817
package-lock.json
generated
817
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -41,7 +41,7 @@
|
|||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-external-link": "^2.5.0",
|
"react-external-link": "^2.5.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router": "^7.7.1",
|
"react-router": "^7.8.0",
|
||||||
"redux-localstorage-simple": "^2.5.1",
|
"redux-localstorage-simple": "^2.5.1",
|
||||||
"workbox-core": "^7.3.0",
|
"workbox-core": "^7.3.0",
|
||||||
"workbox-expiration": "^7.3.0",
|
"workbox-expiration": "^7.3.0",
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@shlinkio/eslint-config-js-coding-standard": "~3.5.0",
|
"@shlinkio/eslint-config-js-coding-standard": "~3.5.0",
|
||||||
"@stylistic/eslint-plugin": "^5.2.2",
|
"@stylistic/eslint-plugin": "^5.2.3",
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"@testing-library/jest-dom": "^6.6.4",
|
"@testing-library/jest-dom": "^6.6.4",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
@@ -59,13 +59,13 @@
|
|||||||
"@total-typescript/shoehorn": "^0.1.2",
|
"@total-typescript/shoehorn": "^0.1.2",
|
||||||
"@types/react": "^19.1.9",
|
"@types/react": "^19.1.9",
|
||||||
"@types/react-dom": "^19.1.7",
|
"@types/react-dom": "^19.1.7",
|
||||||
"@vitejs/plugin-react": "^4.7.0",
|
"@vitejs/plugin-react": "^5.0.0",
|
||||||
"@vitest/browser": "^3.2.4",
|
"@vitest/browser": "^3.2.4",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"axe-core": "^4.10.3",
|
"axe-core": "^4.10.3",
|
||||||
"chalk": "^5.4.1",
|
"chalk": "^5.5.0",
|
||||||
"eslint": "^9.32.0",
|
"eslint": "^9.33.0",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
@@ -76,8 +76,8 @@
|
|||||||
"playwright": "^1.54.2",
|
"playwright": "^1.54.2",
|
||||||
"tailwindcss": "^4.1.3",
|
"tailwindcss": "^4.1.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.38.0",
|
"typescript-eslint": "^8.39.0",
|
||||||
"vite": "^7.0.6",
|
"vite": "^7.1.1",
|
||||||
"vite-plugin-pwa": "^1.0.2",
|
"vite-plugin-pwa": "^1.0.2",
|
||||||
"vitest": "^3.0.5"
|
"vitest": "^3.0.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { changeThemeInMarkup, getSystemPreferredTheme } from '@shlinkio/shlink-frontend-kit';
|
import { changeThemeInMarkup, getSystemPreferredTheme } from '@shlinkio/shlink-frontend-kit';
|
||||||
import { ShlinkSidebarToggleButton, ShlinkSidebarVisibilityProvider } from '@shlinkio/shlink-web-component';
|
|
||||||
import type { Settings } from '@shlinkio/shlink-web-component/settings';
|
import type { Settings } from '@shlinkio/shlink-web-component/settings';
|
||||||
import { clsx } from 'clsx';
|
import { clsx } from 'clsx';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
@@ -63,8 +62,7 @@ const App: FCWithDeps<AppProps, AppDeps> = (
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<ShlinkSidebarVisibilityProvider>
|
<>
|
||||||
<ShlinkSidebarToggleButton className="fixed top-3.5 left-3 z-901" />
|
|
||||||
<MainHeader />
|
<MainHeader />
|
||||||
|
|
||||||
<div className="h-full pt-(--header-height)">
|
<div className="h-full pt-(--header-height)">
|
||||||
@@ -94,7 +92,7 @@ const App: FCWithDeps<AppProps, AppDeps> = (
|
|||||||
<ShlinkVersionsContainer />
|
<ShlinkVersionsContainer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ShlinkSidebarVisibilityProvider>
|
</>
|
||||||
|
|
||||||
<AppUpdateBanner isOpen={appUpdated} onClose={resetAppUpdate} forceUpdate={forceUpdate} />
|
<AppUpdateBanner isOpen={appUpdated} onClose={resetAppUpdate} forceUpdate={forceUpdate} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import type { ShlinkWebComponentProps, TagColorsStorage } from '@shlinkio/shlink-web-component';
|
import type { TagColorsStorage } from '@shlinkio/shlink-web-component';
|
||||||
|
import {
|
||||||
|
ShlinkSidebarToggleButton,
|
||||||
|
ShlinkSidebarVisibilityProvider,
|
||||||
|
ShlinkWebComponent,
|
||||||
|
} from '@shlinkio/shlink-web-component';
|
||||||
import type { Settings } from '@shlinkio/shlink-web-component/settings';
|
import type { Settings } from '@shlinkio/shlink-web-component/settings';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@@ -17,7 +22,6 @@ type ShlinkWebComponentContainerProps = WithSelectedServerProps & {
|
|||||||
type ShlinkWebComponentContainerDeps = {
|
type ShlinkWebComponentContainerDeps = {
|
||||||
buildShlinkApiClient: ShlinkApiClientBuilder,
|
buildShlinkApiClient: ShlinkApiClientBuilder,
|
||||||
TagColorsStorage: TagColorsStorage,
|
TagColorsStorage: TagColorsStorage,
|
||||||
ShlinkWebComponent: FC<ShlinkWebComponentProps>,
|
|
||||||
ServerError: FC,
|
ServerError: FC,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +36,6 @@ const ShlinkWebComponentContainer: FCWithDeps<
|
|||||||
const {
|
const {
|
||||||
buildShlinkApiClient,
|
buildShlinkApiClient,
|
||||||
TagColorsStorage: tagColorsStorage,
|
TagColorsStorage: tagColorsStorage,
|
||||||
ShlinkWebComponent,
|
|
||||||
ServerError,
|
ServerError,
|
||||||
} = useDependencies(ShlinkWebComponentContainer);
|
} = useDependencies(ShlinkWebComponentContainer);
|
||||||
|
|
||||||
@@ -42,23 +45,25 @@ const ShlinkWebComponentContainer: FCWithDeps<
|
|||||||
|
|
||||||
const routesPrefix = `/server/${selectedServer.id}`;
|
const routesPrefix = `/server/${selectedServer.id}`;
|
||||||
return (
|
return (
|
||||||
<ShlinkWebComponent
|
<ShlinkSidebarVisibilityProvider>
|
||||||
serverVersion={selectedServer.version}
|
<ShlinkSidebarToggleButton className="fixed top-3.5 left-3 z-901" />
|
||||||
apiClient={buildShlinkApiClient(selectedServer)}
|
<ShlinkWebComponent
|
||||||
settings={settings}
|
serverVersion={selectedServer.version}
|
||||||
routesPrefix={routesPrefix}
|
apiClient={buildShlinkApiClient(selectedServer)}
|
||||||
tagColorsStorage={tagColorsStorage}
|
settings={settings}
|
||||||
createNotFound={(nonPrefixedHomePath) => (
|
routesPrefix={routesPrefix}
|
||||||
<NotFound to={`${routesPrefix}${nonPrefixedHomePath}`}>List short URLs</NotFound>
|
tagColorsStorage={tagColorsStorage}
|
||||||
)}
|
createNotFound={(nonPrefixedHomePath: string) => (
|
||||||
autoSidebarToggle={false}
|
<NotFound to={`${routesPrefix}${nonPrefixedHomePath}`}>List short URLs</NotFound>
|
||||||
/>
|
)}
|
||||||
|
autoSidebarToggle={false}
|
||||||
|
/>
|
||||||
|
</ShlinkSidebarVisibilityProvider>
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ShlinkWebComponentContainerFactory = componentFactory(ShlinkWebComponentContainer, [
|
export const ShlinkWebComponentContainerFactory = componentFactory(ShlinkWebComponentContainer, [
|
||||||
'buildShlinkApiClient',
|
'buildShlinkApiClient',
|
||||||
'TagColorsStorage',
|
'TagColorsStorage',
|
||||||
'ShlinkWebComponent',
|
|
||||||
'ServerError',
|
'ServerError',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { FetchHttpClient } from '@shlinkio/shlink-js-sdk/fetch';
|
import { FetchHttpClient } from '@shlinkio/shlink-js-sdk/fetch';
|
||||||
import { ShlinkWebComponent } from '@shlinkio/shlink-web-component';
|
|
||||||
import type Bottle from 'bottlejs';
|
import type Bottle from 'bottlejs';
|
||||||
import type { ConnectDecorator } from '../../container/types';
|
import type { ConnectDecorator } from '../../container/types';
|
||||||
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
||||||
@@ -26,7 +25,6 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
|||||||
bottle.decorator('Home', withoutSelectedServer);
|
bottle.decorator('Home', withoutSelectedServer);
|
||||||
bottle.decorator('Home', connect(['servers'], ['resetSelectedServer']));
|
bottle.decorator('Home', connect(['servers'], ['resetSelectedServer']));
|
||||||
|
|
||||||
bottle.serviceFactory('ShlinkWebComponent', () => ShlinkWebComponent);
|
|
||||||
bottle.factory('ShlinkWebComponentContainer', ShlinkWebComponentContainerFactory);
|
bottle.factory('ShlinkWebComponentContainer', ShlinkWebComponentContainerFactory);
|
||||||
bottle.decorator('ShlinkWebComponentContainer', connect(['selectedServer', 'settings'], ['selectServer']));
|
bottle.decorator('ShlinkWebComponentContainer', connect(['selectedServer', 'settings'], ['selectServer']));
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../s
|
|||||||
import { checkAccessibility } from '../__helpers__/accessibility';
|
import { checkAccessibility } from '../__helpers__/accessibility';
|
||||||
import { MemoryRouterWithParams } from '../__helpers__/MemoryRouterWithParams';
|
import { MemoryRouterWithParams } from '../__helpers__/MemoryRouterWithParams';
|
||||||
|
|
||||||
|
vi.mock('@shlinkio/shlink-web-component', () => ({
|
||||||
|
ShlinkSidebarVisibilityProvider: ({ children }: any) => children,
|
||||||
|
ShlinkSidebarToggleButton: ({ children }: any) => children,
|
||||||
|
ShlinkWebComponent: () => <>ShlinkWebComponent</>,
|
||||||
|
}));
|
||||||
|
|
||||||
describe('<ShlinkWebComponentContainer />', () => {
|
describe('<ShlinkWebComponentContainer />', () => {
|
||||||
const ShlinkWebComponentContainer = ShlinkWebComponentContainerFactory(fromPartial({
|
const ShlinkWebComponentContainer = ShlinkWebComponentContainerFactory(fromPartial({
|
||||||
buildShlinkApiClient: vi.fn().mockReturnValue(fromPartial({})),
|
buildShlinkApiClient: vi.fn().mockReturnValue(fromPartial({})),
|
||||||
TagColorsStorage: fromPartial({}),
|
TagColorsStorage: fromPartial({}),
|
||||||
ShlinkWebComponent: () => <>ShlinkWebComponent</>,
|
|
||||||
ServerError: () => <>ServerError</>,
|
ServerError: () => <>ServerError</>,
|
||||||
}));
|
}));
|
||||||
const setUp = (selectedServer: SelectedServer) => render(
|
const setUp = (selectedServer: SelectedServer) => render(
|
||||||
|
|||||||
Reference in New Issue
Block a user