Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.cancelButtonProps 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-label="Edit inline"
class="euiFieldText euiFieldText--fullWidth"
data-test-subj="euiInlineEditModeInput"
Expand All @@ -38,6 +39,12 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.cancelButtonProps 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down Expand Up @@ -169,6 +176,7 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.formRowProps 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-label="Edit inline"
class="euiFieldText euiFieldText--fullWidth"
data-test-subj="euiInlineEditModeInput"
Expand All @@ -180,6 +188,12 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.formRowProps 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down Expand Up @@ -315,6 +329,7 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.inputProps 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-label="Edit inline"
class="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
data-test-subj="customInput"
Expand All @@ -326,6 +341,12 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.inputProps 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down Expand Up @@ -455,6 +476,7 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.saveButtonProps 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-label="Edit inline"
class="euiFieldText euiFieldText--fullWidth"
data-test-subj="euiInlineEditModeInput"
Expand All @@ -466,6 +488,12 @@ exports[`EuiInlineEditForm Edit Mode editModeProps.saveButtonProps 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down Expand Up @@ -595,6 +623,7 @@ exports[`EuiInlineEditForm Edit Mode isInvalid 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-invalid="true"
aria-label="Edit inline"
class="euiFieldText euiFormControlLayout--1icons euiFieldText--fullWidth"
Expand All @@ -615,6 +644,12 @@ exports[`EuiInlineEditForm Edit Mode isInvalid 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down Expand Up @@ -744,6 +779,7 @@ exports[`EuiInlineEditForm Edit Mode isLoading 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-label="Edit inline"
class="euiFieldText euiFormControlLayout--1icons euiFieldText--fullWidth euiFieldText-isLoading"
data-test-subj="euiInlineEditModeInput"
Expand All @@ -764,6 +800,12 @@ exports[`EuiInlineEditForm Edit Mode isLoading 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down Expand Up @@ -847,6 +889,7 @@ exports[`EuiInlineEditForm Edit Mode renders 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-describedby="inlineEdit_generated-id"
aria-label="Edit inline"
class="euiFieldText euiFieldText--fullWidth"
data-test-subj="euiInlineEditModeInput"
Expand All @@ -858,6 +901,12 @@ exports[`EuiInlineEditForm Edit Mode renders 1`] = `
</div>
</div>
</div>
<span
hidden=""
id="inlineEdit_generated-id"
>
Press Enter to save your edited text. Press Escape to cancel your edit.
</span>
</div>
<div
class="euiFlexItem euiInlineEdit testClass1 testClass2 emotion-euiFlexItem-growZero"
Expand Down
64 changes: 64 additions & 0 deletions src/components/inline_edit/inline_edit_form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,5 +333,69 @@ describe('EuiInlineEditForm', () => {
});
});
});

describe('keyboard events', () => {
test('pressing the Enter key saves text and returns to readMode', () => {
const { getByTestSubject, getByText } = render(
<EuiInlineEditForm
{...commonInlineEditFormProps}
startWithEditOpen={true}
/>
);

fireEvent.change(getByTestSubject('euiInlineEditModeInput'), {
target: { value: 'New message!' },
});
fireEvent.keyDown(getByTestSubject('euiInlineEditModeInput'), {
key: 'Enter',
});

expect(getByTestSubject('euiInlineReadModeButton')).toBeTruthy();
expect(getByText('New message!')).toBeTruthy();
});

test('pressing the Escape key cancels text changes and returns to readMode', () => {
const { getByTestSubject, getByText } = render(
<EuiInlineEditForm
{...commonInlineEditFormProps}
startWithEditOpen={true}
/>
);

fireEvent.change(getByTestSubject('euiInlineEditModeInput'), {
target: { value: 'New message!' },
});
fireEvent.keyDown(getByTestSubject('euiInlineEditModeInput'), {
key: 'Escape',
});

expect(getByTestSubject('euiInlineReadModeButton')).toBeTruthy();
expect(getByText('Hello World!')).toBeTruthy();
});

it('calls passed `inputModeProps.onKeyDown` callbacks', () => {
const onKeyDown = jest.fn();

const { getByTestSubject, getByText } = render(
<EuiInlineEditForm
{...commonInlineEditFormProps}
startWithEditOpen={true}
editModeProps={{ inputProps: { onKeyDown } }}
/>
);

fireEvent.change(getByTestSubject('euiInlineEditModeInput'), {
target: { value: 'New message!' },
});
fireEvent.keyDown(getByTestSubject('euiInlineEditModeInput'), {
key: 'Enter',
});

// Both EUI and consumer `onKeyDown` events should have run
expect(onKeyDown).toHaveBeenCalled();
expect(getByTestSubject('euiInlineReadModeButton')).toBeTruthy();
expect(getByText('New message!')).toBeTruthy();
});
});
});
});
32 changes: 30 additions & 2 deletions src/components/inline_edit/inline_edit_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import React, {
useState,
HTMLAttributes,
MouseEvent,
KeyboardEvent,
} from 'react';
import classNames from 'classnames';

Expand All @@ -29,8 +30,8 @@ import { EuiButtonIconPropsForButton } from '../button/button_icon';
import { EuiButtonEmptyPropsForButton } from '../button/button_empty/button_empty';
import { EuiFlexGroup, EuiFlexItem } from '../flex';
import { EuiSkeletonRectangle } from '../skeleton';
import { useEuiTheme } from '../../services';
import { useEuiI18n } from '../i18n';
import { useEuiTheme, keys } from '../../services';
import { EuiI18n, useEuiI18n } from '../i18n';
import { useGeneratedHtmlId } from '../../services/accessibility';

// Props shared between the internal form component as well as consumer-facing components
Expand Down Expand Up @@ -139,6 +140,8 @@ export const EuiInlineEditForm: FunctionComponent<EuiInlineEditFormProps> = ({
'Cancel edit'
);

const editModeDescribedById = useGeneratedHtmlId({ prefix: 'inlineEdit' });

const [isEditing, setIsEditing] = useState(false || startWithEditOpen);
const inlineEditInputId = useGeneratedHtmlId({ prefix: '__inlineEditInput' });

Expand All @@ -163,6 +166,17 @@ export const EuiInlineEditForm: FunctionComponent<EuiInlineEditFormProps> = ({
setIsEditing(false);
};

const editModeInputOnKeyDown = (event: KeyboardEvent<HTMLElement>) => {
switch (event.key) {
case keys.ENTER:
saveInlineEditValue();
break;
case keys.ESCAPE:
cancelInlineEdit();
break;
}
};

const editModeForm = (
<EuiForm fullWidth>
<EuiFlexGroup gutterSize="s">
Expand All @@ -185,8 +199,22 @@ export const EuiInlineEditForm: FunctionComponent<EuiInlineEditFormProps> = ({
isLoading={isLoading}
data-test-subj="euiInlineEditModeInput"
{...editModeProps?.inputProps}
aria-describedby={classNames(
editModeDescribedById,
editModeProps?.inputProps?.['aria-describedby']
)}
onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
editModeInputOnKeyDown(e);
editModeProps?.inputProps?.onKeyDown?.(e);
}}
/>
</EuiFormRow>
<span id={editModeDescribedById} hidden>
<EuiI18n
token="euiInlineEditForm.inputKeyboardInstructions"
default="Press Enter to save your edited text. Press Escape to cancel your edit."
/>
</span>
</EuiFlexItem>

<EuiFlexItem grow={false} className={classes}>
Expand Down