mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Index management] Fix a11y focus order in index mappings page (#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
This commit is contained in:
parent
6e57a23d18
commit
4b0c0e9269
8 changed files with 51 additions and 8 deletions
|
@ -27,6 +27,7 @@ interface Props {
|
|||
onCancelAddingNewFields?: () => void;
|
||||
isAddingFields?: boolean;
|
||||
semanticTextInfo?: SemanticTextInfo;
|
||||
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
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 () => {
|
||||
|
|
|
@ -24,6 +24,7 @@ interface Props {
|
|||
isMultiField?: boolean | null;
|
||||
showDocLink?: boolean;
|
||||
isSemanticTextEnabled?: boolean;
|
||||
fieldTypeInputRef?: React.MutableRefObject<HTMLInputElement | null>;
|
||||
}
|
||||
|
||||
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;
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
|
|
|
@ -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<HTMLDivElement>;
|
||||
}
|
||||
|
||||
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<HTMLInputElement>(null);
|
||||
|
||||
const { form } = useForm<Field>({
|
||||
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}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
|
@ -266,6 +278,8 @@ export const CreateField = React.memo(function CreateFieldComponent({
|
|||
: paddingLeft
|
||||
}px`,
|
||||
}}
|
||||
ref={createFieldFormRef}
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className="mappingsEditor__createFieldContent">
|
||||
{renderFormFields()}
|
||||
|
|
|
@ -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({
|
||||
|
@ -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}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -64,6 +64,8 @@ interface Props {
|
|||
treeDepth: number;
|
||||
state: State;
|
||||
isAddingFields?: boolean;
|
||||
createFieldFormRef?: React.RefObject<HTMLDivElement>;
|
||||
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
function FieldListItemComponent(
|
||||
|
@ -85,6 +87,7 @@ function FieldListItemComponent(
|
|||
state,
|
||||
isAddingFields,
|
||||
setPreviousState,
|
||||
pendingFieldsRef,
|
||||
}: Props,
|
||||
ref: React.Ref<HTMLLIElement>
|
||||
) {
|
||||
|
@ -141,7 +144,6 @@ function FieldListItemComponent(
|
|||
|
||||
const { addMultiFieldButtonLabel, addPropertyButtonLabel, editButtonLabel, deleteButtonLabel } =
|
||||
i18nTexts;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" className="mappingsEditor__fieldsListItem__actions">
|
||||
{canHaveMultiFields && (
|
||||
|
@ -321,6 +323,7 @@ function FieldListItemComponent(
|
|||
state={state}
|
||||
isAddingFields={isAddingFields}
|
||||
setPreviousState={setPreviousState}
|
||||
pendingFieldsRef={pendingFieldsRef}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ interface Props {
|
|||
state: State;
|
||||
setPreviousState?: (state: State) => void;
|
||||
isAddingFields?: boolean;
|
||||
pendingFieldsRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export const FieldsListItemContainer = ({
|
||||
|
@ -27,6 +28,7 @@ export const FieldsListItemContainer = ({
|
|||
state,
|
||||
setPreviousState,
|
||||
isAddingFields,
|
||||
pendingFieldsRef,
|
||||
}: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const listElement = useRef<HTMLLIElement | null>(null);
|
||||
|
@ -110,6 +112,7 @@ export const FieldsListItemContainer = ({
|
|||
toggleExpand={toggleExpand}
|
||||
state={state}
|
||||
isAddingFields={isAddingFields}
|
||||
pendingFieldsRef={pendingFieldsRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<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]);
|
||||
|
||||
|
@ -52,6 +54,7 @@ export const DocumentFieldsTreeEditor = ({
|
|||
onCancelAddingNewFields={onCancelAddingNewFields}
|
||||
isAddingFields={isAddingFields}
|
||||
semanticTextInfo={semanticTextInfo}
|
||||
createFieldFormRef={createFieldFormRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -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()}
|
||||
</>
|
||||
|
|
|
@ -27,7 +27,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 {
|
||||
|
@ -85,6 +85,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
overlays,
|
||||
history,
|
||||
} = useAppContext();
|
||||
const pendingFieldsRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isPlatinumLicense, setIsPlatinumLicense] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
|
@ -559,7 +560,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}
|
||||
|
@ -597,11 +598,13 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
onCancelAddingNewFields={onCancelAddingNewFields}
|
||||
isAddingFields={isAddingFields}
|
||||
semanticTextInfo={semanticTextInfo}
|
||||
pendingFieldsRef={pendingFieldsRef}
|
||||
/>
|
||||
) : (
|
||||
<DocumentFields
|
||||
isAddingFields={isAddingFields}
|
||||
semanticTextInfo={semanticTextInfo}
|
||||
pendingFieldsRef={pendingFieldsRef}
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue