mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 01:20:24 +03:00
Merge pull request #663 from acelaya-forks/feature/rtl-again
Feature/rtl again
This commit is contained in:
commit
2451167296
3 changed files with 110 additions and 129 deletions
|
@ -5,6 +5,7 @@ import { SimpleCard } from '../utils/SimpleCard';
|
|||
import { FormText } from '../utils/forms/FormText';
|
||||
import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup';
|
||||
import { Settings } from './reducers/settings';
|
||||
import { useDomId } from '../utils/helpers/hooks';
|
||||
|
||||
interface RealTimeUpdatesProps {
|
||||
settings: Settings;
|
||||
|
@ -16,39 +17,46 @@ const intervalValue = (interval?: number) => (!interval ? '' : `${interval}`);
|
|||
|
||||
export const RealTimeUpdatesSettings = (
|
||||
{ settings: { realTimeUpdates }, toggleRealTimeUpdates, setRealTimeUpdatesInterval }: RealTimeUpdatesProps,
|
||||
) => (
|
||||
<SimpleCard title="Real-time updates" className="h-100">
|
||||
<FormGroup>
|
||||
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}>
|
||||
Enable or disable real-time updates.
|
||||
<FormText>
|
||||
Real-time updates are currently being <b>{realTimeUpdates.enabled ? 'processed' : 'ignored'}</b>.
|
||||
</FormText>
|
||||
</ToggleSwitch>
|
||||
</FormGroup>
|
||||
<LabeledFormGroup
|
||||
noMargin
|
||||
label="Real-time updates frequency (in minutes):"
|
||||
labelClassName={classNames('form-label', { 'text-muted': !realTimeUpdates.enabled })}
|
||||
>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
placeholder="Immediate"
|
||||
disabled={!realTimeUpdates.enabled}
|
||||
value={intervalValue(realTimeUpdates.interval)}
|
||||
onChange={({ target }) => setRealTimeUpdatesInterval(Number(target.value))}
|
||||
/>
|
||||
{realTimeUpdates.enabled && (
|
||||
<FormText>
|
||||
{realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && (
|
||||
<span>
|
||||
Updates will be reflected in the UI every <b>{realTimeUpdates.interval}</b> minute{realTimeUpdates.interval > 1 && 's'}.
|
||||
</span>
|
||||
)}
|
||||
{!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'}
|
||||
</FormText>
|
||||
)}
|
||||
</LabeledFormGroup>
|
||||
</SimpleCard>
|
||||
);
|
||||
) => {
|
||||
const inputId = useDomId();
|
||||
|
||||
return (
|
||||
<SimpleCard title="Real-time updates" className="h-100">
|
||||
<FormGroup>
|
||||
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}>
|
||||
Enable or disable real-time updates.
|
||||
<FormText>
|
||||
Real-time updates are currently being <b>{realTimeUpdates.enabled ? 'processed' : 'ignored'}</b>.
|
||||
</FormText>
|
||||
</ToggleSwitch>
|
||||
</FormGroup>
|
||||
<LabeledFormGroup
|
||||
noMargin
|
||||
label="Real-time updates frequency (in minutes):"
|
||||
labelClassName={classNames('form-label', { 'text-muted': !realTimeUpdates.enabled })}
|
||||
id={inputId}
|
||||
>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
placeholder="Immediate"
|
||||
disabled={!realTimeUpdates.enabled}
|
||||
value={intervalValue(realTimeUpdates.interval)}
|
||||
id={inputId}
|
||||
onChange={({ target }) => setRealTimeUpdatesInterval(Number(target.value))}
|
||||
/>
|
||||
{realTimeUpdates.enabled && (
|
||||
<FormText>
|
||||
{realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && (
|
||||
<span>
|
||||
Updates will be reflected in the UI
|
||||
every <b>{realTimeUpdates.interval}</b> minute{realTimeUpdates.interval > 1 && 's'}.
|
||||
</span>
|
||||
)}
|
||||
{!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'}
|
||||
</FormText>
|
||||
)}
|
||||
</LabeledFormGroup>
|
||||
</SimpleCard>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,64 +1,54 @@
|
|||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { Mock } from 'ts-mockery';
|
||||
import { Input } from 'reactstrap';
|
||||
import { FormText } from '../../src/utils/forms/FormText';
|
||||
import {
|
||||
RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions,
|
||||
Settings,
|
||||
} from '../../src/settings/reducers/settings';
|
||||
import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings';
|
||||
import { ToggleSwitch } from '../../src/utils/ToggleSwitch';
|
||||
import { LabeledFormGroup } from '../../src/utils/forms/LabeledFormGroup';
|
||||
|
||||
describe('<RealTimeUpdatesSettings />', () => {
|
||||
const toggleRealTimeUpdates = jest.fn();
|
||||
const setRealTimeUpdatesInterval = jest.fn();
|
||||
let wrapper: ShallowWrapper;
|
||||
const createWrapper = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => {
|
||||
const settings = Mock.of<Settings>({ realTimeUpdates });
|
||||
|
||||
wrapper = shallow(
|
||||
const setUp = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => ({
|
||||
user: userEvent.setup(),
|
||||
...render(
|
||||
<RealTimeUpdatesSettings
|
||||
settings={settings}
|
||||
settings={Mock.of<Settings>({ realTimeUpdates })}
|
||||
toggleRealTimeUpdates={toggleRealTimeUpdates}
|
||||
setRealTimeUpdatesInterval={setRealTimeUpdatesInterval}
|
||||
/>,
|
||||
);
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
),
|
||||
});
|
||||
|
||||
afterEach(jest.clearAllMocks);
|
||||
afterEach(() => wrapper?.unmount());
|
||||
|
||||
it('renders enabled real time updates as expected', () => {
|
||||
const wrapper = createWrapper({ enabled: true });
|
||||
const toggle = wrapper.find(ToggleSwitch);
|
||||
const label = wrapper.find(LabeledFormGroup);
|
||||
const input = wrapper.find(Input);
|
||||
const formText = wrapper.find(FormText);
|
||||
setUp({ enabled: true });
|
||||
|
||||
expect(toggle.prop('checked')).toEqual(true);
|
||||
expect(toggle.html()).toContain('processed');
|
||||
expect(toggle.html()).not.toContain('ignored');
|
||||
expect(label.prop('labelClassName')).not.toContain('text-muted');
|
||||
expect(input.prop('disabled')).toEqual(false);
|
||||
expect(formText).toHaveLength(2);
|
||||
expect(screen.getByLabelText(/^Enable or disable real-time updates./)).toBeChecked();
|
||||
expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('processed');
|
||||
expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('ignored');
|
||||
expect(screen.getByText('Real-time updates frequency (in minutes):')).not.toHaveAttribute(
|
||||
'class',
|
||||
expect.stringContaining('text-muted'),
|
||||
);
|
||||
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).not.toHaveAttribute('disabled');
|
||||
expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders disabled real time updates as expected', () => {
|
||||
const wrapper = createWrapper({ enabled: false });
|
||||
const toggle = wrapper.find(ToggleSwitch);
|
||||
const label = wrapper.find(LabeledFormGroup);
|
||||
const input = wrapper.find(Input);
|
||||
const formText = wrapper.find(FormText);
|
||||
setUp({ enabled: false });
|
||||
|
||||
expect(toggle.prop('checked')).toEqual(false);
|
||||
expect(toggle.html()).not.toContain('processed');
|
||||
expect(toggle.html()).toContain('ignored');
|
||||
expect(label.prop('labelClassName')).toContain('text-muted');
|
||||
expect(input.prop('disabled')).toEqual(true);
|
||||
expect(formText).toHaveLength(1);
|
||||
expect(screen.getByLabelText(/^Enable or disable real-time updates./)).not.toBeChecked();
|
||||
expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('processed');
|
||||
expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('ignored');
|
||||
expect(screen.getByText('Real-time updates frequency (in minutes):')).toHaveAttribute(
|
||||
'class',
|
||||
expect.stringContaining('text-muted'),
|
||||
);
|
||||
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveAttribute('disabled');
|
||||
expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
@ -67,43 +57,35 @@ describe('<RealTimeUpdatesSettings />', () => {
|
|||
[10, 'minutes'],
|
||||
[100, 'minutes'],
|
||||
])('shows expected children when interval is greater than 0', (interval, minutesWord) => {
|
||||
const wrapper = createWrapper({ enabled: true, interval });
|
||||
const span = wrapper.find('span');
|
||||
const input = wrapper.find(Input);
|
||||
setUp({ enabled: true, interval });
|
||||
|
||||
expect(span).toHaveLength(1);
|
||||
expect(span.html()).toEqual(
|
||||
`<span>Updates will be reflected in the UI every <b>${interval}</b> ${minutesWord}.</span>`,
|
||||
expect(screen.getByText(/^Updates will be reflected in the UI every/)).toHaveTextContent(
|
||||
`${interval} ${minutesWord}`,
|
||||
);
|
||||
expect(input.prop('value')).toEqual(`${interval}`);
|
||||
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveValue(interval);
|
||||
expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([[undefined], [0]])('shows expected children when interval is 0 or undefined', (interval) => {
|
||||
const wrapper = createWrapper({ enabled: true, interval });
|
||||
const span = wrapper.find('span');
|
||||
const formText = wrapper.find(FormText).at(1);
|
||||
const input = wrapper.find(Input);
|
||||
setUp({ enabled: true, interval });
|
||||
|
||||
expect(span).toHaveLength(0);
|
||||
expect(formText.html()).toContain('Updates will be reflected in the UI as soon as they happen.');
|
||||
expect(input.prop('value')).toEqual('');
|
||||
expect(screen.queryByText(/^Updates will be reflected in the UI every/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('updates real time updates on input change', () => {
|
||||
const wrapper = createWrapper();
|
||||
const input = wrapper.find(Input);
|
||||
it('updates real time updates when typing on input', async () => {
|
||||
const { user } = setUp({ enabled: true });
|
||||
|
||||
expect(setRealTimeUpdatesInterval).not.toHaveBeenCalled();
|
||||
input.simulate('change', { target: { value: '10' } });
|
||||
expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(10);
|
||||
await user.type(screen.getByLabelText('Real-time updates frequency (in minutes):'), '5');
|
||||
expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(5);
|
||||
});
|
||||
|
||||
it('toggles real time updates on switch change', () => {
|
||||
const wrapper = createWrapper();
|
||||
const toggle = wrapper.find(ToggleSwitch);
|
||||
it('toggles real time updates on switch change', async () => {
|
||||
const { user } = setUp({ enabled: true });
|
||||
|
||||
expect(toggleRealTimeUpdates).not.toHaveBeenCalled();
|
||||
toggle.simulate('change');
|
||||
await user.click(screen.getByText(/^Enable or disable real-time updates./));
|
||||
expect(toggleRealTimeUpdates).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,52 +1,43 @@
|
|||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { Mock } from 'ts-mockery';
|
||||
import {
|
||||
DEFAULT_SHORT_URLS_ORDERING,
|
||||
Settings,
|
||||
ShortUrlsListSettings as ShortUrlsSettings,
|
||||
} from '../../src/settings/reducers/settings';
|
||||
import { Settings, ShortUrlsListSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
|
||||
import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings';
|
||||
import { OrderingDropdown } from '../../src/utils/OrderingDropdown';
|
||||
import { ShortUrlsOrder } from '../../src/short-urls/data';
|
||||
|
||||
describe('<ShortUrlsListSettings />', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
const setSettings = jest.fn();
|
||||
const createWrapper = (shortUrlsList?: ShortUrlsSettings) => {
|
||||
wrapper = shallow(
|
||||
const setUp = (shortUrlsList?: ShortUrlsSettings) => ({
|
||||
user: userEvent.setup(),
|
||||
...render(
|
||||
<ShortUrlsListSettings settings={Mock.of<Settings>({ shortUrlsList })} setShortUrlsListSettings={setSettings} />,
|
||||
);
|
||||
),
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
afterEach(() => wrapper?.unmount());
|
||||
afterEach(jest.clearAllMocks);
|
||||
|
||||
it.each([
|
||||
[undefined, DEFAULT_SHORT_URLS_ORDERING],
|
||||
[{}, DEFAULT_SHORT_URLS_ORDERING],
|
||||
[{ defaultOrdering: {} }, {}],
|
||||
[{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, { field: 'longUrl', dir: 'DESC' }],
|
||||
[{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, { field: 'visits', dir: 'ASC' }],
|
||||
[undefined, 'Order by: Created at - DESC'],
|
||||
[{}, 'Order by: Created at - DESC'],
|
||||
[{ defaultOrdering: {} }, 'Order by...'],
|
||||
[{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, 'Order by: Long URL - DESC'],
|
||||
[{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, 'Order by: Visits - ASC'],
|
||||
])('shows expected ordering', (shortUrlsList, expectedOrder) => {
|
||||
const wrapper = createWrapper(shortUrlsList);
|
||||
const dropdown = wrapper.find(OrderingDropdown);
|
||||
|
||||
expect(dropdown.prop('order')).toEqual(expectedOrder);
|
||||
setUp(shortUrlsList);
|
||||
expect(screen.getByRole('button')).toHaveTextContent(expectedOrder);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[undefined, undefined],
|
||||
['longUrl', 'ASC'],
|
||||
['visits', undefined],
|
||||
['title', 'DESC'],
|
||||
])('invokes setSettings when ordering changes', (field, dir) => {
|
||||
const wrapper = createWrapper();
|
||||
const dropdown = wrapper.find(OrderingDropdown);
|
||||
['Clear selection', undefined, undefined],
|
||||
['Long URL', 'longUrl', 'ASC'],
|
||||
['Visits', 'visits', 'ASC'],
|
||||
['Title', 'title', 'ASC'],
|
||||
])('invokes setSettings when ordering changes', async (name, field, dir) => {
|
||||
const { user } = setUp();
|
||||
|
||||
expect(setSettings).not.toHaveBeenCalled();
|
||||
dropdown.simulate('change', field, dir);
|
||||
await user.click(screen.getByRole('button'));
|
||||
await user.click(screen.getByRole('menuitem', { name }));
|
||||
expect(setSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } });
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue