Skip to content

Commit

Permalink
Revert "[WNMGDS-2716] Remove privacy settings dialog (#3083)"
Browse files Browse the repository at this point in the history
This reverts commit 84cf22d.
  • Loading branch information
jack-ryan-nava-pbc committed Sep 3, 2024
1 parent e479a57 commit 6280081
Show file tree
Hide file tree
Showing 39 changed files with 3,466 additions and 60 deletions.
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 = {};
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',
});
});
});
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;
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');
});
});
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;
Loading

0 comments on commit 6280081

Please sign in to comment.