diff --git a/src/settings/VisitsSettings.tsx b/src/settings/VisitsSettings.tsx index 6d6a7bc4..a799bd2a 100644 --- a/src/settings/VisitsSettings.tsx +++ b/src/settings/VisitsSettings.tsx @@ -1,20 +1,39 @@ import { FC } from 'react'; +import { FormGroup } from 'reactstrap'; import { SimpleCard } from '../utils/SimpleCard'; import { DateIntervalSelector } from '../utils/dates/DateIntervalSelector'; import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup'; import { Settings, VisitsSettings as VisitsSettingsConfig } from './reducers/settings'; +import { ToggleSwitch } from '../utils/ToggleSwitch'; +import { FormText } from '../utils/forms/FormText'; +import { DateInterval } from '../utils/helpers/dateIntervals'; interface VisitsProps { settings: Settings; setVisitsSettings: (settings: VisitsSettingsConfig) => void; } +const currentDefaultInterval = (settings: Settings): DateInterval => settings.visits?.defaultInterval ?? 'last30Days'; + export const VisitsSettings: FC = ({ settings, setVisitsSettings }) => ( + + setVisitsSettings( + { defaultInterval: currentDefaultInterval(settings), excludeBots }, + )} + > + Exclude bots wherever possible (this option‘s effect might depend on Shlink server‘s version). + + The visits coming from potential bots will be {settings.visits?.excludeBots ? 'excluded' : 'included'}. + + + setVisitsSettings({ defaultInterval })} /> diff --git a/src/settings/reducers/settings.ts b/src/settings/reducers/settings.ts index e64f3338..c8df4c48 100644 --- a/src/settings/reducers/settings.ts +++ b/src/settings/reducers/settings.ts @@ -34,6 +34,7 @@ export interface UiSettings { export interface VisitsSettings { defaultInterval: DateInterval; + excludeBots?: boolean; } export interface TagsSettings { diff --git a/test/settings/VisitsSettings.test.tsx b/test/settings/VisitsSettings.test.tsx index 8d198272..051bdcf4 100644 --- a/test/settings/VisitsSettings.test.tsx +++ b/test/settings/VisitsSettings.test.tsx @@ -17,6 +17,7 @@ describe('', () => { expect(screen.getByRole('heading')).toHaveTextContent('Visits'); expect(screen.getByText('Default interval to load on visits sections:')).toBeInTheDocument(); + expect(screen.getByText(/^Exclude bots wherever possible/)).toBeInTheDocument(); }); it.each([ @@ -59,4 +60,36 @@ describe('', () => { expect(setVisitsSettings).toHaveBeenNthCalledWith(2, { defaultInterval: 'last180Days' }); expect(setVisitsSettings).toHaveBeenNthCalledWith(3, { defaultInterval: 'yesterday' }); }); + + it.each([ + [ + Mock.all(), + /The visits coming from potential bots will be included.$/, + /The visits coming from potential bots will be excluded.$/, + ], + [ + Mock.of({ visits: { excludeBots: false } }), + /The visits coming from potential bots will be included.$/, + /The visits coming from potential bots will be excluded.$/, + ], + [ + Mock.of({ visits: { excludeBots: true } }), + /The visits coming from potential bots will be excluded.$/, + /The visits coming from potential bots will be included.$/, + ], + ])('displays expected helper text for exclude bots control', (settings, expectedText, notExpectedText) => { + setUp(settings); + + const visitsComponent = screen.getByText(/^Exclude bots wherever possible/); + + expect(visitsComponent).toHaveTextContent(expectedText); + expect(visitsComponent).not.toHaveTextContent(notExpectedText); + }); + + it('invokes setVisitsSettings when bot exclusion is toggled', async () => { + const { user } = setUp(); + + await user.click(screen.getByText(/^Exclude bots wherever possible/)); + expect(setVisitsSettings).toHaveBeenCalledWith(expect.objectContaining({ excludeBots: true })); + }); });