Skip to content

Commit

Permalink
[B] Implement Collapse in user notifications
Browse files Browse the repository at this point in the history
In this instance, the state of the collapsed content is controlled by
a group of radio inputs. So rather than use Collapse.Toggle, I've
retrieved `toggleProps` from the nearest collapse context and applied
"aria-controls" and "aria-expanded" to the inputs. While this isn't
a recognized approach by W3C currently, there is an open issue in the
w3c/aria repo reviewing this design pattern for ARIA 1.3
(w3c/aria#1404). Moreover, GOV.UK (which
opened this issue), a recognized leader in a11y, has found this pattern
to be helpful to users and is already using it. As such, it makes sense
to optimistically implement this with the hope that it will soon
become a recognized pattern. Until that happens, not all browsers
provide the desired feedback to screen reader users. But this seems
limited to Chrome fortunately. Providing an improved experience for
many SR users is preferable to an experience that confuses all SR users.
  • Loading branch information
dananjohnson committed Sep 21, 2021
1 parent 923a87b commit 0aad042
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 245 deletions.
202 changes: 0 additions & 202 deletions client/src/global/components/preferences/NotificationsForm.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from "react";
import PropTypes from "prop-types";
import config from "config";
import Collapse from "global/components/Collapse";
import RadioGroup from "./RadioGroup";
import { useCollapseContext } from "hooks";

function ProjectPreferences({ preferences, onChange, onDigestChange }) {
const { toggleProps } = useCollapseContext();
const items = config.app.locale.notificationPreferences.digest;
const options = {
...(preferences.projects && { projects: "All Projects" }),
followedProjects: "Only Projects I'm Following"
};

return (
<>
<RadioGroup
preference={{
key: "digest",
label: "How often would you like to be notified?"
}}
value={preferences.digest}
options={{
never: "Never",
daily: "Daily",
weekly: "Weekly"
}}
onChange={onChange}
inputProps={{
...toggleProps,
onClick: null,
type: "radio",
"aria-expanded":
preferences.digest === "daily" || preferences.digest === "weekly"
}}
/>
<Collapse.Content>
<div className="subscriptions__collapsed-group">
<RadioGroup
preference={{
key: "digest-projects",
label: "Which projects should be included?"
}}
value={Object.keys(preferences).find(
prefKey => prefKey in options && preferences[prefKey] === "always"
)}
options={options}
onChange={onDigestChange}
/>
{items.map(item => {
return (
<RadioGroup
key={item.key}
preference={item}
value={preferences[item.key]}
onChange={onChange}
/>
);
})}
</div>
</Collapse.Content>
</>
);
}

ProjectPreferences.propTypes = {
preferences: PropTypes.object,
onChange: PropTypes.func,
onDigestChange: PropTypes.func
};

export default ProjectPreferences;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from "react";
import PropTypes from "prop-types";
import { useUIDSeed } from "react-uid";
import classNames from "classnames";

function RadioGroup({
preference,
value,
options = { never: "No", always: "Yes" },
onChange,
inputProps = {}
}) {
const uidSeed = useUIDSeed();

if (!preference) return null;

return (
<fieldset className="subscriptions__radio-group form-input">
<legend className="subscriptions__legend">{preference.label}</legend>
{preference.instructions && (
<span className="instructions">{preference.instructions}</span>
)}
{Object.keys(options).map(option => {
const checked = value === option;
const inputClassNames = classNames("form-toggle", "radio", "inline", {
checked
});

return (
<label
id={uidSeed(option)}
className={inputClassNames}
key={`${preference.key}-${option}`}
>
<input
type="radio"
name={preference.key}
value={option}
checked={checked}
onChange={onChange}
aria-labelledby={uidSeed(option)}
{...inputProps}
/>
<span className="toggle-indicator" />
<span className="toggle-label">{options[option]}</span>
</label>
);
})}
</fieldset>
);
}

RadioGroup.displayName = "NotificationsForm.RadioGroup";

RadioGroup.propTypes = {
preferences: PropTypes.object,
options: PropTypes.object,
value: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func,
inputProps: PropTypes.object
};

export default RadioGroup;
Loading

0 comments on commit 0aad042

Please sign in to comment.