diff --git a/semcore/d3-chart/__tests__/bar-chart.browser-test.tsx b/semcore/d3-chart/__tests__/bar-chart.browser-test.tsx index 4aad3f1e96..a7a68b351f 100644 --- a/semcore/d3-chart/__tests__/bar-chart.browser-test.tsx +++ b/semcore/d3-chart/__tests__/bar-chart.browser-test.tsx @@ -434,7 +434,6 @@ test.describe(`${TAG.VISUAL}`, () => { // Disable all items and verify chart appearance await page.keyboard.press('Tab'); await page.keyboard.press('Tab'); - await page.keyboard.press('Space'); const box = await chart.boundingBox(); if (!box) throw new Error('Bounding box not found'); diff --git a/semcore/d3-chart/__tests__/bar-horizontal-compact.browser-test.tsx b/semcore/d3-chart/__tests__/bar-horizontal-compact.browser-test.tsx index 34b6783b20..a36be3a3f5 100644 --- a/semcore/d3-chart/__tests__/bar-horizontal-compact.browser-test.tsx +++ b/semcore/d3-chart/__tests__/bar-horizontal-compact.browser-test.tsx @@ -126,7 +126,6 @@ test.describe(`${TAG.VISUAL}`, () => { // Navigate with keyboard to verify focus state await page.keyboard.press('Tab'); await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); await expect(page).toHaveScreenshot(); }); }); diff --git a/semcore/d3-chart/__tests__/d3-chart-base.browser-test.tsx b/semcore/d3-chart/__tests__/d3-chart-base.browser-test.tsx index 2a3446c096..7a2bb3015f 100644 --- a/semcore/d3-chart/__tests__/d3-chart-base.browser-test.tsx +++ b/semcore/d3-chart/__tests__/d3-chart-base.browser-test.tsx @@ -284,7 +284,6 @@ test.describe(`${TAG.FUNCTIONAL}`, () => { await test.step('Verify plot svg with aria-label attributes', async () => { const svg = svgs.first(); const svgAttributes = [ - ['tabindex', '0'], ['aria-label', 'Last market trends with pattern'], ['width', '300'], ['height', '200'], @@ -307,6 +306,7 @@ test.describe(`${TAG.FUNCTIONAL}`, () => { await expect(dialog).not.toBeVisible(); await page.keyboard.press('Tab'); + await page.keyboard.press('Enter'); await expect(dialog).toBeVisible(); await expect(dialog).toHaveAttribute('tabindex', '0'); @@ -349,7 +349,6 @@ test.describe(`${TAG.FUNCTIONAL}`, () => { await test.step('Verify plot svg without aria-label attributes', async () => { const svg = svgs.nth(1); const svgAttributes = [ - ['tabindex', '0'], ['aria-label', 'Chart'], ['data-ui-name', 'Plot'], ]; diff --git a/semcore/d3-chart/__tests__/index.test.tsx b/semcore/d3-chart/__tests__/index.test.tsx index 56f8530bd1..abde8d5961 100644 --- a/semcore/d3-chart/__tests__/index.test.tsx +++ b/semcore/d3-chart/__tests__/index.test.tsx @@ -299,6 +299,7 @@ describe('Focus skip to content after plot', () => { const hints = makeDataHintsContainer(); const PlotComponent: React.FC = () => { + const triggerRef = React.useRef(null); const plotRef = React.useRef(null); return ( @@ -312,6 +313,8 @@ describe('Focus skip to content after plot', () => { locale='en' config={{}} hints={hints} + triggerRef={triggerRef} + onCloseHandler={() => {}} />
@@ -331,7 +334,6 @@ describe('Focus skip to content after plot', () => { const { getByTestId } = render(); - await userEvent.keyboard('[Tab]'); await userEvent.keyboard('[Tab]'); await userEvent.keyboard('[Tab]'); await userEvent.keyboard('[Enter]'); @@ -349,6 +351,7 @@ describe('Focus skip to content after plot', () => { const hints = makeDataHintsContainer(); const PlotComponent: React.FC = () => { + const triggerRef = React.useRef(null); const plotRef = React.useRef(null); return ( @@ -362,6 +365,8 @@ describe('Focus skip to content after plot', () => { locale='en' config={{}} hints={hints} + triggerRef={triggerRef} + onCloseHandler={() => {}} />
@@ -385,7 +390,6 @@ describe('Focus skip to content after plot', () => { const { getByTestId } = render(); - await userEvent.keyboard('[Tab]'); await userEvent.keyboard('[Tab]'); await userEvent.keyboard('[Tab]'); await userEvent.keyboard('[Enter]'); diff --git a/semcore/d3-chart/src/Plot.jsx b/semcore/d3-chart/src/Plot.jsx index bef6a18095..3a8515b42f 100644 --- a/semcore/d3-chart/src/Plot.jsx +++ b/semcore/d3-chart/src/Plot.jsx @@ -103,7 +103,6 @@ class PlotRoot extends Component { onMouseMove={this.handlerMouseMove} onMouseLeave={this.handlerMouseLeave} aria-label={ariaLabel} - tabIndex={0} data-plot-id={this.plotId} > diff --git a/semcore/d3-chart/src/a11y/PlotA11yModule.tsx b/semcore/d3-chart/src/a11y/PlotA11yModule.tsx index 55383a8315..59fc99864e 100644 --- a/semcore/d3-chart/src/a11y/PlotA11yModule.tsx +++ b/semcore/d3-chart/src/a11y/PlotA11yModule.tsx @@ -3,37 +3,20 @@ import { Root, sstyled } from '@semcore/core'; import { Context as I18nContext, useI18n } from '@semcore/core/lib/utils/enhances/WithI18n'; import React from 'react'; -import type { DataStructureHints, PartialDataSummarizationConfig } from './hints'; import { normalizeLocale } from './locale'; +import type { A11yViewProps } from './PlotA11yView'; import styles from '../style/plotA11yModule.shadow.css'; import { localizedMessages } from './translations/module/__intergalactic-dynamic-locales'; -let globalWasFocused = false; -let globalNavWithKeyboard = false; +type A11yModuleProps = Omit; -export type A11yViewProps = { - id: string; - data: Record[]; - hints: DataStructureHints; - plotLabel: string; - locale: NavigatorLanguage['language']; - config: PartialDataSummarizationConfig; - - plotRef: React.RefObject; -}; - -export function PlotA11yModule(props: A11yViewProps) { +export function PlotA11yModule(props: A11yModuleProps) { const SPlotA11yModule = Root; - const [wasFocused, setWasFocused] = React.useState(globalWasFocused); - const [navWithKeyboard, setNavWithKeyboard] = React.useState(globalNavWithKeyboard); + const [isOpened, setIsOpened] = React.useState(false); const [plotA11yView, setPlotA11yView] = React.useState<{ Component: React.FC; } | null>(null); - - const hadnleHiddenElementsFocus = React.useCallback(() => { - setWasFocused(true); - setNavWithKeyboard(true); - }, []); + const srButtonRef = React.useRef(null); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(null); @@ -46,40 +29,7 @@ export function PlotA11yModule(props: A11yViewProps) { const t = useI18n(localizedMessages, locale!); React.useEffect(() => { - if (wasFocused) return; - const focusListener = () => { - globalWasFocused = true; - setWasFocused(true); - }; - - props.plotRef.current?.addEventListener('focus', focusListener); - return () => props.plotRef.current?.removeEventListener('focus', focusListener); - }, [wasFocused, props.plotRef]); - React.useEffect(() => { - if (navWithKeyboard) return; - const keyboardListener = (event: Event) => { - const navigationKeys = [ - 'Tab', - 'ArrowUp', - 'ArrowLeft', - 'ArrowDown', - 'ArrowRight', - 'ArrowUp', - 'ArrowLeft', - ]; - if ('key' in event && navigationKeys.includes((event as KeyboardEvent).key)) { - setNavWithKeyboard(true); - globalNavWithKeyboard = true; - } - }; - document.body?.addEventListener('keydown', keyboardListener); - return () => document.body?.removeEventListener('keydown', keyboardListener); - }, [navWithKeyboard]); - - const shouldDisplayView = wasFocused && navWithKeyboard; - - React.useEffect(() => { - if (!shouldDisplayView) return; + if (!isOpened) return; if (plotA11yView) return; if (loading) return; @@ -95,33 +45,35 @@ export function PlotA11yModule(props: A11yViewProps) { // eslint-disable-next-line no-console console.error(error); setError(error); + setLoading(false); }); - }, [plotA11yView, shouldDisplayView, loading, setLoading]); + }, [plotA11yView, isOpened, loading, setLoading]); if (plotA11yView) { return sstyled(styles)( - , - ) as React.ReactElement; - } - - if (error) { - return sstyled(styles)( - - {t('failed')} - , - ) as React.ReactElement; - } - if (loading) { - return sstyled(styles)( - - {t('loading')} - , + { + setIsOpened(false); + setPlotA11yView(null); + }} + triggerRef={srButtonRef} + locale={locale!} + />, ) as React.ReactElement; } return sstyled(styles)( - - {t('disabled')} + +