mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-02-27 04:06:39 +00:00
Refactor and fix main app tests
This commit is contained in:
@@ -109,7 +109,7 @@ export class ShlinkApiClient implements BaseShlinkApiClient {
|
||||
.then(({ data }) => ({ tags: data.map(({ tag }) => tag), stats: data }));
|
||||
|
||||
public readonly deleteTags = async (tags: string[]): Promise<{ tags: string[] }> =>
|
||||
this.performEmptyRequest({ url: '/tags', method: 'DELETE', body: { tags } }).then(() => ({ tags }));
|
||||
this.performEmptyRequest({ url: '/tags', method: 'DELETE', query: { tags } }).then(() => ({ tags }));
|
||||
|
||||
public readonly editTag = async (oldName: string, newName: string): Promise<{ oldName: string; newName: string }> =>
|
||||
this.performEmptyRequest({
|
||||
|
||||
@@ -21,7 +21,7 @@ interface AppProps {
|
||||
export const App = (
|
||||
MainHeader: FC,
|
||||
Home: FC,
|
||||
MenuLayout: FC,
|
||||
ShlinkWebComponentContainer: FC,
|
||||
CreateServer: FC,
|
||||
EditServer: FC,
|
||||
SettingsComp: FC,
|
||||
@@ -52,7 +52,7 @@ export const App = (
|
||||
<Route path="/manage-servers" element={<ManageServers />} />
|
||||
<Route path="/server/create" element={<CreateServer />} />
|
||||
<Route path="/server/:serverId/edit" element={<EditServer />} />
|
||||
<Route path="/server/:serverId/*" element={<MenuLayout />} />
|
||||
<Route path="/server/:serverId/*" element={<ShlinkWebComponentContainer />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||
App,
|
||||
'MainHeader',
|
||||
'Home',
|
||||
'MenuLayout',
|
||||
'ShlinkWebComponentContainer',
|
||||
'CreateServer',
|
||||
'EditServer',
|
||||
'Settings',
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import type { FC } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import type { Settings } from '../../shlink-web-component/src';
|
||||
import { ShlinkWebComponent } from '../../shlink-web-component/src';
|
||||
import type { Settings, ShlinkWebComponentType } from '../../shlink-web-component/src';
|
||||
import type { ShlinkApiClientBuilder } from '../api/services/ShlinkApiClientBuilder';
|
||||
import { isReachableServer } from '../servers/data';
|
||||
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||
import { NotFound } from './NotFound';
|
||||
import './MenuLayout.scss';
|
||||
import './ShlinkWebComponentContainer.scss';
|
||||
|
||||
interface MenuLayoutProps {
|
||||
interface ShlinkWebComponentContainerProps {
|
||||
sidebarPresent: Function;
|
||||
sidebarNotPresent: Function;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
// FIXME Rename this to something else
|
||||
export const MenuLayout = (
|
||||
export const ShlinkWebComponentContainer = (
|
||||
buildShlinkApiClient: ShlinkApiClientBuilder,
|
||||
ShlinkWebComponent: ShlinkWebComponentType,
|
||||
ServerError: FC,
|
||||
) => withSelectedServer<MenuLayoutProps>(({ selectedServer, sidebarNotPresent, sidebarPresent, settings }) => {
|
||||
const showContent = isReachableServer(selectedServer);
|
||||
const routesPrefix = showContent ? `/server/${selectedServer.id}` : '';
|
||||
) => withSelectedServer<ShlinkWebComponentContainerProps>((
|
||||
{ selectedServer, sidebarNotPresent, sidebarPresent, settings },
|
||||
) => {
|
||||
const selectedServerIsReachable = isReachableServer(selectedServer);
|
||||
const routesPrefix = selectedServerIsReachable ? `/server/${selectedServer.id}` : '';
|
||||
|
||||
useEffect(() => {
|
||||
showContent && sidebarPresent();
|
||||
selectedServerIsReachable && sidebarPresent();
|
||||
return () => sidebarNotPresent();
|
||||
}, []);
|
||||
|
||||
if (!showContent) {
|
||||
if (!selectedServerIsReachable) {
|
||||
return <ServerError />;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
// TODO This is only used for some components to have extra paddings/styles if existing section has a side menu
|
||||
// Now that's basically the route which renders ShlinkWebComponent, so maybe there's some way to re-think this
|
||||
// logic, and perhaps get rid of a reducer just for that
|
||||
// FIXME This is only used for some components to have extra paddings/styles if existing section has a side menu
|
||||
// Now that's basically the route which renders ShlinkWebComponent, so maybe there's some way to re-think this
|
||||
// logic, and perhaps get rid of a reducer just for that
|
||||
|
||||
export interface Sidebar {
|
||||
sidebarPresent: boolean;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type Bottle from 'bottlejs';
|
||||
import { ShlinkWebComponent } from '../../../shlink-web-component/src';
|
||||
import type { ConnectDecorator } from '../../container/types';
|
||||
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
||||
import { ErrorHandler } from '../ErrorHandler';
|
||||
import { Home } from '../Home';
|
||||
import { MainHeader } from '../MainHeader';
|
||||
import { MenuLayout } from '../MenuLayout';
|
||||
import { sidebarNotPresent, sidebarPresent } from '../reducers/sidebar';
|
||||
import { ScrollToTop } from '../ScrollToTop';
|
||||
import { ShlinkVersionsContainer } from '../ShlinkVersionsContainer';
|
||||
import { ShlinkWebComponentContainer } from '../ShlinkWebComponentContainer';
|
||||
import { HttpClient } from './HttpClient';
|
||||
|
||||
export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||
@@ -26,8 +27,15 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||
bottle.decorator('Home', withoutSelectedServer);
|
||||
bottle.decorator('Home', connect(['servers'], ['resetSelectedServer']));
|
||||
|
||||
bottle.serviceFactory('MenuLayout', MenuLayout, 'buildShlinkApiClient', 'ServerError');
|
||||
bottle.decorator('MenuLayout', connect(
|
||||
bottle.serviceFactory('ShlinkWebComponent', () => ShlinkWebComponent);
|
||||
bottle.serviceFactory(
|
||||
'ShlinkWebComponentContainer',
|
||||
ShlinkWebComponentContainer,
|
||||
'buildShlinkApiClient',
|
||||
'ShlinkWebComponent',
|
||||
'ServerError',
|
||||
);
|
||||
bottle.decorator('ShlinkWebComponentContainer', connect(
|
||||
['selectedServer', 'settings'],
|
||||
['selectServer', 'sidebarPresent', 'sidebarNotPresent'],
|
||||
));
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { DropdownItem, FormGroup } from 'reactstrap';
|
||||
import { DropdownBtn, LabeledFormGroup, SimpleCard, ToggleSwitch } from '../../shlink-frontend-kit/src';
|
||||
import type { Settings } from '../../shlink-web-component/src';
|
||||
import type { Settings, ShortUrlCreationSettings as ShortUrlsSettings } from '../../shlink-web-component/src';
|
||||
import { FormText } from '../utils/forms/FormText';
|
||||
import type { Defined } from '../utils/types';
|
||||
|
||||
type ShortUrlsSettings = Defined<Settings['shortUrlCreation']>;
|
||||
type TagFilteringMode = Defined<ShortUrlsSettings['tagFilteringMode']>;
|
||||
|
||||
interface ShortUrlCreationProps {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import type { FC } from 'react';
|
||||
import { LabeledFormGroup, OrderingDropdown, SimpleCard } from '../../shlink-frontend-kit/src';
|
||||
import type { Settings } from '../../shlink-web-component/src';
|
||||
import type { Settings, ShortUrlsListSettings as ShortUrlsSettings } from '../../shlink-web-component/src';
|
||||
import { SHORT_URLS_ORDERABLE_FIELDS } from '../../shlink-web-component/src/short-urls/data';
|
||||
import type { Defined } from '../utils/types';
|
||||
import { DEFAULT_SHORT_URLS_ORDERING } from './reducers/settings';
|
||||
|
||||
type ShortUrlsSettings = Defined<Settings['shortUrlsList']>;
|
||||
|
||||
interface ShortUrlsListSettingsProps {
|
||||
settings: Settings;
|
||||
setShortUrlsListSettings: (settings: ShortUrlsSettings) => void;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import type { FC } from 'react';
|
||||
import { LabeledFormGroup, OrderingDropdown, SimpleCard } from '../../shlink-frontend-kit/src';
|
||||
import type { Settings } from '../../shlink-web-component/src';
|
||||
import type { Settings, TagsSettings as TagsSettingsOptions } from '../../shlink-web-component/src';
|
||||
import { TAGS_ORDERABLE_FIELDS } from '../../shlink-web-component/src/tags/data/TagsListChildrenProps';
|
||||
import type { Defined } from '../utils/types';
|
||||
|
||||
type TagsSettingsOptions = Defined<Settings['tags']>;
|
||||
|
||||
interface TagsProps {
|
||||
settings: Settings;
|
||||
|
||||
@@ -2,14 +2,11 @@ import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import type { FC } from 'react';
|
||||
import { SimpleCard, ToggleSwitch } from '../../shlink-frontend-kit/src';
|
||||
import type { Settings } from '../../shlink-web-component/src';
|
||||
import type { Settings, UiSettings } from '../../shlink-web-component/src';
|
||||
import type { Theme } from '../utils/theme';
|
||||
import { changeThemeInMarkup } from '../utils/theme';
|
||||
import type { Defined } from '../utils/types';
|
||||
import './UserInterfaceSettings.scss';
|
||||
|
||||
type UiSettings = Defined<Settings['ui']>;
|
||||
|
||||
interface UserInterfaceProps {
|
||||
settings: Settings;
|
||||
setUiSettings: (settings: UiSettings) => void;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import type { FC } from 'react';
|
||||
import { FormGroup } from 'reactstrap';
|
||||
import { LabeledFormGroup, SimpleCard, ToggleSwitch } from '../../shlink-frontend-kit/src';
|
||||
import type { Settings } from '../../shlink-web-component/src';
|
||||
import type { Settings, VisitsSettings as VisitsSettingsConfig } from '../../shlink-web-component/src';
|
||||
import type { DateInterval } from '../../shlink-web-component/src/utils/dates/helpers/dateIntervals';
|
||||
import { DateIntervalSelector } from '../utils/dates/DateIntervalSelector';
|
||||
import { FormText } from '../utils/forms/FormText';
|
||||
|
||||
type VisitsSettingsConfig = Settings['visits'];
|
||||
|
||||
interface VisitsProps {
|
||||
settings: Settings;
|
||||
setVisitsSettings: (settings: VisitsSettingsConfig) => void;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import type { PayloadAction, PrepareAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { mergeDeepRight } from 'ramda';
|
||||
import type { Settings } from '../../../shlink-web-component/src';
|
||||
import type {
|
||||
Settings,
|
||||
ShortUrlCreationSettings,
|
||||
ShortUrlsListSettings,
|
||||
TagsSettings,
|
||||
UiSettings, VisitsSettings } from '../../../shlink-web-component/src';
|
||||
import type { Defined } from '../../utils/types';
|
||||
|
||||
type ShortUrlsOrder = Defined<Defined<Settings['shortUrlsList']>['defaultOrdering']>;
|
||||
type ShortUrlsOrder = Defined<ShortUrlsListSettings['defaultOrdering']>;
|
||||
|
||||
export const DEFAULT_SHORT_URLS_ORDERING: ShortUrlsOrder = {
|
||||
field: 'dateCreated',
|
||||
@@ -43,14 +48,14 @@ const { reducer, actions } = createSlice({
|
||||
toggleRealTimeUpdates: toReducer((enabled: boolean) => toPreparedAction({ realTimeUpdates: { enabled } })),
|
||||
setRealTimeUpdatesInterval: toReducer((interval: number) => toPreparedAction({ realTimeUpdates: { interval } })),
|
||||
setShortUrlCreationSettings: toReducer(
|
||||
(shortUrlCreation: Settings['shortUrlCreation']) => toPreparedAction({ shortUrlCreation }),
|
||||
(shortUrlCreation: ShortUrlCreationSettings) => toPreparedAction({ shortUrlCreation }),
|
||||
),
|
||||
setShortUrlsListSettings: toReducer(
|
||||
(shortUrlsList: Settings['shortUrlsList']) => toPreparedAction({ shortUrlsList }),
|
||||
(shortUrlsList: ShortUrlsListSettings) => toPreparedAction({ shortUrlsList }),
|
||||
),
|
||||
setUiSettings: toReducer((ui: Settings['ui']) => toPreparedAction({ ui })),
|
||||
setVisitsSettings: toReducer((visits: Settings['visits']) => toPreparedAction({ visits })),
|
||||
setTagsSettings: toReducer((tags: Settings['tags']) => toPreparedAction({ tags })),
|
||||
setUiSettings: toReducer((ui: UiSettings) => toPreparedAction({ ui })),
|
||||
setVisitsSettings: toReducer((visits: VisitsSettings) => toPreparedAction({ visits })),
|
||||
setTagsSettings: toReducer((tags: TagsSettings) => toPreparedAction({ tags })),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { FC } from 'react';
|
||||
import { DropdownItem } from 'reactstrap';
|
||||
import { DropdownBtn } from '../../../shlink-frontend-kit/src';
|
||||
import type { Settings } from '../../../shlink-web-component/src';
|
||||
import type { VisitsSettings } from '../../../shlink-web-component/src';
|
||||
import { rangeOrIntervalToString } from '../../../shlink-web-component/src/utils/dates/helpers/dateIntervals';
|
||||
import type { Defined } from '../types';
|
||||
|
||||
export type DateInterval = Defined<Settings['visits']>['defaultInterval'];
|
||||
export type DateInterval = VisitsSettings['defaultInterval'];
|
||||
|
||||
export interface DateIntervalSelectorProps {
|
||||
active?: DateInterval;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { compare } from 'compare-versions';
|
||||
import { identity, isEmpty, isNil, memoizeWith } from 'ramda';
|
||||
|
||||
type Empty = null | undefined | '' | never[];
|
||||
export type Empty = null | undefined | '' | never[];
|
||||
|
||||
const hasValue = <T>(value: T | Empty): value is T => !isNil(value) && !isEmpty(value);
|
||||
|
||||
@@ -11,7 +11,7 @@ type SemVerPattern = SemVerPatternFragment
|
||||
| `${SemVerPatternFragment}.${SemVerPatternFragment}`
|
||||
| `${SemVerPatternFragment}.${SemVerPatternFragment}.${SemVerPatternFragment}`;
|
||||
|
||||
type Versions = {
|
||||
export type Versions = {
|
||||
maxVersion?: SemVerPattern;
|
||||
minVersion?: SemVerPattern;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user