-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[B] Implement Collapse in user notifications
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
1 parent
923a87b
commit 0aad042
Showing
8 changed files
with
280 additions
and
245 deletions.
There are no files selected for viewing
202 changes: 0 additions & 202 deletions
202
client/src/global/components/preferences/NotificationsForm.js
This file was deleted.
Oops, something went wrong.
73 changes: 73 additions & 0 deletions
73
client/src/global/components/preferences/NotificationsForm/ProjectPreferences.js
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,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; |
64 changes: 64 additions & 0 deletions
64
client/src/global/components/preferences/NotificationsForm/RadioGroup.js
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,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; |
Oops, something went wrong.