Skip to content

Commit

Permalink
[8.x] [Search][A11Y] Playground -> Open AI form (#202071) (#204428)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to `8.x`:
- [[Search][A11Y] Playground -> Open AI form
(#202071)](#202071)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Samiul
Monir","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-16T15:49:37Z","message":"[Search][A11Y]
Playground -> Open AI form (#202071)\n\n##
Summary\r\n\r\nrefers\r\nhttps://github.com//issues/195048#issuecomment-2393788073\r\n\r\nThe
goal is to display a high-level error message at the top of the\r\nform.
We don’t need to show specific details but can provide a
general\r\nhint, such as `There are some errors in the form`. When using
a screen\r\nreader, this message will indicate that the form submission
failed due\r\nto errors.\r\n\r\nEach individual field will then be
responsible for displaying its own\r\nspecific error
messages.\r\n\r\nThis approach ensures compliance with A11Y
standards.\r\n\r\n### PR Screen Record\r\n\r\n####
Add\r\n\r\n\r\nhttps://github.com/user-attachments/assets/2e6c3304-0ad1-4d84-acc0-f9413e1fd73f\r\n\r\n####
Edit\r\n\r\n\r\nhttps://github.com/user-attachments/assets/7f54114c-4b95-40cd-bdec-f506d7151674\r\n\r\n\r\n\r\n###
Checklist\r\n\r\nCheck the PR satisfies following conditions.
\r\n\r\nReviewers should verify this PR satisfies this list as
well.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[X] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"07c64de7ef08ece1b2710fa54fbeb29460ec809d","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","v9.0.0","Team:Search","backport:version","a11y","v8.18.0","v8.16.2","v8.17.1"],"title":"[Search][A11Y]
Playground -> Open AI
form","number":202071,"url":"https://github.com/elastic/kibana/pull/202071","mergeCommit":{"message":"[Search][A11Y]
Playground -> Open AI form (#202071)\n\n##
Summary\r\n\r\nrefers\r\nhttps://github.com//issues/195048#issuecomment-2393788073\r\n\r\nThe
goal is to display a high-level error message at the top of the\r\nform.
We don’t need to show specific details but can provide a
general\r\nhint, such as `There are some errors in the form`. When using
a screen\r\nreader, this message will indicate that the form submission
failed due\r\nto errors.\r\n\r\nEach individual field will then be
responsible for displaying its own\r\nspecific error
messages.\r\n\r\nThis approach ensures compliance with A11Y
standards.\r\n\r\n### PR Screen Record\r\n\r\n####
Add\r\n\r\n\r\nhttps://github.com/user-attachments/assets/2e6c3304-0ad1-4d84-acc0-f9413e1fd73f\r\n\r\n####
Edit\r\n\r\n\r\nhttps://github.com/user-attachments/assets/7f54114c-4b95-40cd-bdec-f506d7151674\r\n\r\n\r\n\r\n###
Checklist\r\n\r\nCheck the PR satisfies following conditions.
\r\n\r\nReviewers should verify this PR satisfies this list as
well.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[X] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"07c64de7ef08ece1b2710fa54fbeb29460ec809d"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","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/202071","number":202071,"mergeCommit":{"message":"[Search][A11Y]
Playground -> Open AI form (#202071)\n\n##
Summary\r\n\r\nrefers\r\nhttps://github.com//issues/195048#issuecomment-2393788073\r\n\r\nThe
goal is to display a high-level error message at the top of the\r\nform.
We don’t need to show specific details but can provide a
general\r\nhint, such as `There are some errors in the form`. When using
a screen\r\nreader, this message will indicate that the form submission
failed due\r\nto errors.\r\n\r\nEach individual field will then be
responsible for displaying its own\r\nspecific error
messages.\r\n\r\nThis approach ensures compliance with A11Y
standards.\r\n\r\n### PR Screen Record\r\n\r\n####
Add\r\n\r\n\r\nhttps://github.com/user-attachments/assets/2e6c3304-0ad1-4d84-acc0-f9413e1fd73f\r\n\r\n####
Edit\r\n\r\n\r\nhttps://github.com/user-attachments/assets/7f54114c-4b95-40cd-bdec-f506d7151674\r\n\r\n\r\n\r\n###
Checklist\r\n\r\nCheck the PR satisfies following conditions.
\r\n\r\nReviewers should verify this PR satisfies this list as
well.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[X] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"07c64de7ef08ece1b2710fa54fbeb29460ec809d"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.16","label":"v8.16.2","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Samiul Monir <[email protected]>
  • Loading branch information
kibanamachine and Samiul-TheSoccerFan authored Dec 16, 2024
1 parent e8e5844 commit 8cd6eab
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { lazy } from 'react';

import { actionTypeRegistryMock } from '../../../action_type_registry.mock';
import userEvent from '@testing-library/user-event';
import { waitFor, act } from '@testing-library/react';
import { waitFor, act, screen } from '@testing-library/react';
import CreateConnectorFlyout from '.';
import { AppMockRenderer, createAppMockRenderer } from '../../test_utils';
import { TECH_PREVIEW_LABEL } from '../../translations';
Expand Down Expand Up @@ -426,7 +426,7 @@ describe('CreateConnectorFlyout', () => {

describe('Submitting', () => {
it('creates a connector correctly', async () => {
const { getByTestId } = appMockRenderer.render(
const { getByTestId, queryByTestId } = appMockRenderer.render(
<CreateConnectorFlyout
actionTypeRegistry={actionTypeRegistry}
onClose={onClose}
Expand Down Expand Up @@ -469,6 +469,70 @@ describe('CreateConnectorFlyout', () => {
name: 'My test',
secrets: {},
});
expect(queryByTestId('connector-form-header-error-label')).not.toBeInTheDocument();
});

it('show error message in the form header', async () => {
appMockRenderer.render(
<CreateConnectorFlyout
actionTypeRegistry={actionTypeRegistry}
onClose={onClose}
onConnectorCreated={onConnectorCreated}
onTestConnector={onTestConnector}
/>
);

await userEvent.click(await screen.findByTestId(`${actionTypeModel.id}-card`));
expect(await screen.findByTestId('test-connector-text-field')).toBeInTheDocument();

await userEvent.type(
await screen.findByTestId('test-connector-text-field'),
'My text field',
{
delay: 100,
}
);

await userEvent.click(await screen.findByTestId('create-connector-flyout-save-btn'));
expect(onClose).not.toHaveBeenCalled();
expect(onConnectorCreated).not.toHaveBeenCalled();
expect(await screen.findByTestId('connector-form-header-error-label')).toBeInTheDocument();
});

it('removes error message from the form header', async () => {
appMockRenderer.render(
<CreateConnectorFlyout
actionTypeRegistry={actionTypeRegistry}
onClose={onClose}
onConnectorCreated={onConnectorCreated}
onTestConnector={onTestConnector}
/>
);

await userEvent.click(await screen.findByTestId(`${actionTypeModel.id}-card`));
expect(await screen.findByTestId('test-connector-text-field')).toBeInTheDocument();

await userEvent.type(
await screen.findByTestId('test-connector-text-field'),
'My text field',
{
delay: 100,
}
);

await userEvent.click(await screen.findByTestId('create-connector-flyout-save-btn'));
expect(onClose).not.toHaveBeenCalled();
expect(onConnectorCreated).not.toHaveBeenCalled();
expect(await screen.findByTestId('connector-form-header-error-label')).toBeInTheDocument();

await userEvent.type(await screen.findByTestId('nameInput'), 'My test', {
delay: 100,
});

await userEvent.click(await screen.findByTestId('create-connector-flyout-save-btn'));
expect(onClose).toHaveBeenCalled();
expect(onConnectorCreated).toHaveBeenCalled();
expect(screen.queryByTestId('connector-form-header-error-label')).not.toBeInTheDocument();
});

it('runs pre submit validator correctly', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React, { memo, ReactNode, useCallback, useEffect, useRef, useState } from
import {
EuiButton,
EuiButtonGroup,
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
Expand All @@ -18,6 +19,7 @@ import {

import { getConnectorCompatibility } from '@kbn/actions-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import {
ActionConnector,
ActionType,
Expand Down Expand Up @@ -60,6 +62,7 @@ const CreateConnectorFlyoutComponent: React.FC<CreateConnectorFlyoutProps> = ({
const [actionType, setActionType] = useState<ActionType | null>(null);
const [hasActionsUpgradeableByTrial, setHasActionsUpgradeableByTrial] = useState<boolean>(false);
const canSave = hasSaveActionsCapability(capabilities);
const [showFormErrors, setShowFormErrors] = useState<boolean>(false);

const [preSubmitValidationErrorMessage, setPreSubmitValidationErrorMessage] =
useState<ReactNode>(null);
Expand Down Expand Up @@ -106,6 +109,7 @@ const CreateConnectorFlyoutComponent: React.FC<CreateConnectorFlyoutProps> = ({

const setResetForm = (reset: ResetForm) => {
resetConnectorForm.current = reset;
setShowFormErrors(false);
};

const onChangeGroupAction = (id: string) => {
Expand All @@ -127,6 +131,7 @@ const CreateConnectorFlyoutComponent: React.FC<CreateConnectorFlyoutProps> = ({

const validateAndCreateConnector = useCallback(async () => {
setPreSubmitValidationErrorMessage(null);
setShowFormErrors(false);

const { isValid, data } = await submit();
if (!isMounted.current) {
Expand Down Expand Up @@ -159,6 +164,8 @@ const CreateConnectorFlyoutComponent: React.FC<CreateConnectorFlyoutProps> = ({

const createdConnector = await createConnector(validConnector);
return createdConnector;
} else {
setShowFormErrors(true);
}
}, [submit, preSubmitValidator, createConnector]);

Expand Down Expand Up @@ -228,6 +235,23 @@ const CreateConnectorFlyoutComponent: React.FC<CreateConnectorFlyoutProps> = ({
<EuiSpacer size="xs" />
</>
)}
{showFormErrors && (
<>
<EuiCallOut
size="s"
color="danger"
iconType="warning"
data-test-subj="connector-form-header-error-label"
title={i18n.translate(
'xpack.triggersActionsUI.sections.actionConnectorAdd.headerFormLabel',
{
defaultMessage: 'There are errors in the form',
}
)}
/>
<EuiSpacer size="m" />
</>
)}
<ConnectorForm
actionTypeModel={actionTypeModel}
connector={initialConnector}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { lazy } from 'react';

import { actionTypeRegistryMock } from '../../../action_type_registry.mock';
import userEvent from '@testing-library/user-event';
import { waitFor, act } from '@testing-library/react';
import { waitFor, act, screen } from '@testing-library/react';
import EditConnectorFlyout from '.';
import { ActionConnector, EditConnectorTabs, GenericValidationResult } from '../../../../types';
import { AppMockRenderer, createAppMockRenderer } from '../../test_utils';
Expand Down Expand Up @@ -415,7 +415,7 @@ describe('EditConnectorFlyout', () => {

describe('Submitting', () => {
it('updates the connector correctly', async () => {
const { getByTestId } = appMockRenderer.render(
const { getByTestId, queryByTestId } = appMockRenderer.render(
<EditConnectorFlyout
actionTypeRegistry={actionTypeRegistry}
onClose={onClose}
Expand Down Expand Up @@ -459,6 +459,7 @@ describe('EditConnectorFlyout', () => {
name: 'My test',
secrets: {},
});
expect(queryByTestId('connector-form-header-error-label')).not.toBeInTheDocument();
});

it('updates connector form field with latest value', async () => {
Expand Down Expand Up @@ -555,6 +556,39 @@ describe('EditConnectorFlyout', () => {
});
});

it('show error message in the form header', async () => {
appMockRenderer.render(
<EditConnectorFlyout
actionTypeRegistry={actionTypeRegistry}
onClose={onClose}
connector={connector}
onConnectorUpdated={onConnectorUpdated}
/>
);

expect(await screen.findByTestId('test-connector-text-field')).toBeInTheDocument();
await userEvent.clear(screen.getByTestId('nameInput'));
await userEvent.click(screen.getByTestId('edit-connector-flyout-save-btn'));
expect(await screen.findByTestId('connector-form-header-error-label')).toBeInTheDocument();
});

it('removes error message from the form header', async () => {
appMockRenderer.render(
<EditConnectorFlyout
actionTypeRegistry={actionTypeRegistry}
onClose={onClose}
connector={connector}
onConnectorUpdated={onConnectorUpdated}
/>
);

await userEvent.clear(screen.getByTestId('nameInput'));
await userEvent.type(screen.getByTestId('nameInput'), 'My new name');
await userEvent.type(screen.getByTestId('test-connector-secret-text-field'), 'password');
await userEvent.click(screen.getByTestId('edit-connector-flyout-save-btn'));
expect(screen.queryByTestId('connector-form-header-error-label')).not.toBeInTheDocument();
});

it('runs pre submit validator correctly', async () => {
const errorActionTypeModel = actionTypeRegistryMock.createMockActionTypeModel({
actionConnectorFields: lazy(() => import('../connector_error_mock')),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
*/

import React, { memo, ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import { EuiFlyout, EuiFlyoutBody, EuiButton, EuiConfirmModal } from '@elastic/eui';
import {
EuiFlyout,
EuiFlyoutBody,
EuiButton,
EuiConfirmModal,
EuiCallOut,
EuiSpacer,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { ActionTypeExecutorResult, isActionTypeExecutorResult } from '@kbn/actions-plugin/common';
Expand Down Expand Up @@ -62,6 +69,7 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
const canSave = hasSaveActionsCapability(capabilities);
const { isLoading: isUpdatingConnector, updateConnector } = useUpdateConnector();
const { isLoading: isExecutingConnector, executeConnector } = useExecuteConnector();
const [showFormErrors, setShowFormErrors] = useState<boolean>(false);

const [preSubmitValidationErrorMessage, setPreSubmitValidationErrorMessage] =
useState<ReactNode>(null);
Expand Down Expand Up @@ -90,6 +98,7 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
if (nextPage === EditConnectorTabs.Configuration && testExecutionResult !== none) {
setTestExecutionResult(none);
}
setShowFormErrors(false);
setTab(nextPage);
},
[testExecutionResult, setTestExecutionResult]
Expand Down Expand Up @@ -146,6 +155,7 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({

const onClickSave = useCallback(async () => {
setPreSubmitValidationErrorMessage(null);
setShowFormErrors(false);

const { isValid, data } = await submit();
if (!isMounted.current) {
Expand Down Expand Up @@ -194,6 +204,8 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
}

return updatedConnector;
} else {
setShowFormErrors(true);
}
}, [
onConnectorUpdated,
Expand All @@ -218,6 +230,23 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
<>
{isEdit && (
<>
{showFormErrors && (
<>
<EuiCallOut
size="s"
color="danger"
iconType="warning"
data-test-subj="connector-form-header-error-label"
title={i18n.translate(
'xpack.triggersActionsUI.sections.editConnectorForm.headerFormLabel',
{
defaultMessage: 'There are errors in the form',
}
)}
/>
<EuiSpacer size="m" />
</>
)}
<ConnectorForm
actionTypeModel={actionTypeModel}
connector={getConnectorWithoutSecrets(connector)}
Expand Down Expand Up @@ -265,17 +294,18 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
);
}, [
connector,
docLinks.links.alerting.preconfiguredConnectors,
actionTypeModel,
isEdit,
docLinks.links.alerting.preconfiguredConnectors,
hasErrors,
isFormModified,
isSaved,
isSaving,
showFormErrors,
onFormModifiedChange,
preSubmitValidationErrorMessage,
showButtons,
isSaved,
isSaving,
onClickSave,
onFormModifiedChange,
isFormModified,
hasErrors,
]);

const renderTestTab = useCallback(() => {
Expand Down

0 comments on commit 8cd6eab

Please sign in to comment.