mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-10 02:07:26 +03:00
Created new settings card for tags-related options
This commit is contained in:
parent
e77508edcc
commit
5598fe0f53
14 changed files with 121 additions and 58 deletions
|
@ -18,9 +18,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
### Changed
|
### Changed
|
||||||
* [#534](https://github.com/shlinkio/shlink-web-client/pull/534) Updated axios.
|
* [#534](https://github.com/shlinkio/shlink-web-client/pull/534) Updated axios.
|
||||||
* [#538](https://github.com/shlinkio/shlink-web-client/pull/538) Switched to the `<field>-<dir>` notation in `orderBy` param for short URLs list, in preparation for Shlink v3.0.0
|
* [#538](https://github.com/shlinkio/shlink-web-client/pull/538) Switched to the `<field>-<dir>` notation in `orderBy` param for short URLs list, in preparation for Shlink v3.0.0
|
||||||
* Fixed typo in identifier for "Last 180 days" interval.
|
|
||||||
|
|
||||||
If that was your default interval, you will see now "All visits" is selected instead. You will need to go to settings page and change it again to "Last 180 days".
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
* *Nothing*
|
* *Nothing*
|
||||||
|
|
|
@ -18,7 +18,8 @@ import { ConnectDecorator } from './types';
|
||||||
type LazyActionMap = Record<string, Function>;
|
type LazyActionMap = Record<string, Function>;
|
||||||
|
|
||||||
const bottle = new Bottle();
|
const bottle = new Bottle();
|
||||||
const { container } = bottle;
|
|
||||||
|
export const { container } = bottle;
|
||||||
|
|
||||||
const lazyService = <T extends Function, K>(container: IContainer, serviceName: string) =>
|
const lazyService = <T extends Function, K>(container: IContainer, serviceName: string) =>
|
||||||
(...args: any[]) => (container[serviceName] as T)(...args) as K;
|
(...args: any[]) => (container[serviceName] as T)(...args) as K;
|
||||||
|
@ -44,5 +45,3 @@ provideUtilsServices(bottle);
|
||||||
provideMercureServices(bottle);
|
provideMercureServices(bottle);
|
||||||
provideSettingsServices(bottle, connect);
|
provideSettingsServices(bottle, connect);
|
||||||
provideDomainsServices(bottle, connect);
|
provideDomainsServices(bottle, connect);
|
||||||
|
|
||||||
export default container;
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ import ReduxThunk from 'redux-thunk';
|
||||||
import { applyMiddleware, compose, createStore } from 'redux';
|
import { applyMiddleware, compose, createStore } from 'redux';
|
||||||
import { save, load, RLSOptions } from 'redux-localstorage-simple';
|
import { save, load, RLSOptions } from 'redux-localstorage-simple';
|
||||||
import reducers from '../reducers';
|
import reducers from '../reducers';
|
||||||
|
import { migrateDeprecatedSettings } from '../settings/helpers';
|
||||||
|
import { ShlinkState } from './types';
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV !== 'production';
|
const isProduction = process.env.NODE_ENV !== 'production';
|
||||||
const composeEnhancers: Function = !isProduction && (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
const composeEnhancers: Function = !isProduction && (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||||
|
@ -12,9 +14,8 @@ const localStorageConfig: RLSOptions = {
|
||||||
namespaceSeparator: '.',
|
namespaceSeparator: '.',
|
||||||
debounce: 300,
|
debounce: 300,
|
||||||
};
|
};
|
||||||
|
const preloadedState = migrateDeprecatedSettings(load(localStorageConfig) as ShlinkState);
|
||||||
|
|
||||||
const store = createStore(reducers, load(localStorageConfig), composeEnhancers(
|
export const store = createStore(reducers, preloadedState, composeEnhancers(
|
||||||
applyMiddleware(save(localStorageConfig), ReduxThunk),
|
applyMiddleware(save(localStorageConfig), ReduxThunk),
|
||||||
));
|
));
|
||||||
|
|
||||||
export default store;
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { render } from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { homepage } from '../package.json';
|
import { homepage } from '../package.json';
|
||||||
import container from './container';
|
import { container } from './container';
|
||||||
import store from './container/store';
|
import { store } from './container/store';
|
||||||
import { fixLeafletIcons } from './utils/helpers/leaflet';
|
import { fixLeafletIcons } from './utils/helpers/leaflet';
|
||||||
import { register as registerServiceWorker } from './serviceWorkerRegistration';
|
import { register as registerServiceWorker } from './serviceWorkerRegistration';
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
|
|
|
@ -7,7 +7,7 @@ const SettingsSections: FC<{ items: ReactNode[][] }> = ({ items }) => (
|
||||||
{items.map((child, index) => (
|
{items.map((child, index) => (
|
||||||
<Row key={index}>
|
<Row key={index}>
|
||||||
{child.map((subChild, subIndex) => (
|
{child.map((subChild, subIndex) => (
|
||||||
<div key={subIndex} className="col-lg-6 mb-3">
|
<div key={subIndex} className={`col-lg-${12 / child.length} mb-3`}>
|
||||||
{subChild}
|
{subChild}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -16,12 +16,13 @@ const SettingsSections: FC<{ items: ReactNode[][] }> = ({ items }) => (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Settings = (RealTimeUpdates: FC, ShortUrlCreation: FC, UserInterface: FC, Visits: FC) => () => (
|
const Settings = (RealTimeUpdates: FC, ShortUrlCreation: FC, UserInterface: FC, Visits: FC, Tags: FC) => () => (
|
||||||
<NoMenuLayout>
|
<NoMenuLayout>
|
||||||
<SettingsSections
|
<SettingsSections
|
||||||
items={[
|
items={[
|
||||||
[ <UserInterface />, <Visits /> ], // eslint-disable-line react/jsx-key
|
[ <UserInterface /> ], // eslint-disable-line react/jsx-key
|
||||||
[ <ShortUrlCreation />, <RealTimeUpdates /> ], // eslint-disable-line react/jsx-key
|
[ <ShortUrlCreation />, <RealTimeUpdates /> ], // eslint-disable-line react/jsx-key
|
||||||
|
[ <Tags />, <Visits /> ], // eslint-disable-line react/jsx-key
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</NoMenuLayout>
|
</NoMenuLayout>
|
||||||
|
|
25
src/settings/Tags.tsx
Normal file
25
src/settings/Tags.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { FormGroup } from 'reactstrap';
|
||||||
|
import { SimpleCard } from '../utils/SimpleCard';
|
||||||
|
import { TagsModeDropdown } from '../tags/TagsModeDropdown';
|
||||||
|
import { capitalize } from '../utils/utils';
|
||||||
|
import { Settings, TagsSettings } from './reducers/settings';
|
||||||
|
|
||||||
|
interface TagsProps {
|
||||||
|
settings: Settings;
|
||||||
|
setTagsSettings: (settings: TagsSettings) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tags: FC<TagsProps> = ({ settings: { tags }, setTagsSettings }) => (
|
||||||
|
<SimpleCard title="Tags">
|
||||||
|
<FormGroup className="mb-0">
|
||||||
|
<label>Default display mode when managing tags:</label>
|
||||||
|
<TagsModeDropdown
|
||||||
|
mode={tags?.defaultMode ?? 'cards'}
|
||||||
|
renderTitle={(tagsMode) => capitalize(tagsMode)}
|
||||||
|
onChange={(defaultMode) => setTagsSettings({ ...tags, defaultMode })}
|
||||||
|
/>
|
||||||
|
<small className="form-text text-muted">Tags will be displayed as <b>{tags?.defaultMode ?? 'cards'}</b>.</small>
|
||||||
|
</FormGroup>
|
||||||
|
</SimpleCard>
|
||||||
|
);
|
|
@ -5,8 +5,6 @@ import { FormGroup } from 'reactstrap';
|
||||||
import { SimpleCard } from '../utils/SimpleCard';
|
import { SimpleCard } from '../utils/SimpleCard';
|
||||||
import ToggleSwitch from '../utils/ToggleSwitch';
|
import ToggleSwitch from '../utils/ToggleSwitch';
|
||||||
import { changeThemeInMarkup, Theme } from '../utils/theme';
|
import { changeThemeInMarkup, Theme } from '../utils/theme';
|
||||||
import { TagsModeDropdown } from '../tags/TagsModeDropdown';
|
|
||||||
import { capitalize } from '../utils/utils';
|
|
||||||
import { Settings, UiSettings } from './reducers/settings';
|
import { Settings, UiSettings } from './reducers/settings';
|
||||||
import './UserInterface.scss';
|
import './UserInterface.scss';
|
||||||
|
|
||||||
|
@ -31,14 +29,5 @@ export const UserInterface: FC<UserInterfaceProps> = ({ settings: { ui }, setUiS
|
||||||
Use dark theme.
|
Use dark theme.
|
||||||
</ToggleSwitch>
|
</ToggleSwitch>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup className="mb-0">
|
|
||||||
<label>Default display mode when managing tags:</label>
|
|
||||||
<TagsModeDropdown
|
|
||||||
mode={ui?.tagsMode ?? 'cards'}
|
|
||||||
renderTitle={(tagsMode) => capitalize(tagsMode)}
|
|
||||||
onChange={(tagsMode) => setUiSettings({ ...ui ?? { theme: 'light' }, tagsMode })}
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Tags will be displayed as <b>{ui?.tagsMode ?? 'cards'}</b>.</small>
|
|
||||||
</FormGroup>
|
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
);
|
);
|
||||||
|
|
17
src/settings/helpers/index.ts
Normal file
17
src/settings/helpers/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { ShlinkState } from '../../container/types';
|
||||||
|
|
||||||
|
export const migrateDeprecatedSettings = (state: ShlinkState): ShlinkState => {
|
||||||
|
// The "last180Days" interval had a typo, with a lowercase d
|
||||||
|
if ((state.settings.visits?.defaultInterval as any) === 'last180days') {
|
||||||
|
state.settings.visits && (state.settings.visits.defaultInterval = 'last180Days');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "tags display mode" option has been moved from "ui" to "tags"
|
||||||
|
state.settings.tags = {
|
||||||
|
...state.settings.tags,
|
||||||
|
defaultMode: state.settings.tags?.defaultMode ?? (state.settings.ui as any)?.tagsMode,
|
||||||
|
};
|
||||||
|
state.settings.ui && delete (state.settings.ui as any).tagsMode;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
|
@ -4,6 +4,7 @@ import { buildReducer } from '../../utils/helpers/redux';
|
||||||
import { RecursivePartial } from '../../utils/utils';
|
import { RecursivePartial } from '../../utils/utils';
|
||||||
import { Theme } from '../../utils/theme';
|
import { Theme } from '../../utils/theme';
|
||||||
import { DateInterval } from '../../utils/dates/types';
|
import { DateInterval } from '../../utils/dates/types';
|
||||||
|
import { TagsOrder } from '../../tags/data/TagsListChildrenProps';
|
||||||
|
|
||||||
export const SET_SETTINGS = 'shlink/realTimeUpdates/SET_SETTINGS';
|
export const SET_SETTINGS = 'shlink/realTimeUpdates/SET_SETTINGS';
|
||||||
|
|
||||||
|
@ -29,18 +30,23 @@ export type TagsMode = 'cards' | 'list';
|
||||||
|
|
||||||
export interface UiSettings {
|
export interface UiSettings {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
tagsMode?: TagsMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VisitsSettings {
|
export interface VisitsSettings {
|
||||||
defaultInterval: DateInterval;
|
defaultInterval: DateInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TagsSettings {
|
||||||
|
defaultOrdering?: TagsOrder;
|
||||||
|
defaultMode?: TagsMode;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
realTimeUpdates: RealTimeUpdatesSettings;
|
realTimeUpdates: RealTimeUpdatesSettings;
|
||||||
shortUrlCreation?: ShortUrlCreationSettings;
|
shortUrlCreation?: ShortUrlCreationSettings;
|
||||||
ui?: UiSettings;
|
ui?: UiSettings;
|
||||||
visits?: VisitsSettings;
|
visits?: VisitsSettings;
|
||||||
|
tags?: TagsSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: Settings = {
|
const initialState: Settings = {
|
||||||
|
@ -90,3 +96,8 @@ export const setVisitsSettings = (settings: VisitsSettings): PartialSettingsActi
|
||||||
type: SET_SETTINGS,
|
type: SET_SETTINGS,
|
||||||
visits: settings,
|
visits: settings,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setTagsSettings = (settings: TagsSettings): PartialSettingsAction => ({
|
||||||
|
type: SET_SETTINGS,
|
||||||
|
tags: settings,
|
||||||
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Settings from '../Settings';
|
||||||
import {
|
import {
|
||||||
setRealTimeUpdatesInterval,
|
setRealTimeUpdatesInterval,
|
||||||
setShortUrlCreationSettings,
|
setShortUrlCreationSettings,
|
||||||
|
setTagsSettings,
|
||||||
setUiSettings,
|
setUiSettings,
|
||||||
setVisitsSettings,
|
setVisitsSettings,
|
||||||
toggleRealTimeUpdates,
|
toggleRealTimeUpdates,
|
||||||
|
@ -13,10 +14,11 @@ import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServ
|
||||||
import { ShortUrlCreation } from '../ShortUrlCreation';
|
import { ShortUrlCreation } from '../ShortUrlCreation';
|
||||||
import { UserInterface } from '../UserInterface';
|
import { UserInterface } from '../UserInterface';
|
||||||
import { Visits } from '../Visits';
|
import { Visits } from '../Visits';
|
||||||
|
import { Tags } from '../Tags';
|
||||||
|
|
||||||
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
// Components
|
// Components
|
||||||
bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates', 'ShortUrlCreation', 'UserInterface', 'Visits');
|
bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates', 'ShortUrlCreation', 'UserInterface', 'Visits', 'Tags');
|
||||||
bottle.decorator('Settings', withoutSelectedServer);
|
bottle.decorator('Settings', withoutSelectedServer);
|
||||||
bottle.decorator('Settings', connect(null, [ 'resetSelectedServer' ]));
|
bottle.decorator('Settings', connect(null, [ 'resetSelectedServer' ]));
|
||||||
|
|
||||||
|
@ -35,12 +37,16 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
bottle.serviceFactory('Visits', () => Visits);
|
bottle.serviceFactory('Visits', () => Visits);
|
||||||
bottle.decorator('Visits', connect([ 'settings' ], [ 'setVisitsSettings' ]));
|
bottle.decorator('Visits', connect([ 'settings' ], [ 'setVisitsSettings' ]));
|
||||||
|
|
||||||
|
bottle.serviceFactory('Tags', () => Tags);
|
||||||
|
bottle.decorator('Tags', connect([ 'settings' ], [ 'setTagsSettings' ]));
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('toggleRealTimeUpdates', () => toggleRealTimeUpdates);
|
bottle.serviceFactory('toggleRealTimeUpdates', () => toggleRealTimeUpdates);
|
||||||
bottle.serviceFactory('setRealTimeUpdatesInterval', () => setRealTimeUpdatesInterval);
|
bottle.serviceFactory('setRealTimeUpdatesInterval', () => setRealTimeUpdatesInterval);
|
||||||
bottle.serviceFactory('setShortUrlCreationSettings', () => setShortUrlCreationSettings);
|
bottle.serviceFactory('setShortUrlCreationSettings', () => setShortUrlCreationSettings);
|
||||||
bottle.serviceFactory('setUiSettings', () => setUiSettings);
|
bottle.serviceFactory('setUiSettings', () => setUiSettings);
|
||||||
bottle.serviceFactory('setVisitsSettings', () => setVisitsSettings);
|
bottle.serviceFactory('setVisitsSettings', () => setVisitsSettings);
|
||||||
|
bottle.serviceFactory('setTagsSettings', () => setTagsSettings);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
export default provideServices;
|
||||||
|
|
|
@ -28,7 +28,7 @@ export interface TagsListProps {
|
||||||
const TagsList = (TagsCards: FC<TagsListChildrenProps>, TagsTable: FC<TagsTableProps>) => boundToMercureHub((
|
const TagsList = (TagsCards: FC<TagsListChildrenProps>, TagsTable: FC<TagsTableProps>) => boundToMercureHub((
|
||||||
{ filterTags, forceListTags, tagsList, selectedServer, settings }: TagsListProps,
|
{ filterTags, forceListTags, tagsList, selectedServer, settings }: TagsListProps,
|
||||||
) => {
|
) => {
|
||||||
const [ mode, setMode ] = useState<TagsMode>(settings.ui?.tagsMode ?? 'cards');
|
const [ mode, setMode ] = useState<TagsMode>(settings.tags?.defaultMode ?? 'cards');
|
||||||
const [ order, setOrder ] = useState<TagsOrder>({});
|
const [ order, setOrder ] = useState<TagsOrder>({});
|
||||||
const resolveSortedTags = pipe(
|
const resolveSortedTags = pipe(
|
||||||
() => tagsList.filteredTags.map((tag): NormalizedTag => ({
|
() => tagsList.filteredTags.map((tag): NormalizedTag => ({
|
||||||
|
|
|
@ -4,7 +4,7 @@ import NoMenuLayout from '../../src/common/NoMenuLayout';
|
||||||
|
|
||||||
describe('<Settings />', () => {
|
describe('<Settings />', () => {
|
||||||
const Component = () => null;
|
const Component = () => null;
|
||||||
const Settings = createSettings(Component, Component, Component, Component);
|
const Settings = createSettings(Component, Component, Component, Component, Component);
|
||||||
|
|
||||||
it('renders a no-menu layout with the expected settings sections', () => {
|
it('renders a no-menu layout with the expected settings sections', () => {
|
||||||
const wrapper = shallow(<Settings />);
|
const wrapper = shallow(<Settings />);
|
||||||
|
@ -13,6 +13,6 @@ describe('<Settings />', () => {
|
||||||
|
|
||||||
expect(layout).toHaveLength(1);
|
expect(layout).toHaveLength(1);
|
||||||
expect(sections).toHaveLength(1);
|
expect(sections).toHaveLength(1);
|
||||||
expect((sections.prop('items') as any[]).flat()).toHaveLength(4);
|
expect((sections.prop('items') as any[]).flat()).toHaveLength(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
44
test/settings/Tags.test.tsx
Normal file
44
test/settings/Tags.test.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
|
import { Settings, TagsMode, TagsSettings } from '../../src/settings/reducers/settings';
|
||||||
|
import { TagsModeDropdown } from '../../src/tags/TagsModeDropdown';
|
||||||
|
import { Tags } from '../../src/settings/Tags';
|
||||||
|
|
||||||
|
describe('<Tags />', () => {
|
||||||
|
let wrapper: ShallowWrapper;
|
||||||
|
const setTagsSettings = jest.fn();
|
||||||
|
const createWrapper = (tags?: TagsSettings) => {
|
||||||
|
wrapper = shallow(<Tags settings={Mock.of<Settings>({ tags })} setTagsSettings={setTagsSettings} />);
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => wrapper?.unmount());
|
||||||
|
afterEach(jest.clearAllMocks);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ undefined, 'cards' ],
|
||||||
|
[{}, 'cards' ],
|
||||||
|
[{ defaultMode: 'cards' as TagsMode }, 'cards' ],
|
||||||
|
[{ defaultMode: 'list' as TagsMode }, 'list' ],
|
||||||
|
])('shows expected tags displaying mode', (tags, expectedMode) => {
|
||||||
|
const wrapper = createWrapper(tags);
|
||||||
|
const dropdown = wrapper.find(TagsModeDropdown);
|
||||||
|
const small = wrapper.find('small');
|
||||||
|
|
||||||
|
expect(dropdown.prop('mode')).toEqual(expectedMode);
|
||||||
|
expect(small.html()).toContain(`Tags will be displayed as <b>${expectedMode}</b>.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ 'cards' as TagsMode ],
|
||||||
|
[ 'list' as TagsMode ],
|
||||||
|
])('invokes setTagsSettings when tags mode changes', (defaultMode) => {
|
||||||
|
const wrapper = createWrapper();
|
||||||
|
const dropdown = wrapper.find(TagsModeDropdown);
|
||||||
|
|
||||||
|
expect(setTagsSettings).not.toHaveBeenCalled();
|
||||||
|
dropdown.simulate('change', defaultMode);
|
||||||
|
expect(setTagsSettings).toHaveBeenCalledWith({ defaultMode });
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,11 +2,10 @@ import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons';
|
import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { Settings, TagsMode, UiSettings } from '../../src/settings/reducers/settings';
|
import { Settings, UiSettings } from '../../src/settings/reducers/settings';
|
||||||
import { UserInterface } from '../../src/settings/UserInterface';
|
import { UserInterface } from '../../src/settings/UserInterface';
|
||||||
import ToggleSwitch from '../../src/utils/ToggleSwitch';
|
import ToggleSwitch from '../../src/utils/ToggleSwitch';
|
||||||
import { Theme } from '../../src/utils/theme';
|
import { Theme } from '../../src/utils/theme';
|
||||||
import { TagsModeDropdown } from '../../src/tags/TagsModeDropdown';
|
|
||||||
|
|
||||||
describe('<UserInterface />', () => {
|
describe('<UserInterface />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
|
@ -53,30 +52,4 @@ describe('<UserInterface />', () => {
|
||||||
toggle.simulate('change', checked);
|
toggle.simulate('change', checked);
|
||||||
expect(setUiSettings).toHaveBeenCalledWith({ theme });
|
expect(setUiSettings).toHaveBeenCalledWith({ theme });
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
|
||||||
[ undefined, 'cards' ],
|
|
||||||
[{ theme: 'light' as Theme }, 'cards' ],
|
|
||||||
[{ theme: 'light' as Theme, tagsMode: 'cards' as TagsMode }, 'cards' ],
|
|
||||||
[{ theme: 'light' as Theme, tagsMode: 'list' as TagsMode }, 'list' ],
|
|
||||||
])('shows expected tags displaying mode', (ui, expectedMode) => {
|
|
||||||
const wrapper = createWrapper(ui);
|
|
||||||
const dropdown = wrapper.find(TagsModeDropdown);
|
|
||||||
const small = wrapper.find('small');
|
|
||||||
|
|
||||||
expect(dropdown.prop('mode')).toEqual(expectedMode);
|
|
||||||
expect(small.html()).toContain(`Tags will be displayed as <b>${expectedMode}</b>.`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each([
|
|
||||||
[ 'cards' as TagsMode ],
|
|
||||||
[ 'list' as TagsMode ],
|
|
||||||
])('invokes setUiSettings when tags mode changes', (tagsMode) => {
|
|
||||||
const wrapper = createWrapper();
|
|
||||||
const dropdown = wrapper.find(TagsModeDropdown);
|
|
||||||
|
|
||||||
expect(setUiSettings).not.toHaveBeenCalled();
|
|
||||||
dropdown.simulate('change', tagsMode);
|
|
||||||
expect(setUiSettings).toHaveBeenCalledWith({ theme: 'light', tagsMode });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue