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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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)}