From 438a4834351a05ea6ef0731ca1602f5575a1a6be Mon Sep 17 00:00:00 2001 From: Anna Scheuler Kammeyer <33579665+scheul93@users.noreply.github.com> Date: Mon, 6 Jun 2022 11:01:30 -0600 Subject: [PATCH] [WNMGDS-1712] icon props bugfix (#1881) * updating SvgIcon to take other props * icno test updates --- .../src/components/Icons/StarIcon.tsx | 11 +- .../src/components/Icons/SvgIcon.test.tsx | 63 ++++++ .../src/components/Icons/SvgIcon.tsx | 10 +- .../Icons/__snapshots__/SvgIcon.test.tsx.snap | 50 +++++ .../src/components/Icons/icons.stories.tsx | 201 ++++++++++++++++++ .../__snapshots__/Pagination.test.tsx.snap | 4 + .../__snapshots__/UsaBanner.test.jsx.snap | 1 + 7 files changed, 331 insertions(+), 9 deletions(-) create mode 100644 packages/design-system/src/components/Icons/SvgIcon.test.tsx create mode 100644 packages/design-system/src/components/Icons/__snapshots__/SvgIcon.test.tsx.snap create mode 100644 packages/design-system/src/components/Icons/icons.stories.tsx diff --git a/packages/design-system/src/components/Icons/StarIcon.tsx b/packages/design-system/src/components/Icons/StarIcon.tsx index 0d79b35184..5c2cab05b4 100644 --- a/packages/design-system/src/components/Icons/StarIcon.tsx +++ b/packages/design-system/src/components/Icons/StarIcon.tsx @@ -8,24 +8,25 @@ export interface StarIconProps extends IconCommonProps { const defaultProps = { className: '', - isFilled: false, viewBox: '0 0 18 16', }; function StarIcon(props: StarIconProps): React.ReactElement { + // don't want to pass isFilled through to SvgIcon + const { isFilled, ...otherProps } = props; const iconCssClasses = classNames( 'ds-c-icon--star', { - 'ds-c-icon--star-filled': props.isFilled, + 'ds-c-icon--star-filled': isFilled, }, props.className ); - const title = props.isFilled ? t('icons.starFilled') : t('icons.star'); + const title = isFilled ? t('icons.starFilled') : t('icons.star'); return ( - - {props.isFilled ? ( + + {isFilled ? ( { + const renderSvgIcon = (overrideProps?) => { + return render( + + + + ); + }; + + it('passes through additional props', () => { + const { getByTestId } = renderSvgIcon({ 'data-testid': 'iconTest' }); + const iconEl = getByTestId('iconTest'); + expect(iconEl).toMatchSnapshot(); + }); + + it('wrapper icon can pass through additional props', () => { + const { getByTestId } = render(); + const iconEl = getByTestId('addIconTest'); + expect(iconEl).toMatchSnapshot(); + }); + + describe('when ariaHidden is false', () => { + it('shows accessibility attributes', () => { + const { getByRole } = renderSvgIcon(); + const iconEl = getByRole('img'); + + expect(iconEl).toBeDefined(); + expect(iconEl.getAttribute('aria-labelledby')).toEqual('test-icon__title'); + }); + + it('renders title', () => { + const { getByTitle } = renderSvgIcon(); + const iconEl = getByTitle('test icon'); + expect(iconEl).toBeDefined(); + }); + + it('updates aria-labelledby if description exists', () => { + const { getByRole } = renderSvgIcon({ description: 'i am a description of the svg' }); + const iconEl = getByRole('img'); + + expect(iconEl.getAttribute('aria-labelledby')).toEqual('test-icon__title test-icon__desc'); + }); + }); + + describe('when ariaHidden is true', () => { + it('does not have role="img"', () => { + const { queryByRole } = renderSvgIcon({ ariaHidden: true }); + const iconEl = queryByRole('img'); + expect(iconEl).toBe(null); + }); + + it('renders without title or description"', () => { + const { getByTestId } = renderSvgIcon({ ariaHidden: true, 'data-testid': 'testId' }); + const iconEl = getByTestId('testId'); + expect(iconEl).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/design-system/src/components/Icons/SvgIcon.tsx b/packages/design-system/src/components/Icons/SvgIcon.tsx index 3faee2f95d..66958fa2c2 100644 --- a/packages/design-system/src/components/Icons/SvgIcon.tsx +++ b/packages/design-system/src/components/Icons/SvgIcon.tsx @@ -55,6 +55,7 @@ function SvgIcon({ inversed, title, viewBox, + ...otherProps }: Omit, OmitProps> & SvgIconProps): React.ReactElement { const svgClasses = classNames('ds-c-icon', { 'ds-c-icon--inverse': inversed }, className); @@ -63,10 +64,10 @@ function SvgIcon({ const descriptionId = `${iconId}__desc`; const ariaLabelledBy = description ? `${titleId} ${descriptionId}` : titleId; const isSrVisible = !ariaHidden; - const additionalProps = {}; + const screenReaderProps = {}; if (isSrVisible) { - additionalProps['aria-labelledby'] = ariaLabelledBy; - additionalProps['role'] = 'img'; + screenReaderProps['aria-labelledby'] = ariaLabelledBy; + screenReaderProps['role'] = 'img'; } return ( @@ -77,7 +78,8 @@ function SvgIcon({ id={iconId} viewBox={viewBox} xmlns="http://www.w3.org/2000/svg" - {...additionalProps} + {...screenReaderProps} + {...otherProps} > {isSrVisible && {title}} {isSrVisible && description && {description}} diff --git a/packages/design-system/src/components/Icons/__snapshots__/SvgIcon.test.tsx.snap b/packages/design-system/src/components/Icons/__snapshots__/SvgIcon.test.tsx.snap new file mode 100644 index 0000000000..1676f53aff --- /dev/null +++ b/packages/design-system/src/components/Icons/__snapshots__/SvgIcon.test.tsx.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SvgIcon passes through additional props 1`] = ` + + + test icon + + + +`; + +exports[`SvgIcon when ariaHidden is true renders without title or description" 1`] = ` + +`; + +exports[`SvgIcon wrapper icon can pass through additional props 1`] = ` + +`; diff --git a/packages/design-system/src/components/Icons/icons.stories.tsx b/packages/design-system/src/components/Icons/icons.stories.tsx new file mode 100644 index 0000000000..d0efa475a7 --- /dev/null +++ b/packages/design-system/src/components/Icons/icons.stories.tsx @@ -0,0 +1,201 @@ +import React from 'react'; +import { + AddIcon, + AlertCircleIcon, + ArrowsStackedIcon, + ArrowIcon, + BuildingCircleIcon, + CheckCircleIcon, + CheckIcon, + CloseIcon, + CloseIconThin, + DownloadIcon, + ExternalLinkIcon, + ImageIcon, + InfoCircleIcon, + InfoCircleIconThin, + LockCircleIcon, + LockIcon, + MenuIcon, + MenuIconThin, + NextIcon, + PdfIcon, + RemoveIcon, + StarIcon, + UsaFlagIcon, + WarningIcon, +} from './index'; + +import SvgIcon from './SvgIcon'; + +export default { + title: 'Components/Icons', + component: SvgIcon, +}; + +const iconData = [ + { + defaultTitle: 'Add', + component: , + name: 'AddIcon', + }, + { + defaultTitle: 'Alert', + component: , + name: 'AlertCircleIcon', + }, + { + defaultTitle: 'Sort', + component: , + name: 'ArrowsStackedIcon', + }, + { + defaultTitle: '[direction of arrow]', + component: ( + <> + + + + + + ), + name: 'ArrowIcon', + notes: + 'Component takes direction prop to determine if it is up, down, left or right.', + }, + { + defaultTitle: 'Building in circle', + component: , + name: 'BuildingCircleIcon', + }, + { + defaultTitle: 'Check mark in circle', + component: , + name: 'CheckCircleIcon', + }, + { + defaultTitle: 'Check mark', + component: , + name: 'CheckIcon', + }, + { + defaultTitle: 'Close', + component: , + name: 'CloseIcon', + }, + { + defaultTitle: 'Close', + component: , + name: 'CloseIconThin', + }, + { + defaultTitle: 'Download', + component: , + name: 'DownloadIcon', + }, + { + defaultTitle: 'External Link', + component: , + name: 'ExternalLinkIcon', + }, + { + defaultTitle: 'Image', + component: , + name: 'ImageIcon', + }, + { + defaultTitle: 'Information', + component: , + name: 'InfoCircleIcon', + }, + { + defaultTitle: 'Information', + component: , + name: 'InfoCircleIconThin', + }, + { + defaultTitle: 'Lock in circle', + component: , + name: 'LockCircleIcon', + }, + { + defaultTitle: 'Lock', + component: , + name: 'LockIcon', + }, + { + defaultTitle: 'Menu Icon', + component: , + name: 'MenuIcon', + }, + { + defaultTitle: 'Menu', + component: , + name: 'MenuIconThin', + }, + { + defaultTitle: 'Next', + component: , + name: 'NextIcon', + }, + { + defaultTitle: 'Pdf', + component: , + name: 'PdfIcon', + }, + { + defaultTitle: 'Remove', + component: , + name: 'RemoveIcon', + }, + { + defaultTitle: 'Star / Star Filled', + component: ( + <> + + + + ), + name: 'StarIcon', + notes: 'Component takes isFilled prop to determine star is filled or an outline.', + }, + { + defaultTitle: 'U.S. flag', + component: , + name: 'UsaFlagIcon', + }, + { + defaultTitle: 'Warning', + component: , + name: 'WarningIcon', + }, +]; + +export const AvailableIcons = () => ( + <> + + + + + + + + + + + {iconData.map(({ defaultTitle, component, name, notes }) => ( + + + + + + ))} + +
Icon ComponentExample + Default title attribute + Notes
+ {name} + {component}{defaultTitle} +
+ +); diff --git a/packages/design-system/src/components/Pagination/__snapshots__/Pagination.test.tsx.snap b/packages/design-system/src/components/Pagination/__snapshots__/Pagination.test.tsx.snap index 18fd04c279..7bb473d1b6 100644 --- a/packages/design-system/src/components/Pagination/__snapshots__/Pagination.test.tsx.snap +++ b/packages/design-system/src/components/Pagination/__snapshots__/Pagination.test.tsx.snap @@ -66,6 +66,7 @@ exports[`Pagination should render component 1`] = `