Skip to content

Commit

Permalink
[Index management] Fix a11y focus order in index mappings page (elast…
Browse files Browse the repository at this point in the history
…ic#203361)

## Summary

Fix a11y focus order in index mappings page. When new field is in
pending state and after closing edit pending field Flyout.

https://github.com/user-attachments/assets/dbdf59e5-0ebd-47e0-9b5e-19ab1556e771

### Test instructions
#### Adding a field
1. Add new field in index mappings page by navigating via tab
2. Notice that type fields combo box is focused
3. Add field and click to Add field button again with in pending fields
form
4. Notice focus is on new create field form

#### Edit field in pending state
1. Add new fields via tab key
2. click on edit field
3. Try closing, updating and cancelling in the edit field flyout form
4. Notice after edit field flyout is closed, focus is on the pending
fields form

(cherry picked from commit 4b0c0e9)
  • Loading branch information
saarikabhasi committed Dec 10, 2024
1 parent 371ae4d commit 736489c
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface Props {
onCancelAddingNewFields?: () => void;
isAddingFields?: boolean;
semanticTextInfo?: SemanticTextInfo;
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
}
export const DocumentFields = React.memo(
({
Expand All @@ -35,6 +36,7 @@ export const DocumentFields = React.memo(
onCancelAddingNewFields,
isAddingFields,
semanticTextInfo,
pendingFieldsRef,
}: Props) => {
const { fields, documentFields } = useMappingsState();
const dispatch = useDispatch();
Expand All @@ -58,6 +60,7 @@ export const DocumentFields = React.memo(
onCancelAddingNewFields={onCancelAddingNewFields}
isAddingFields={isAddingFields}
semanticTextInfo={semanticTextInfo}
pendingFieldsRef={pendingFieldsRef}
/>
);

Expand All @@ -81,8 +84,9 @@ export const DocumentFields = React.memo(
useEffect(() => {
if (!isEditing) {
removeContentFromGlobalFlyout('mappingsEditField');
if (pendingFieldsRef?.current) pendingFieldsRef.current.focus();
}
}, [isEditing, removeContentFromGlobalFlyout]);
}, [isEditing, removeContentFromGlobalFlyout, pendingFieldsRef]);

useEffect(() => {
return () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ interface Props {
isMultiField?: boolean | null;
showDocLink?: boolean;
isSemanticTextEnabled?: boolean;
fieldTypeInputRef?: React.MutableRefObject<HTMLInputElement | null>;
}

export const TypeParameter = ({
isMultiField,
isRootLevelField,
showDocLink = false,
isSemanticTextEnabled = true,
fieldTypeInputRef,
}: Props) => {
const fieldTypeOptions = useMemo(() => {
let options = isMultiField
Expand Down Expand Up @@ -97,6 +99,9 @@ export const TypeParameter = ({
onChange={typeField.setValue}
isClearable={false}
data-test-subj="fieldType"
inputRef={(input) => {
if (fieldTypeInputRef) fieldTypeInputRef.current = input;
}}
/>
</EuiFormRow>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { i18n } from '@kbn/i18n';
import { TrainedModelStat } from '@kbn/ml-plugin/common/types/trained_models';
import { MlPluginStart } from '@kbn/ml-plugin/public';
import classNames from 'classnames';
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import { EUI_SIZE, TYPE_DEFINITION } from '../../../../constants';
import { fieldSerializer } from '../../../../lib';
import { isSemanticTextField } from '../../../../lib/utils';
Expand Down Expand Up @@ -62,6 +62,7 @@ interface Props {
onCancelAddingNewFields?: () => void;
isAddingFields?: boolean;
semanticTextInfo?: SemanticTextInfo;
createFieldFormRef?: React.RefObject<HTMLDivElement>;
}

export const CreateField = React.memo(function CreateFieldComponent({
Expand All @@ -74,9 +75,11 @@ export const CreateField = React.memo(function CreateFieldComponent({
onCancelAddingNewFields,
isAddingFields,
semanticTextInfo,
createFieldFormRef,
}: Props) {
const { isSemanticTextEnabled, ml, setErrorsInTrainedModelDeployment } = semanticTextInfo ?? {};
const dispatch = useDispatch();
const fieldTypeInputRef = useRef<HTMLInputElement>(null);

const { form } = useForm<Field>({
serializer: fieldSerializer,
Expand Down Expand Up @@ -111,6 +114,10 @@ export const CreateField = React.memo(function CreateFieldComponent({

const isSemanticText = form.getFormData().type === 'semantic_text';

useEffect(() => {
if (createFieldFormRef?.current) createFieldFormRef?.current.focus();
}, [createFieldFormRef]);

const submitForm = async (
e?: React.FormEvent,
exitAfter: boolean = false,
Expand All @@ -134,6 +141,10 @@ export const CreateField = React.memo(function CreateFieldComponent({
}
form.reset();
}

if (fieldTypeInputRef.current) {
fieldTypeInputRef.current.focus();
}
};

const onClickOutside = () => {
Expand All @@ -157,6 +168,7 @@ export const CreateField = React.memo(function CreateFieldComponent({
isMultiField={isMultiField}
showDocLink
isSemanticTextEnabled={isSemanticTextEnabled}
fieldTypeInputRef={fieldTypeInputRef}
/>
</EuiFlexItem>

Expand Down Expand Up @@ -266,6 +278,8 @@ export const CreateField = React.memo(function CreateFieldComponent({
: paddingLeft
}px`,
}}
ref={createFieldFormRef}
tabIndex={0}
>
<div className="mappingsEditor__createFieldContent">
{renderFormFields()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Props {
state: State;
setPreviousState?: (state: State) => void;
isAddingFields?: boolean;
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
}

export const FieldsList = React.memo(function FieldsListComponent({
Expand All @@ -24,6 +25,7 @@ export const FieldsList = React.memo(function FieldsListComponent({
state,
setPreviousState,
isAddingFields,
pendingFieldsRef,
}: Props) {
if (fields === undefined) {
return null;
Expand All @@ -39,6 +41,7 @@ export const FieldsList = React.memo(function FieldsListComponent({
state={state}
setPreviousState={setPreviousState}
isAddingFields={isAddingFields}
pendingFieldsRef={pendingFieldsRef}
/>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ interface Props {
treeDepth: number;
state: State;
isAddingFields?: boolean;
createFieldFormRef?: React.RefObject<HTMLDivElement>;
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
}

function FieldListItemComponent(
Expand All @@ -85,6 +87,7 @@ function FieldListItemComponent(
state,
isAddingFields,
setPreviousState,
pendingFieldsRef,
}: Props,
ref: React.Ref<HTMLLIElement>
) {
Expand Down Expand Up @@ -141,7 +144,6 @@ function FieldListItemComponent(

const { addMultiFieldButtonLabel, addPropertyButtonLabel, editButtonLabel, deleteButtonLabel } =
i18nTexts;

return (
<EuiFlexGroup gutterSize="s" className="mappingsEditor__fieldsListItem__actions">
{canHaveMultiFields && (
Expand Down Expand Up @@ -321,6 +323,7 @@ function FieldListItemComponent(
state={state}
isAddingFields={isAddingFields}
setPreviousState={setPreviousState}
pendingFieldsRef={pendingFieldsRef}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface Props {
state: State;
setPreviousState?: (state: State) => void;
isAddingFields?: boolean;
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
}

export const FieldsListItemContainer = ({
Expand All @@ -27,6 +28,7 @@ export const FieldsListItemContainer = ({
state,
setPreviousState,
isAddingFields,
pendingFieldsRef,
}: Props) => {
const dispatch = useDispatch();
const listElement = useRef<HTMLLIElement | null>(null);
Expand Down Expand Up @@ -110,6 +112,7 @@ export const FieldsListItemContainer = ({
toggleExpand={toggleExpand}
state={state}
isAddingFields={isAddingFields}
pendingFieldsRef={pendingFieldsRef}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useRef } from 'react';

import { useDispatch, useMappingsState } from '../../mappings_state_context';
import { CreateField, FieldsList, SemanticTextInfo } from './fields';
Expand All @@ -16,19 +16,21 @@ interface Props {
onCancelAddingNewFields?: () => void;
isAddingFields?: boolean;
semanticTextInfo?: SemanticTextInfo;
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
}

export const DocumentFieldsTreeEditor = ({
onCancelAddingNewFields,
isAddingFields,
semanticTextInfo,
pendingFieldsRef,
}: Props) => {
const dispatch = useDispatch();
const {
fields: { byId, rootLevelFields },
documentFields: { status, fieldToAddFieldTo },
} = useMappingsState();

const createFieldFormRef = useRef<HTMLDivElement>(null);
const getField = useCallback((fieldId: string) => byId[fieldId], [byId]);
const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields, getField]);

Expand All @@ -52,6 +54,7 @@ export const DocumentFieldsTreeEditor = ({
onCancelAddingNewFields={onCancelAddingNewFields}
isAddingFields={isAddingFields}
semanticTextInfo={semanticTextInfo}
createFieldFormRef={createFieldFormRef}
/>
);
};
Expand All @@ -77,7 +80,12 @@ export const DocumentFieldsTreeEditor = ({

return (
<>
<FieldsList fields={fields} state={useMappingsState()} isAddingFields={isAddingFields} />
<FieldsList
fields={fields}
state={useMappingsState()}
isAddingFields={isAddingFields}
pendingFieldsRef={pendingFieldsRef}
/>
{renderCreateField()}
{renderAddFieldButton()}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React, { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react';
import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ILicense } from '@kbn/licensing-plugin/public';
import { useUnsavedChangesPrompt } from '@kbn/unsaved-changes-prompt';
import {
Expand Down Expand Up @@ -81,6 +81,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
overlays,
history,
} = useAppContext();
const pendingFieldsRef = useRef<HTMLDivElement>(null);

const [isPlatinumLicense, setIsPlatinumLicense] = useState<boolean>(false);
useEffect(() => {
Expand Down Expand Up @@ -540,7 +541,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
</EuiFlexItem>
{errorSavingMappings}
{isAddingFields && (
<EuiFlexItem grow={false}>
<EuiFlexItem grow={false} ref={pendingFieldsRef} tabIndex={0}>
<EuiPanel hasBorder paddingSize="s">
<EuiAccordion
id={pendingFieldListId}
Expand Down Expand Up @@ -578,11 +579,13 @@ export const DetailsPageMappingsContent: FunctionComponent<{
onCancelAddingNewFields={onCancelAddingNewFields}
isAddingFields={isAddingFields}
semanticTextInfo={semanticTextInfo}
pendingFieldsRef={pendingFieldsRef}
/>
) : (
<DocumentFields
isAddingFields={isAddingFields}
semanticTextInfo={semanticTextInfo}
pendingFieldsRef={pendingFieldsRef}
/>
)}
</EuiPanel>
Expand Down

0 comments on commit 736489c

Please sign in to comment.