-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "[WNMGDS-2716] Remove privacy settings dialog (#3083)"
This reverts commit 84cf22d.
- Loading branch information
1 parent
e479a57
commit 6280081
Showing
39 changed files
with
3,466 additions
and
60 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
...ages/design-system/src/components/PrivacySettingsDialog/PrivacySettingsDialog.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import PrivacySettingsDialog from './PrivacySettingsDialog'; | ||
import { Title, Subtitle, Description, ArgsTable } from '@storybook/blocks'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
const meta: Meta<typeof PrivacySettingsDialog> = { | ||
title: 'Components/PrivacySettingsDialog', | ||
component: PrivacySettingsDialog, | ||
args: { | ||
domain: 'Test.gov', | ||
privacyPolicyUrl: 'https://youtu.be/dQw4w9WgXcQ', | ||
}, | ||
parameters: { | ||
docs: { | ||
// The dialog was overlapping the docs page, so customizing the docs page to remove the examples | ||
page: () => ( | ||
<> | ||
<Title /> | ||
<Subtitle /> | ||
<Description /> | ||
<ArgsTable /> | ||
</> | ||
), | ||
}, | ||
}, | ||
}; | ||
export default meta; | ||
|
||
type Story = StoryObj<typeof PrivacySettingsDialog>; | ||
|
||
export const Default: Story = {}; |
67 changes: 67 additions & 0 deletions
67
packages/design-system/src/components/PrivacySettingsDialog/PrivacySettingsDialog.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import '@testing-library/jest-dom'; | ||
import PrivacySettingsDialog from './PrivacySettingsDialog'; | ||
import { fireEvent, render, screen } from '@testing-library/react'; | ||
|
||
jest.mock('./privacySettings', () => ({ | ||
getPrivacySettings: jest.fn(() => ({ | ||
c2: '0', | ||
c3: '0', | ||
c4: '1', | ||
})), | ||
setPrivacySettings: jest.fn(), | ||
})); | ||
|
||
const defaultProps = { | ||
domain: 'Test.gov', | ||
privacyPolicyUrl: 'https://www.healthcare.gov/privacy/', | ||
thirdPartyPoliciesUrl: 'https://www.healthcare.gov/third-party-privacy-policies/', | ||
onExit: () => {}, | ||
isOpen: true, | ||
}; | ||
|
||
function renderComponent(props) { | ||
props = Object.assign({}, defaultProps, props); | ||
return render(<PrivacySettingsDialog {...props} />); | ||
} | ||
|
||
describe('<PrivacySettingsDialog />', function () { | ||
it('clicking close button calls onExit and does not save settings', () => { | ||
const { setPrivacySettings } = require('./privacySettings'); | ||
const onExit = jest.fn(); | ||
renderComponent({ onExit }); | ||
fireEvent.click(screen.getByRole('button', { name: 'Close modal dialog' })); | ||
expect(onExit).toHaveBeenCalled(); | ||
expect(setPrivacySettings).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('sets settings and closes dialog on save', () => { | ||
const { setPrivacySettings } = require('./privacySettings'); | ||
const onExit = jest.fn(); | ||
renderComponent({ onExit }); | ||
const radios = screen.getAllByRole('radio'); | ||
|
||
function getSettingRadios(name) { | ||
return radios.filter((radio) => radio.name === `cookie-${name}`); | ||
} | ||
|
||
function getSettingValue(name) { | ||
const radio = getSettingRadios(name).find((radio) => radio.checked); | ||
return radio.value; | ||
} | ||
|
||
expect(getSettingValue('c2')).toEqual('0'); | ||
expect(getSettingValue('c3')).toEqual('0'); | ||
expect(getSettingValue('c4')).toEqual('1'); | ||
|
||
const allow = getSettingRadios('c3').find((radio) => radio.value === '1'); | ||
fireEvent.click(allow); | ||
fireEvent.click(screen.getByRole('button', { name: /Update my settings/ })); | ||
|
||
expect(onExit).toHaveBeenCalled(); | ||
expect(setPrivacySettings).toHaveBeenCalledWith({ | ||
c2: '0', | ||
c3: '1', | ||
c4: '1', | ||
}); | ||
}); | ||
}); |
81 changes: 81 additions & 0 deletions
81
packages/design-system/src/components/PrivacySettingsDialog/PrivacySettingsDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import PrivacySettingsTable from './PrivacySettingsTable'; | ||
import { Button } from '../Button/index'; | ||
import { Dialog } from '../Dialog/index'; | ||
import { getPrivacySettings, setPrivacySettings } from './privacySettings'; | ||
import { t } from '../i18n'; | ||
import { useState } from 'react'; | ||
|
||
export const privacySettingConfigs = [ | ||
{ settingsKey: 'c3', translationKey: 'advertising' }, | ||
{ settingsKey: 'c4', translationKey: 'socialMedia' }, | ||
{ settingsKey: 'c2', translationKey: 'webAnalytics' }, | ||
]; | ||
|
||
interface PrivacySettingsDialogProps { | ||
domain: string; | ||
privacyPolicyUrl: string; | ||
thirdPartyPoliciesUrl?: string; | ||
onExit: () => void; | ||
/** | ||
* Controls whether the dialog is in an open state | ||
*/ | ||
isOpen?: boolean; | ||
} | ||
|
||
/** | ||
* The PrivacySettingsDialog allows users to adjust their privacy settings. | ||
* Typically these settings are accessed from a button in the footer of a CMS | ||
* website. Props allow for customizing on a per-domain basis. | ||
*/ | ||
export const PrivacySettingsDialog = (props: PrivacySettingsDialogProps) => { | ||
const [localPrivacySettings, setLocalPrivacySettings] = useState(getPrivacySettings()); | ||
|
||
function setPrivacySetting(settingsKey: string, value: string) { | ||
setLocalPrivacySettings({ | ||
...localPrivacySettings, | ||
[settingsKey]: value, | ||
}); | ||
} | ||
|
||
function savePrivacySettings() { | ||
setPrivacySettings(localPrivacySettings); | ||
props.onExit(); | ||
} | ||
|
||
const { domain, privacyPolicyUrl, thirdPartyPoliciesUrl, ...dialogProps } = props; | ||
const privacySettingsProperties = privacySettingConfigs.map((config) => ({ | ||
...config, | ||
value: localPrivacySettings[config.settingsKey], | ||
})); | ||
|
||
let intro = t('privacy.introText', { domain }); | ||
intro += ' ' + t('privacy.privacyPolicy', { url: privacyPolicyUrl }); | ||
if (thirdPartyPoliciesUrl) { | ||
intro += ' ' + t('privacy.thirdPartyPolicies', { url: thirdPartyPoliciesUrl }); | ||
} | ||
intro += '.'; | ||
|
||
return ( | ||
<Dialog | ||
{...dialogProps} | ||
heading={t('privacy.dialogTitle', { domain })} | ||
size="full" | ||
actions={ | ||
<Button variation="solid" onClick={savePrivacySettings}> | ||
{t('privacy.save')} | ||
</Button> | ||
} | ||
isOpen={props.isOpen} | ||
> | ||
<p className="ds-u-margin-top--0" dangerouslySetInnerHTML={{ __html: intro }} /> | ||
|
||
<PrivacySettingsTable | ||
domain={domain} | ||
privacySettings={privacySettingsProperties} | ||
setPrivacySetting={setPrivacySetting} | ||
/> | ||
</Dialog> | ||
); | ||
}; | ||
|
||
export default PrivacySettingsDialog; |
33 changes: 33 additions & 0 deletions
33
packages/design-system/src/components/PrivacySettingsDialog/PrivacySettingsTable.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import PrivacySettingsTable from './PrivacySettingsTable'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { privacySettingConfigs } from './PrivacySettingsDialog'; | ||
import { render, screen } from '@testing-library/react'; | ||
|
||
const defaultProps = { | ||
domain: 'Test.gov', | ||
privacySettings: [ | ||
{ ...privacySettingConfigs[0], defaultValue: '0', defaultChecked: false }, | ||
{ ...privacySettingConfigs[1], defaultValue: '1', defaultChecked: false }, | ||
{ ...privacySettingConfigs[2], defaultValue: '0', defaultChecked: false }, | ||
], | ||
setPrivacySetting: jest.fn(), | ||
}; | ||
|
||
function makePrivacySettingsTable(props) { | ||
props = Object.assign({}, defaultProps, props); | ||
return render(<PrivacySettingsTable {...props} />); | ||
} | ||
|
||
describe('<PrivacySettingsTable />', function () { | ||
it('renders the privacy settings table', () => { | ||
expect(makePrivacySettingsTable()).toMatchSnapshot(); | ||
}); | ||
|
||
it('calls the setPrivacySetting prop on Choice change', () => { | ||
const setPrivacySetting = jest.fn(); | ||
makePrivacySettingsTable({ setPrivacySetting }); | ||
const choice = screen.getAllByRole('radio'); | ||
userEvent.click(choice[1]); | ||
expect(setPrivacySetting).toHaveBeenCalledWith('c3', '1'); | ||
}); | ||
}); |
92 changes: 92 additions & 0 deletions
92
packages/design-system/src/components/PrivacySettingsDialog/PrivacySettingsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { ChoiceList } from '../ChoiceList/index'; | ||
import { Table, TableHead, TableRow, TableCell, TableBody } from '../Table/index'; | ||
import { t } from '../i18n'; | ||
|
||
export interface PrivacySettingsProperty { | ||
settingsKey: string; | ||
translationKey: string; | ||
value: string; | ||
} | ||
|
||
export interface PrivacySettingsTableProps { | ||
domain: string; | ||
privacySettings: PrivacySettingsProperty[]; | ||
setPrivacySetting: (key: string, value: string) => any; | ||
} | ||
|
||
export const PrivacySettingsTable = ({ | ||
domain, | ||
privacySettings, | ||
setPrivacySetting, | ||
}: PrivacySettingsTableProps) => { | ||
function renderToggle(settingsKey: string, value: string, category: string, description: string) { | ||
const choices = [ | ||
{ | ||
label: t('privacy.allow'), | ||
value: '0', | ||
checked: value === '0', | ||
}, | ||
{ | ||
label: t('privacy.dontAllow'), | ||
value: '1', | ||
checked: value === '1', | ||
}, | ||
]; | ||
const label = <span className="ds-u-visibility--screen-reader">{category}</span>; | ||
return ( | ||
<ChoiceList | ||
type="radio" | ||
choices={choices} | ||
onChange={(event) => { | ||
setPrivacySetting(settingsKey, event.target.value); | ||
}} | ||
className="ds-u-margin-top--0" | ||
label={label} | ||
aria-label={description} | ||
name={`cookie-${settingsKey}`} | ||
/> | ||
); | ||
} | ||
|
||
const categoryHeaderId = 'privacy-settings-header-category'; | ||
const descriptionHeaderId = 'privacy-settings-header-description'; | ||
const statusHeaderId = 'privacy-settings-header-status'; | ||
|
||
function renderRow({ settingsKey, translationKey, value }: PrivacySettingsProperty) { | ||
const category = t(`privacy.${translationKey}.category`); | ||
const description = t(`privacy.${translationKey}.description`, { domain }); | ||
return ( | ||
<TableRow key={settingsKey}> | ||
<TableCell headers={categoryHeaderId} stackedTitle={t('privacy.category')}> | ||
{category} | ||
</TableCell> | ||
<TableCell headers={descriptionHeaderId} stackedTitle={t('privacy.description')}> | ||
{description} | ||
</TableCell> | ||
<TableCell headers={statusHeaderId} stackedTitle={t('privacy.status')}> | ||
{renderToggle(settingsKey, value, category, description)} | ||
</TableCell> | ||
</TableRow> | ||
); | ||
} | ||
|
||
return ( | ||
<Table | ||
className="ds-c-privacy-settings-table ds-u-margin-top--2 ds-u-lg-margin--0" | ||
borderless | ||
stackable | ||
stackableBreakpoint="md" | ||
> | ||
<TableHead> | ||
<TableRow> | ||
<TableCell id={categoryHeaderId}>{t('privacy.category')}</TableCell> | ||
<TableCell id={descriptionHeaderId}>{t('privacy.description')}</TableCell> | ||
<TableCell id={statusHeaderId}>{t('privacy.status')}</TableCell> | ||
</TableRow> | ||
</TableHead> | ||
<TableBody>{privacySettings.map(renderRow)}</TableBody> | ||
</Table> | ||
); | ||
}; | ||
|
||
export default PrivacySettingsTable; |
Oops, something went wrong.