From 6c02ef82a9135f612aece257f778e2e6a903192b Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 11 Dec 2024 04:45:34 +1100 Subject: [PATCH] [8.17] [Index management] Fix a11y focus order in index mappings page (#203361) (#203627) # Backport This will backport the following commits from `main` to `8.17`: - [[Index management] Fix a11y focus order in index mappings page (#203361)](https://github.com/elastic/kibana/pull/203361) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com> --- .../document_fields/document_fields.tsx | 6 +++++- .../field_parameters/type_parameter.tsx | 5 +++++ .../fields/create_field/create_field.tsx | 16 +++++++++++++++- .../document_fields/fields/fields_list.tsx | 3 +++ .../document_fields/fields/fields_list_item.tsx | 5 ++++- .../fields/fields_list_item_container.tsx | 3 +++ .../document_fields/fields_tree_editor.tsx | 14 +++++++++++--- .../details_page_mappings_content.tsx | 7 +++++-- 8 files changed, 51 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx index d81b1e3dc9e5b..54dd434458486 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx @@ -27,6 +27,7 @@ interface Props { onCancelAddingNewFields?: () => void; isAddingFields?: boolean; semanticTextInfo?: SemanticTextInfo; + pendingFieldsRef?: React.RefObject; } export const DocumentFields = React.memo( ({ @@ -35,6 +36,7 @@ export const DocumentFields = React.memo( onCancelAddingNewFields, isAddingFields, semanticTextInfo, + pendingFieldsRef, }: Props) => { const { fields, documentFields } = useMappingsState(); const dispatch = useDispatch(); @@ -58,6 +60,7 @@ export const DocumentFields = React.memo( onCancelAddingNewFields={onCancelAddingNewFields} isAddingFields={isAddingFields} semanticTextInfo={semanticTextInfo} + pendingFieldsRef={pendingFieldsRef} /> ); @@ -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 () => { diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx index ec467f113c46d..528019416ad69 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx @@ -24,6 +24,7 @@ interface Props { isMultiField?: boolean | null; showDocLink?: boolean; isSemanticTextEnabled?: boolean; + fieldTypeInputRef?: React.MutableRefObject; } export const TypeParameter = ({ @@ -31,6 +32,7 @@ export const TypeParameter = ({ isRootLevelField, showDocLink = false, isSemanticTextEnabled = true, + fieldTypeInputRef, }: Props) => { const fieldTypeOptions = useMemo(() => { let options = isMultiField @@ -97,6 +99,9 @@ export const TypeParameter = ({ onChange={typeField.setValue} isClearable={false} data-test-subj="fieldType" + inputRef={(input) => { + if (fieldTypeInputRef) fieldTypeInputRef.current = input; + }} /> ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index 1daa437356546..0289f07534d35 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -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'; @@ -62,6 +62,7 @@ interface Props { onCancelAddingNewFields?: () => void; isAddingFields?: boolean; semanticTextInfo?: SemanticTextInfo; + createFieldFormRef?: React.RefObject; } export const CreateField = React.memo(function CreateFieldComponent({ @@ -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(null); const { form } = useForm({ serializer: fieldSerializer, @@ -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, @@ -134,6 +141,10 @@ export const CreateField = React.memo(function CreateFieldComponent({ } form.reset(); } + + if (fieldTypeInputRef.current) { + fieldTypeInputRef.current.focus(); + } }; const onClickOutside = () => { @@ -157,6 +168,7 @@ export const CreateField = React.memo(function CreateFieldComponent({ isMultiField={isMultiField} showDocLink isSemanticTextEnabled={isSemanticTextEnabled} + fieldTypeInputRef={fieldTypeInputRef} /> @@ -266,6 +278,8 @@ export const CreateField = React.memo(function CreateFieldComponent({ : paddingLeft }px`, }} + ref={createFieldFormRef} + tabIndex={0} >
{renderFormFields()} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx index 241fa132a28bf..9b2e8e0219908 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx @@ -16,6 +16,7 @@ interface Props { state: State; setPreviousState?: (state: State) => void; isAddingFields?: boolean; + pendingFieldsRef?: React.RefObject; } export const FieldsList = React.memo(function FieldsListComponent({ @@ -24,6 +25,7 @@ export const FieldsList = React.memo(function FieldsListComponent({ state, setPreviousState, isAddingFields, + pendingFieldsRef, }: Props) { if (fields === undefined) { return null; @@ -39,6 +41,7 @@ export const FieldsList = React.memo(function FieldsListComponent({ state={state} setPreviousState={setPreviousState} isAddingFields={isAddingFields} + pendingFieldsRef={pendingFieldsRef} /> ))} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx index 33c51a3cb644b..505ca26ce7ca4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx @@ -64,6 +64,8 @@ interface Props { treeDepth: number; state: State; isAddingFields?: boolean; + createFieldFormRef?: React.RefObject; + pendingFieldsRef?: React.RefObject; } function FieldListItemComponent( @@ -85,6 +87,7 @@ function FieldListItemComponent( state, isAddingFields, setPreviousState, + pendingFieldsRef, }: Props, ref: React.Ref ) { @@ -141,7 +144,6 @@ function FieldListItemComponent( const { addMultiFieldButtonLabel, addPropertyButtonLabel, editButtonLabel, deleteButtonLabel } = i18nTexts; - return ( {canHaveMultiFields && ( @@ -321,6 +323,7 @@ function FieldListItemComponent( state={state} isAddingFields={isAddingFields} setPreviousState={setPreviousState} + pendingFieldsRef={pendingFieldsRef} /> )} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx index a5e85eb1aad17..bcadd859f179e 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx @@ -18,6 +18,7 @@ interface Props { state: State; setPreviousState?: (state: State) => void; isAddingFields?: boolean; + pendingFieldsRef?: React.RefObject; } export const FieldsListItemContainer = ({ @@ -27,6 +28,7 @@ export const FieldsListItemContainer = ({ state, setPreviousState, isAddingFields, + pendingFieldsRef, }: Props) => { const dispatch = useDispatch(); const listElement = useRef(null); @@ -110,6 +112,7 @@ export const FieldsListItemContainer = ({ toggleExpand={toggleExpand} state={state} isAddingFields={isAddingFields} + pendingFieldsRef={pendingFieldsRef} /> ); }; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx index e7ae3c995a2f3..9274f10ab603e 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx @@ -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'; @@ -16,19 +16,21 @@ interface Props { onCancelAddingNewFields?: () => void; isAddingFields?: boolean; semanticTextInfo?: SemanticTextInfo; + pendingFieldsRef?: React.RefObject; } export const DocumentFieldsTreeEditor = ({ onCancelAddingNewFields, isAddingFields, semanticTextInfo, + pendingFieldsRef, }: Props) => { const dispatch = useDispatch(); const { fields: { byId, rootLevelFields }, documentFields: { status, fieldToAddFieldTo }, } = useMappingsState(); - + const createFieldFormRef = useRef(null); const getField = useCallback((fieldId: string) => byId[fieldId], [byId]); const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields, getField]); @@ -52,6 +54,7 @@ export const DocumentFieldsTreeEditor = ({ onCancelAddingNewFields={onCancelAddingNewFields} isAddingFields={isAddingFields} semanticTextInfo={semanticTextInfo} + createFieldFormRef={createFieldFormRef} /> ); }; @@ -77,7 +80,12 @@ export const DocumentFieldsTreeEditor = ({ return ( <> - + {renderCreateField()} {renderAddFieldButton()} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx index a43cdf0e8367d..524230eee18a7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx @@ -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 { @@ -83,6 +83,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{ overlays, history, } = useAppContext(); + const pendingFieldsRef = useRef(null); const [isPlatinumLicense, setIsPlatinumLicense] = useState(false); useEffect(() => { @@ -543,7 +544,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{ {errorSavingMappings} {isAddingFields && ( - + ) : ( )}