From 1b742e77f41c6b0674ee01c28b17171508cb5784 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 22 Nov 2024 21:38:24 +0200 Subject: [PATCH] fix: [Search:Search Applications page]Popover for Create button is inaccessible via keyboard (#201193) Closes: #199760 Popovers, dialogs which are accessible with mouse, should also be accessible with keyboard. Otherwise users using only keyboard will miss the information present in popover, dialog. Closes: #199749 User reaches the same button two times when navigating using only keyboard and it can get confusing. Better for element to get focus only one time when navigating in sequence from one element to another and for the user only to hear one announcement of the element. ## What was changed: 1. `CreateSearchApplicationButton` component: - `EuiPopover` was replaced to more a11y-friendly `EuiToolTip` - extra `div` element with `tabindex` was removed. ## Screen https://github.com/user-attachments/assets/fbb62841-6f2f-45d0-bee3-39a11a4fc777 --- ...search_applications_create_button.test.tsx | 41 ++++++++++ .../search_applications_list.test.tsx | 79 +----------------- .../search_applications_list.tsx | 80 ++++++++----------- 3 files changed, 74 insertions(+), 126 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_create_button.test.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_create_button.test.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_create_button.test.tsx new file mode 100644 index 0000000000000..548314dcda43b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_create_button.test.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { type ReactNode } from 'react'; + +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { waitForEuiToolTipVisible } from '@elastic/eui/lib/test/rtl'; + +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +import { CreateSearchApplicationButton } from './search_applications_list'; + +function Container({ children }: { children?: ReactNode }) { + return {children}; +} + +describe('CreateSearchApplicationButton', () => { + test('disabled={false}', async () => { + render( + + + + ); + + await userEvent.hover( + await screen.findByTestId('enterprise-search-search-applications-creation-button') + ); + + await waitForEuiToolTipVisible(); + + expect( + await screen.findByTestId('create-search-application-button-popover-content') + ).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.test.tsx index f34aac9e4359c..2683a0afc0caa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.test.tsx @@ -9,7 +9,7 @@ import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; import React from 'react'; -import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { Status } from '../../../../../common/types/api'; @@ -116,80 +116,3 @@ describe('SearchApplicationsList', () => { expect(wrapper.find(LicensingCallout)).toHaveLength(0); }); }); - -describe('CreateSearchApplicationButton', () => { - describe('disabled={true}', () => { - it('renders a disabled button that shows a popover when hovered', () => { - const wrapper = mountWithIntl(); - - const button = wrapper.find( - 'button[data-test-subj="enterprise-search-search-applications-creation-button"]' - ); - - expect(button).toHaveLength(1); - expect(button.prop('disabled')).toBeTruthy(); - - let popover = wrapper.find( - 'div[data-test-subj="create-search-application-button-popover-content"]' - ); - - expect(popover).toHaveLength(0); - - const hoverTarget = wrapper.find( - 'div[data-test-subj="create-search-application-button-hover-target"]' - ); - - expect(hoverTarget).toHaveLength(1); - - hoverTarget.simulate('mouseEnter'); - - wrapper.update(); - - popover = wrapper.find( - 'div[data-test-subj="create-search-application-button-popover-content"]' - ); - - expect(popover).toHaveLength(1); - expect(popover.text()).toMatch( - 'This functionality may be changed or removed completely in a future release.' - ); - }); - }); - describe('disabled={false}', () => { - it('renders a button and shows a popover when hovered', () => { - const wrapper = mountWithIntl(); - - const button = wrapper.find( - 'button[data-test-subj="enterprise-search-search-applications-creation-button"]' - ); - - expect(button).toHaveLength(1); - expect(button.prop('disabled')).toBeFalsy(); - - let popover = wrapper.find( - 'div[data-test-subj="create-search-application-button-popover-content"]' - ); - - expect(popover).toHaveLength(0); - - const hoverTarget = wrapper.find( - 'div[data-test-subj="create-search-application-button-hover-target"]' - ); - - expect(hoverTarget).toHaveLength(1); - - hoverTarget.simulate('mouseEnter'); - - wrapper.update(); - - popover = wrapper.find( - 'div[data-test-subj="create-search-application-button-popover-content"]' - ); - - expect(popover).toHaveLength(1); - expect(popover.text()).toMatch( - 'This functionality may be changed or removed completely in a future release.' - ); - }); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.tsx index 126c44b6b5dca..fbc07eef5c5e1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_applications/search_applications_list.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; import useThrottle from 'react-use/lib/useThrottle'; @@ -17,10 +17,9 @@ import { EuiFlexItem, EuiIcon, EuiLink, - EuiPopover, - EuiPopoverTitle, EuiSpacer, EuiText, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -53,38 +52,10 @@ interface CreateSearchApplicationButtonProps { export const CreateSearchApplicationButton: React.FC = ({ disabled, }) => { - const [showPopover, setShowPopover] = useState(false); - return ( - setShowPopover(false)} - button={ -
setShowPopover(true)} - onMouseLeave={() => setShowPopover(false)} - tabIndex={0} - > - KibanaLogic.values.navigateToUrl(SEARCH_APPLICATION_CREATION_PATH)} - > - {i18n.translate( - 'xpack.enterpriseSearch.searchApplications.list.createSearchApplicationButton.label', - { - defaultMessage: 'Create', - } - )} - -
- } - > - + @@ -96,23 +67,35 @@ export const CreateSearchApplicationButton: React.FC - -
+ + + } + > + KibanaLogic.values.navigateToUrl(SEARCH_APPLICATION_CREATION_PATH)} > - - - - - -
-
+ {i18n.translate( + 'xpack.enterpriseSearch.searchApplications.list.createSearchApplicationButton.label', + { + defaultMessage: 'Create', + } + )} + + ); }; + interface ListProps { createSearchApplicationFlyoutOpen?: boolean; } @@ -223,6 +206,7 @@ export const SearchApplicationsList: React.FC = ({ <>