From f4d4ebdab4d16f852b1fccaa2f19a2cb67df81d1 Mon Sep 17 00:00:00 2001 From: Alberto Carreras <10593890+AlbertCarreras@users.noreply.github.com> Date: Wed, 29 May 2024 17:08:47 -0400 Subject: [PATCH] Link, Dropdown: fix A11y (opens new tab logic) (#3600) --- packages/gestalt/src/Dropdown/OptionItem.tsx | 5 +- .../gestalt/src/DropdownLink.jsdom.test.tsx | 4 +- .../gestalt/src/HelpButton.jsdom.test.tsx | 2 +- packages/gestalt/src/Link.jsdom.test.tsx | 4 +- packages/gestalt/src/Link.tsx | 36 +++++------- .../Dropdown.jsdom.test.tsx.snap | 57 +++++-------------- .../src/__snapshots__/Link.test.tsx.snap | 21 ++----- .../accessibility/AccessibilityOpenNewTab.tsx | 23 ++++++++ 8 files changed, 64 insertions(+), 88 deletions(-) create mode 100644 packages/gestalt/src/accessibility/AccessibilityOpenNewTab.tsx diff --git a/packages/gestalt/src/Dropdown/OptionItem.tsx b/packages/gestalt/src/Dropdown/OptionItem.tsx index 55ca30da7a..9bb34eb01a 100644 --- a/packages/gestalt/src/Dropdown/OptionItem.tsx +++ b/packages/gestalt/src/Dropdown/OptionItem.tsx @@ -1,5 +1,6 @@ import { forwardRef, Fragment, ReactNode } from 'react'; import classnames from 'classnames'; +import AccessibilityOpenNewTab from '../accessibility/AccessibilityOpenNewTab'; import { useRequestAnimationFrame } from '../animation/RequestAnimationFrameContext'; import Badge from '../Badge'; import Box from '../Box'; @@ -155,16 +156,14 @@ const OptionItemWithForwardRef = forwardRef {isExternal && ( - + )} diff --git a/packages/gestalt/src/DropdownLink.jsdom.test.tsx b/packages/gestalt/src/DropdownLink.jsdom.test.tsx index 7fd4101e78..7544e7d77f 100644 --- a/packages/gestalt/src/DropdownLink.jsdom.test.tsx +++ b/packages/gestalt/src/DropdownLink.jsdom.test.tsx @@ -57,9 +57,9 @@ describe('Dropdown.Link', () => { // eslint-disable-next-line testing-library/prefer-presence-queries -- Please fix the next time this file is touched! expect(screen.queryByText('Beta Badge')).toBeInTheDocument(); expect( - screen.queryByText('; Opens a new tab', { + screen.queryByTitle(', Opens a new tab', { exact: true, }), - ).toBeVisible(); + ).not.toBeVisible(); }); }); diff --git a/packages/gestalt/src/HelpButton.jsdom.test.tsx b/packages/gestalt/src/HelpButton.jsdom.test.tsx index b48c87aa43..cb464fb337 100644 --- a/packages/gestalt/src/HelpButton.jsdom.test.tsx +++ b/packages/gestalt/src/HelpButton.jsdom.test.tsx @@ -156,7 +156,7 @@ describe('HelpButton', () => { const element = screen.getByRole('link'); // @ts-expect-error - TS2339 - Property 'text' does not exist on type 'HTMLElement'. - expect(element.text).toEqual('New link text; Opens a new tab'); + expect(element.text).toEqual('New link text'); }); it('renders a link spying the link trigger', () => { diff --git a/packages/gestalt/src/Link.jsdom.test.tsx b/packages/gestalt/src/Link.jsdom.test.tsx index 8e6da748bc..78a8c0605b 100644 --- a/packages/gestalt/src/Link.jsdom.test.tsx +++ b/packages/gestalt/src/Link.jsdom.test.tsx @@ -40,10 +40,10 @@ describe('Link', () => { ).toBeVisible(); expect( - screen.getByText('; Opens a new tab', { + screen.getByTitle(', Opens a new tab', { exact: true, }), - ).toBeVisible(); + ).not.toBeVisible(); render( ['size']; }; -function ExternalIcon({ externalLinkIcon }: { externalLinkIcon: ExternalLinkIcon }) { - return externalLinkIcon === 'none' ? null : ( - - - - ); -} - type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 'circle' | 'pill'; type Props = { @@ -289,8 +271,20 @@ const LinkWithForwardRef = forwardRef(function Link( target={target ? `_${target}` : null} > {children} - - + {externalLinkIcon === 'none' ? null : ( + + + + )} ); }); diff --git a/packages/gestalt/src/__snapshots__/Dropdown.jsdom.test.tsx.snap b/packages/gestalt/src/__snapshots__/Dropdown.jsdom.test.tsx.snap index 5bba5eca01..7e5acba5a0 100644 --- a/packages/gestalt/src/__snapshots__/Dropdown.jsdom.test.tsx.snap +++ b/packages/gestalt/src/__snapshots__/Dropdown.jsdom.test.tsx.snap @@ -719,34 +719,25 @@ exports[`Dropdown renders a menu of 6 items 1`] = ` /> -
-
- ; Opens a new tab -
-
@@ -892,34 +883,25 @@ exports[`Dropdown renders a menu of 6 items 1`] = ` /> -
-
- ; Opens a new tab -
-
@@ -969,34 +951,25 @@ exports[`Dropdown renders a menu of 6 items 1`] = ` /> -
-
- ; Opens a new tab -
-
diff --git a/packages/gestalt/src/__snapshots__/Link.test.tsx.snap b/packages/gestalt/src/__snapshots__/Link.test.tsx.snap index 6fdeb5f98a..f08c6655a8 100644 --- a/packages/gestalt/src/__snapshots__/Link.test.tsx.snap +++ b/packages/gestalt/src/__snapshots__/Link.test.tsx.snap @@ -40,18 +40,19 @@ exports[`external link with nofollow 1`] = ` > Link
+ + , + Opens a new tab + @@ -162,20 +163,6 @@ exports[`target blank 1`] = ` target="_blank" > Link -
-
- ; Opens a new tab -
-
`; diff --git a/packages/gestalt/src/accessibility/AccessibilityOpenNewTab.tsx b/packages/gestalt/src/accessibility/AccessibilityOpenNewTab.tsx new file mode 100644 index 0000000000..7da6ec2a04 --- /dev/null +++ b/packages/gestalt/src/accessibility/AccessibilityOpenNewTab.tsx @@ -0,0 +1,23 @@ +import { ComponentProps } from 'react'; +import classnames from 'classnames'; +import { useDefaultLabelContext } from '../contexts/DefaultLabelProvider'; +import Icon from '../Icon'; +import styles from '../Icon.css'; +import icons from '../icons/index'; + +type Props = { + color: ComponentProps['color']; + size: ComponentProps['size']; +}; + +export default function AccessibilityOpenNewTab({ size, color }: Props) { + const cs = classnames(styles.rtlSupport, styles[color ?? 'default'], styles.icon); + const { accessibilityNewTabLabel } = useDefaultLabelContext('Link'); + + return ( + + , {accessibilityNewTabLabel} + + + ); +}