-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pages Editor: design updates for internal test #7129
Changes from 13 commits
2cd5204
5d0a5ad
b42e07a
2719866
4a7bcf8
ee6243b
4e9762a
9fd1672
4f000d2
1d6b945
f72bb8f
8d1be63
1f9d10d
46db5b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import { useEffect, useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import CollapseIcon from '../../../../../icons/CollapseIcon.jsx'; | ||
import DeleteIcon from '../../../../../icons/DeleteIcon.jsx'; | ||
import DrawingToolIcon from '../../../../../icons/DrawingToolIcon.jsx'; | ||
import ExpandIcon from '../../../../../icons/ExpandIcon.jsx'; | ||
import MinusIcon from '../../../../../icons/MinusIcon.jsx'; | ||
import PlusIcon from '../../../../../icons/PlusIcon.jsx'; | ||
|
||
|
@@ -51,6 +53,7 @@ const TOOL_TYPE_OPTIONS = [ | |
|
||
function DrawingTask({ | ||
deleteTask = DEFAULT_HANDLER, | ||
isFirstTaskInStep = true, | ||
stepHasManyTasks = false, | ||
task, | ||
taskKey, | ||
|
@@ -140,6 +143,11 @@ function DrawingTask({ | |
return false; | ||
} | ||
|
||
const [ showHelpField, setShowHelpField ] = useState(isFirstTaskInStep || task?.help?.length > 0); | ||
function toggleShowHelpField() { | ||
setShowHelpField(!showHelpField); | ||
} | ||
|
||
// For inputs that don't have onBlur, update triggers automagically. | ||
// (You can't call update() in the onChange() right after setStateValue().) | ||
// TODO: useEffect() means update() is called on the first render, which is unnecessary. Clean this up. | ||
|
@@ -319,14 +327,30 @@ function DrawingTask({ | |
</span> | ||
</div> | ||
<div className="input-row"> | ||
<label | ||
className="big spacing-bottom-S" | ||
htmlFor={`task-${taskKey}-help`} | ||
> | ||
Help Text | ||
</label> | ||
<div className="flex-row spacing-bottom-S"> | ||
<label | ||
className="medium" | ||
htmlFor={`task-${taskKey}-help`} | ||
> | ||
Help Text | ||
</label> | ||
<button | ||
aria-label={`Show/Hide Help field`} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can modify this text to be more accurate to the existing state:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would change the labels to 'Show help field (closed)' and 'Hide help field (expanded)', since the ARIA expanded state is read out as part of the label. I don't think it's recommended practice to change the labels for toggles, based on the toggle state. Rather, have a single toggle button label that makes sense when accompanied by the open/closed state. The visible label could be changed, though, for sighted users. |
||
aria-controls={`task-${taskKey}-help`} | ||
aria-expanded={showHelpField ? 'true' : 'false'} | ||
className="plain" | ||
onClick={toggleShowHelpField} | ||
type="button" | ||
> | ||
{showHelpField | ||
? <CollapseIcon /> | ||
: <ExpandIcon /> | ||
} | ||
</button> | ||
</div> | ||
<textarea | ||
id={`task-${taskKey}-help`} | ||
hidden={!showHelpField} | ||
value={help} | ||
onBlur={update} | ||
onChange={(e) => { setHelp(e?.target?.value) }} | ||
|
@@ -338,6 +362,7 @@ function DrawingTask({ | |
|
||
DrawingTask.propTypes = { | ||
deleteTask: PropTypes.func, | ||
isFirstTaskInStep: PropTypes.bool, | ||
stepHasManyTasks: PropTypes.bool, | ||
task: PropTypes.object, | ||
taskKey: PropTypes.string, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,15 @@ | ||
import { useEffect, useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import CollapseIcon from '../../../../../icons/CollapseIcon.jsx'; | ||
import DeleteIcon from '../../../../../icons/DeleteIcon.jsx'; | ||
import ExpandIcon from '../../../../../icons/ExpandIcon.jsx'; | ||
|
||
const DEFAULT_HANDLER = () => {}; | ||
|
||
function TextTask({ | ||
deleteTask = DEFAULT_HANDLER, | ||
isFirstTaskInStep = true, | ||
stepHasManyTasks = false, | ||
task, | ||
taskKey, | ||
|
@@ -31,6 +34,11 @@ function TextTask({ | |
deleteTask(taskKey); | ||
} | ||
|
||
const [ showHelpField, setShowHelpField ] = useState(isFirstTaskInStep || task?.help?.length > 0); | ||
function toggleShowHelpField() { | ||
setShowHelpField(!showHelpField); | ||
} | ||
|
||
// For inputs that don't have onBlur, update triggers automagically. | ||
// (You can't call update() in the onChange() right after setStateValue().) | ||
// TODO: useEffect() means update() is called on the first render, which is unnecessary. Clean this up. | ||
|
@@ -81,14 +89,30 @@ function TextTask({ | |
</span> | ||
</div> | ||
<div className="input-row"> | ||
<label | ||
className="big spacing-bottom-S" | ||
htmlFor={`task-${taskKey}-help`} | ||
> | ||
Help Text | ||
</label> | ||
<div className="flex-row spacing-bottom-S"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd strongly consider refactoring the shared code between DrawingTask / TextTask / QuestionTask into a file called |
||
<label | ||
className="medium" | ||
htmlFor={`task-${taskKey}-help`} | ||
> | ||
Help Text | ||
</label> | ||
<button | ||
aria-label={`Show/Hide Help field`} | ||
aria-controls={`task-${taskKey}-help`} | ||
aria-expanded={showHelpField ? 'true' : 'false'} | ||
className="plain" | ||
onClick={toggleShowHelpField} | ||
type="button" | ||
> | ||
{showHelpField | ||
? <CollapseIcon /> | ||
: <ExpandIcon /> | ||
} | ||
</button> | ||
</div> | ||
<textarea | ||
id={`task-${taskKey}-help`} | ||
hidden={!showHelpField} | ||
value={help} | ||
onBlur={update} | ||
onChange={(e) => { setHelp(e?.target?.value) }} | ||
|
@@ -100,6 +124,7 @@ function TextTask({ | |
|
||
TextTask.propTypes = { | ||
deleteTask: PropTypes.func, | ||
isFirstTaskInStep: PropTypes.bool, | ||
stepHasManyTasks: PropTypes.bool, | ||
task: PropTypes.object, | ||
taskKey: PropTypes.string, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default function CollapseIcon({ alt }) { | ||
return ( | ||
<span className="icon fa fa-angle-up" aria-label={alt} role={!!alt ? 'img' : undefined} /> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default function ExpandIcon({ alt }) { | ||
return ( | ||
<span className="icon fa fa-angle-down" aria-label={alt} role={!!alt ? 'img' : undefined} /> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requires the input to be in the DOM at all times. You can’t have a label without its corresponding input in the DOM, so the show/hide pattern used in this PR is broken. It works visually but produces a broken DOM when the text input is removed. That won’t work in a screen reader.
This needs to use a details/summary pattern, or the correct ARIA roles and state for a collapsible panel, in order to work for any users that can't physically see the task editor.
I’d also suggest using the HTML
hidden
boolean attribute to hide the input, rather than removing it from the DOM.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the WAI-ARIA pattern for building a disclosure widget, including roles, states and keyboard interactions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, of course, you could use the native HTML disclosure widget.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scott O'Hara summarised the pros and cons of using a native disclosure widget vs. rolling your own in JavaScript. You can use that to make a decision as to which fits best here.