Skip to content

Commit

Permalink
[8.16] [Search][a11y] Announce success and error events for screen re…
Browse files Browse the repository at this point in the history
…aders (#203555) (#204429)

# Backport

This will backport the following commits from `main` to `8.16`:
- [[Search][a11y] Announce success and error events for screen readers
(#203555)](#203555)

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

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

<!--BACKPORT [{"author":{"name":"José Luis
González","email":"joseluisgj@gmail.com"},"sourceCommit":{"committedDate":"2024-12-16T15:55:38Z","message":"[Search][a11y]
Announce success and error events for screen readers (#203555)\n\n##
Summary\r\n\r\nThis PR fixes this issue
https://github.com/elastic/kibana/issues/197400\r\nwere the UI didn't
announce that the connector was deleted. As we do\r\nwith indices, after
deleting a connector or Web Crawler the UI announces\r\nthis action with
a Toast component which is announced by screen readers\r\nlike
VoiceOver.\r\n\r\nAdded the attributes `aria-live=\"assertive\"` and
`role=\"alert\"` to get\r\nscreen readers priority on this UI element to
be announced over the rest\r\nof the UI.\r\nReferences:
\r\n-\r\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live\r\n-\r\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alert_role\r\n\r\nThis
change affects and fixes all the calls to the
`flashSuccessToast`\r\nmethod used when we delete an Index, a connector,
a Search Application\r\nand many
more.\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/b45b3ec4-1895-4c50-b926-a523882d8b25\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- [ ] 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-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [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- [ ] If a plugin
configuration key changed, check if it needs to be\r\nallowlisted in the
cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This was checked for breaking HTTP API changes, and any
breaking\r\nchanges have been approved by the breaking-change committee.
The\r\n`release_note:breaking` label should be applied in these
situations.\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [ ] The PR description includes
the appropriate Release Notes section,\r\nand the correct
`release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n###
Identify risks\r\n\r\nDoes this PR introduce any risks? For example,
consider risks like hard\r\nto test bugs, performance regression,
potential of data loss.\r\n\r\nDescribe the risk, its severity, and
mitigation for each identified\r\nrisk. Invite stakeholders and evaluate
how to proceed before merging.\r\n\r\n- [ ] [See some
risk\r\nexamples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)\r\n-
[ ] ...\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"a5e25b2d5d218ba0a4c9dc29eec2f21197b0e6e0","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Project:Accessibility","release_note:skip","v9.0.0","Team:Search","backport:all-open"],"title":"[Search][a11y]
Announce success and error events for screen
readers","number":203555,"url":"https://github.com/elastic/kibana/pull/203555","mergeCommit":{"message":"[Search][a11y]
Announce success and error events for screen readers (#203555)\n\n##
Summary\r\n\r\nThis PR fixes this issue
https://github.com/elastic/kibana/issues/197400\r\nwere the UI didn't
announce that the connector was deleted. As we do\r\nwith indices, after
deleting a connector or Web Crawler the UI announces\r\nthis action with
a Toast component which is announced by screen readers\r\nlike
VoiceOver.\r\n\r\nAdded the attributes `aria-live=\"assertive\"` and
`role=\"alert\"` to get\r\nscreen readers priority on this UI element to
be announced over the rest\r\nof the UI.\r\nReferences:
\r\n-\r\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live\r\n-\r\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alert_role\r\n\r\nThis
change affects and fixes all the calls to the
`flashSuccessToast`\r\nmethod used when we delete an Index, a connector,
a Search Application\r\nand many
more.\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/b45b3ec4-1895-4c50-b926-a523882d8b25\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- [ ] 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-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [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- [ ] If a plugin
configuration key changed, check if it needs to be\r\nallowlisted in the
cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This was checked for breaking HTTP API changes, and any
breaking\r\nchanges have been approved by the breaking-change committee.
The\r\n`release_note:breaking` label should be applied in these
situations.\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [ ] The PR description includes
the appropriate Release Notes section,\r\nand the correct
`release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n###
Identify risks\r\n\r\nDoes this PR introduce any risks? For example,
consider risks like hard\r\nto test bugs, performance regression,
potential of data loss.\r\n\r\nDescribe the risk, its severity, and
mitigation for each identified\r\nrisk. Invite stakeholders and evaluate
how to proceed before merging.\r\n\r\n- [ ] [See some
risk\r\nexamples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)\r\n-
[ ] ...\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"a5e25b2d5d218ba0a4c9dc29eec2f21197b0e6e0"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/203555","number":203555,"mergeCommit":{"message":"[Search][a11y]
Announce success and error events for screen readers (#203555)\n\n##
Summary\r\n\r\nThis PR fixes this issue
https://github.com/elastic/kibana/issues/197400\r\nwere the UI didn't
announce that the connector was deleted. As we do\r\nwith indices, after
deleting a connector or Web Crawler the UI announces\r\nthis action with
a Toast component which is announced by screen readers\r\nlike
VoiceOver.\r\n\r\nAdded the attributes `aria-live=\"assertive\"` and
`role=\"alert\"` to get\r\nscreen readers priority on this UI element to
be announced over the rest\r\nof the UI.\r\nReferences:
\r\n-\r\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live\r\n-\r\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alert_role\r\n\r\nThis
change affects and fixes all the calls to the
`flashSuccessToast`\r\nmethod used when we delete an Index, a connector,
a Search Application\r\nand many
more.\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/b45b3ec4-1895-4c50-b926-a523882d8b25\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- [ ] 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-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [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- [ ] If a plugin
configuration key changed, check if it needs to be\r\nallowlisted in the
cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This was checked for breaking HTTP API changes, and any
breaking\r\nchanges have been approved by the breaking-change committee.
The\r\n`release_note:breaking` label should be applied in these
situations.\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [ ] The PR description includes
the appropriate Release Notes section,\r\nand the correct
`release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n###
Identify risks\r\n\r\nDoes this PR introduce any risks? For example,
consider risks like hard\r\nto test bugs, performance regression,
potential of data loss.\r\n\r\nDescribe the risk, its severity, and
mitigation for each identified\r\nrisk. Invite stakeholders and evaluate
how to proceed before merging.\r\n\r\n- [ ] [See some
risk\r\nexamples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)\r\n-
[ ] ...\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"a5e25b2d5d218ba0a4c9dc29eec2f21197b0e6e0"}}]}]
BACKPORT-->

Co-authored-by: José Luis González <joseluisgj@gmail.com>
  • Loading branch information
kibanamachine and JoseLuisGJ authored Dec 16, 2024
1 parent 165abc4 commit 8e78b8e
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';

import { DeleteConnectorResponse } from '../../../../../common/types/connectors';

Expand All @@ -12,30 +13,42 @@ import { HttpLogic } from '../../../shared/http';

export interface DeleteConnectorApiLogicArgs {
connectorId: string;
connectorName: string;
shouldDeleteIndex: boolean;
}

export interface DeleteConnectorApiLogicResponse {
acknowledged: boolean;
connectorName: string;
}

export const deleteConnector = async ({
connectorId,
connectorName,
shouldDeleteIndex = false,
}: DeleteConnectorApiLogicArgs) => {
return await HttpLogic.values.http.delete(
`/internal/enterprise_search/connectors/${connectorId}`,
{
query: {
shouldDeleteIndex,
},
}
);
}: DeleteConnectorApiLogicArgs): Promise<DeleteConnectorApiLogicResponse> => {
await HttpLogic.values.http.delete(`/internal/enterprise_search/connectors/${connectorId}`, {
query: {
shouldDeleteIndex,
},
});
return { connectorName };
};

export const DeleteConnectorApiLogic = createApiLogic(
['delete_connector_api_logic'],
deleteConnector
deleteConnector,
{
showSuccessFlashFn: ({ connectorName }) =>
i18n.translate(
'xpack.enterpriseSearch.content.connectors.deleteConnector.successToast.title',
{
defaultMessage: 'The connector {connectorName} was successfully deleted',
values: {
connectorName,
},
}
),
}
);

export type DeleteConnectorApiLogicActions = Actions<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const DeleteConnectorModal: React.FC<DeleteConnectorModalProps> = ({ isCr
isDeleteModalVisible,
} = useValues(ConnectorsLogic);

const connectorName = isCrawler ? deleteModalIndexName : deleteModalConnectorName;
const connectorName = (isCrawler ? deleteModalIndexName : deleteModalConnectorName) || '';

const [inputConnectorName, setInputConnectorName] = useState('');
const [shouldDeleteIndex, setShouldDeleteIndex] = useState(false);
Expand Down Expand Up @@ -80,6 +80,7 @@ export const DeleteConnectorModal: React.FC<DeleteConnectorModalProps> = ({ isCr
} else {
deleteConnector({
connectorId,
connectorName,
shouldDeleteIndex,
});
setConnectorUiOptions(omit(connectorUiOptions, connectorId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ describe('PipelinesLogic', () => {
describe('apiSuccess', () => {
it('should call flashSuccessToast', () => {
PipelinesLogic.actions.apiSuccess({ connectorId: 'a', pipeline: newPipeline });
expect(flashSuccessToast).toHaveBeenCalledWith('Pipelines updated');
expect(flashSuccessToast).toHaveBeenCalledWith('Pipelines updated', {
'aria-live': 'assertive',
role: 'alert',
});
});
});
describe('createCustomPipelineError', () => {
Expand All @@ -154,7 +157,10 @@ describe('PipelinesLogic', () => {
PipelinesLogic.actions.fetchCustomPipeline = jest.fn();
PipelinesLogic.actions.fetchIndexApiSuccess(connectorIndex);
PipelinesLogic.actions.createCustomPipelineSuccess({ [connectorIndex.name]: {} });
expect(flashSuccessToast).toHaveBeenCalledWith('Custom pipeline created');
expect(flashSuccessToast).toHaveBeenCalledWith('Custom pipeline created', {
'aria-live': 'assertive',
role: 'alert',
});
expect(PipelinesLogic.actions.setPipelineState).toHaveBeenCalledWith({
...PipelinesLogic.values.pipelineState,
name: connectorIndex.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ describe('CreateApiLogic', () => {
const { mount: messageMount } = messageLogic;
messageMount();
messageLogic.actions.apiSuccess({});
expect(flashSuccessToast).toHaveBeenCalledWith('test message');
expect(flashSuccessToast).toHaveBeenCalledWith('test message', {
'aria-live': 'assertive',
role: 'alert',
});
});
});
describe('apiError', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ export const createApiLogic = <Result, Args>(
},
apiSuccess: (result) => {
if (options.showSuccessFlashFn) {
flashSuccessToast(options.showSuccessFlashFn(result));
flashSuccessToast(options.showSuccessFlashFn(result), {
'aria-live': 'assertive',
role: 'alert',
});
}
},
makeRequest: async (args, breakpoint) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,18 @@ describe('toastAPIErrors', () => {
it('converts API errors into flash messages', () => {
toastAPIErrors(mockHttpError);

expect(flashErrorToast).toHaveBeenNthCalledWith(1, 'Could not find X');
expect(flashErrorToast).toHaveBeenNthCalledWith(2, 'Could not find Y');
expect(flashErrorToast).toHaveBeenNthCalledWith(3, 'Something else bad happened');
expect(flashErrorToast).toHaveBeenNthCalledWith(1, 'Could not find X', {
'aria-live': 'assertive',
role: 'alert',
});
expect(flashErrorToast).toHaveBeenNthCalledWith(2, 'Could not find Y', {
'aria-live': 'assertive',
role: 'alert',
});
expect(flashErrorToast).toHaveBeenNthCalledWith(3, 'Something else bad happened', {
'aria-live': 'assertive',
role: 'alert',
});
});

it('falls back to the basic message for http responses without an errors array', () => {
Expand All @@ -117,7 +126,10 @@ describe('toastAPIErrors', () => {
},
} as any);

expect(flashErrorToast).toHaveBeenCalledWith('Not Found');
expect(flashErrorToast).toHaveBeenCalledWith('Not Found', {
'aria-live': 'assertive',
role: 'alert',
});
});

it('displays a generic error message and re-throws non-API errors', () => {
Expand All @@ -127,7 +139,10 @@ describe('toastAPIErrors', () => {
toastAPIErrors(error as any);
}).toThrowError(error);

expect(flashErrorToast).toHaveBeenCalledWith(expect.any(String));
expect(flashErrorToast).toHaveBeenCalledWith(expect.any(String), {
'aria-live': 'assertive',
role: 'alert',
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export const toastAPIErrors = (response: HttpResponse<ErrorResponse>) => {
const messages = getErrorsFromHttpResponse(response);

for (const message of messages) {
flashErrorToast(message);
flashErrorToast(message, {
'aria-live': 'assertive',
role: 'alert',
});
}
// If this was a programming error or a failed request (such as a CORS) error,
// we rethrow the error so it shows up in the developer console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export interface IFlashMessage {

// @see EuiGlobalToastListToast for more props
export interface ToastOptions {
'aria-live'?: 'assertive' | 'polite'; // Defaults to 'polite'
iconType?: string;
role?: string; // Defaults to the log role. The alert role can be considered only if all toasts in this list will require immediate user attention.
text?: string; // Additional text below the message/title, same as EuiToast['text']
toastLifeTimeMs?: number; // Allows customizing per-toast timeout
}

0 comments on commit 8e78b8e

Please sign in to comment.