Focus trap in poll creation dialog (#7847)

* add autofocus

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* scope ids

Signed-off-by: Kerry Archibald <kerrya@element.io>

* whitespace

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry
2022-02-18 17:35:08 +01:00
committed by GitHub
parent 5f5bb4a4fe
commit fc9a221371
3 changed files with 41 additions and 4 deletions

View File

@@ -35,10 +35,15 @@ interface IProps extends IDialogProps {
editingMxEvent?: MatrixEvent; // Truthy if we are editing an existing poll
}
enum FocusTarget {
Topic,
NewOption,
}
interface IState extends IScrollableBaseState {
question: string;
options: string[];
busy: boolean;
autoFocusTarget: FocusTarget;
}
const MIN_OPTIONS = 2;
@@ -55,6 +60,7 @@ function creatingInitialState(): IState {
question: "",
options: arraySeed("", DEFAULT_NUM_OPTIONS),
busy: false,
autoFocusTarget: FocusTarget.Topic,
};
}
@@ -69,6 +75,7 @@ function editingInitialState(editingMxEvent: MatrixEvent): IState {
question: poll.question.text,
options: poll.answers.map(ans => ans.text),
busy: false,
autoFocusTarget: FocusTarget.Topic,
};
}
@@ -113,7 +120,7 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
private onOptionAdd = () => {
const newOptions = arrayFastClone(this.state.options);
newOptions.push("");
this.setState({ options: newOptions }, () => {
this.setState({ options: newOptions, autoFocusTarget: FocusTarget.NewOption }, () => {
// Scroll the button into view after the state update to ensure we don't experience
// a pop-in effect, and to avoid the button getting cut off due to a mid-scroll render.
this.addOptionRef.current?.scrollIntoView?.();
@@ -185,6 +192,7 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
return <div className="mx_PollCreateDialog">
<h2>{ _t("What is your poll question or topic?") }</h2>
<Field
id='poll-topic-input'
value={this.state.question}
maxLength={MAX_QUESTION_LENGTH}
label={_t("Question or topic")}
@@ -192,11 +200,13 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
onChange={this.onQuestionChange}
usePlaceholderAsHint={true}
disabled={this.state.busy}
autoFocus={this.state.autoFocusTarget === FocusTarget.Topic}
/>
<h2>{ _t("Create options") }</h2>
{
this.state.options.map((op, i) => <div key={`option_${i}`} className="mx_PollCreateDialog_option">
<Field
id={`pollcreate_option_${i}`}
value={op}
maxLength={MAX_OPTION_LENGTH}
label={_t("Option %(number)s", { number: i + 1 })}
@@ -207,6 +217,10 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
}
usePlaceholderAsHint={true}
disabled={this.state.busy}
autoFocus={
this.state.autoFocusTarget === FocusTarget.NewOption &&
i === this.state.options.length - 1
}
/>
<AccessibleButton
onClick={() => this.onOptionRemove(i)}