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:
parent
5f5bb4a4fe
commit
fc9a221371
3 changed files with 41 additions and 4 deletions
|
@ -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)}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue