mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.16`: - [[Index management] Fix a11y focus order in index mappings page (#203361)](https://github.com/elastic/kibana/pull/203361) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Saarika Bhasi","email":"55930906+saarikabhasi@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-12-10T15:46:20Z","message":"[Index management] Fix a11y focus order in index mappings page (#203361)\n\n## Summary\r\n\r\nFix a11y focus order in index mappings page. When new field is in\r\npending state and after closing edit pending field Flyout.\r\n\r\n\r\nhttps://github.com/user-attachments/assets/dbdf59e5-0ebd-47e0-9b5e-19ab1556e771\r\n\r\n### Test instructions \r\n#### Adding a field\r\n1. Add new field in index mappings page by navigating via tab \r\n2. Notice that type fields combo box is focused\r\n3. Add field and click to Add field button again with in pending fields\r\nform\r\n4. Notice focus is on new create field form\r\n\r\n#### Edit field in pending state\r\n1. Add new fields via tab key\r\n2. click on edit field \r\n3. Try closing, updating and cancelling in the edit field flyout form\r\n4. Notice after edit field flyout is closed, focus is on the pending\r\nfields form","sha":"4b0c0e92693ad759e3ce45b4b259c9908ddd0d51","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Search","backport:prev-major","v8.16.0","v8.17.0"],"title":"[Index management] Fix a11y focus order in index mappings page ","number":203361,"url":"https://github.com/elastic/kibana/pull/203361","mergeCommit":{"message":"[Index management] Fix a11y focus order in index mappings page (#203361)\n\n## Summary\r\n\r\nFix a11y focus order in index mappings page. When new field is in\r\npending state and after closing edit pending field Flyout.\r\n\r\n\r\nhttps://github.com/user-attachments/assets/dbdf59e5-0ebd-47e0-9b5e-19ab1556e771\r\n\r\n### Test instructions \r\n#### Adding a field\r\n1. Add new field in index mappings page by navigating via tab \r\n2. Notice that type fields combo box is focused\r\n3. Add field and click to Add field button again with in pending fields\r\nform\r\n4. Notice focus is on new create field form\r\n\r\n#### Edit field in pending state\r\n1. Add new fields via tab key\r\n2. click on edit field \r\n3. Try closing, updating and cancelling in the edit field flyout form\r\n4. Notice after edit field flyout is closed, focus is on the pending\r\nfields form","sha":"4b0c0e92693ad759e3ce45b4b259c9908ddd0d51"}},"sourceBranch":"main","suggestedTargetBranches":["8.16","8.17"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/203361","number":203361,"mergeCommit":{"message":"[Index management] Fix a11y focus order in index mappings page (#203361)\n\n## Summary\r\n\r\nFix a11y focus order in index mappings page. When new field is in\r\npending state and after closing edit pending field Flyout.\r\n\r\n\r\nhttps://github.com/user-attachments/assets/dbdf59e5-0ebd-47e0-9b5e-19ab1556e771\r\n\r\n### Test instructions \r\n#### Adding a field\r\n1. Add new field in index mappings page by navigating via tab \r\n2. Notice that type fields combo box is focused\r\n3. Add field and click to Add field button again with in pending fields\r\nform\r\n4. Notice focus is on new create field form\r\n\r\n#### Edit field in pending state\r\n1. Add new fields via tab key\r\n2. click on edit field \r\n3. Try closing, updating and cancelling in the edit field flyout form\r\n4. Notice after edit field flyout is closed, focus is on the pending\r\nfields form","sha":"4b0c0e92693ad759e3ce45b4b259c9908ddd0d51"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com>
This commit is contained in:
parent
371ae4de45
commit
7bcb2627ea
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()}
|
||||
</>
|
||||
|
|
|
@ -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 {
|
||||
|
@ -81,6 +81,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
overlays,
|
||||
history,
|
||||
} = useAppContext();
|
||||
const pendingFieldsRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isPlatinumLicense, setIsPlatinumLicense] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
|
@ -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}
|
||||
|
@ -578,11 +579,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