Skip to content

Commit

Permalink
[WNMGDS-1712] icon props bugfix (#1881)
Browse files Browse the repository at this point in the history
* updating SvgIcon to take other props

* icno test updates
  • Loading branch information
scheul93 authored Jun 6, 2022
1 parent 8117503 commit 438a483
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 9 deletions.
11 changes: 6 additions & 5 deletions packages/design-system/src/components/Icons/StarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<SvgIcon title={title} {...defaultProps} {...props} className={iconCssClasses}>
{props.isFilled ? (
<SvgIcon title={title} {...defaultProps} {...otherProps} className={iconCssClasses}>
{isFilled ? (
<path
d="M8.533 13.063l-5.274 2.69 1.008-5.699L0 6.017l5.896-.831L8.533 0l2.637 5.186 5.897.831-4.267 4.037 1.007 5.7z"
fillRule="nonzero"
Expand Down
63 changes: 63 additions & 0 deletions packages/design-system/src/components/Icons/SvgIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import { render } from '@testing-library/react';
import SvgIcon from './SvgIcon';
import AddIcon from './AddIcon';

describe('SvgIcon', () => {
const renderSvgIcon = (overrideProps?) => {
return render(
<SvgIcon ariaHidden={false} title="test icon" id="test-icon" {...overrideProps}>
<path />
</SvgIcon>
);
};

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(<AddIcon data-testid="addIconTest" />);
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();
});
});
});
10 changes: 6 additions & 4 deletions packages/design-system/src/components/Icons/SvgIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function SvgIcon({
inversed,
title,
viewBox,
...otherProps
}: Omit<React.SVGProps<SVGSVGElement>, OmitProps> & SvgIconProps): React.ReactElement {
const svgClasses = classNames('ds-c-icon', { 'ds-c-icon--inverse': inversed }, className);

Expand All @@ -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 (
Expand All @@ -77,7 +78,8 @@ function SvgIcon({
id={iconId}
viewBox={viewBox}
xmlns="http://www.w3.org/2000/svg"
{...additionalProps}
{...screenReaderProps}
{...otherProps}
>
{isSrVisible && <title id={titleId}>{title}</title>}
{isSrVisible && description && <desc id={descriptionId}>{description}</desc>}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`SvgIcon passes through additional props 1`] = `
<svg
aria-hidden="false"
aria-labelledby="test-icon__title"
class="ds-c-icon"
data-testid="iconTest"
focusable="false"
id="test-icon"
role="img"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="test-icon__title"
>
test icon
</title>
<path />
</svg>
`;

exports[`SvgIcon when ariaHidden is true renders without title or description" 1`] = `
<svg
aria-hidden="true"
class="ds-c-icon"
data-testid="testId"
focusable="false"
id="test-icon"
xmlns="http://www.w3.org/2000/svg"
>
<path />
</svg>
`;

exports[`SvgIcon wrapper icon can pass through additional props 1`] = `
<svg
aria-hidden="true"
class="ds-c-icon ds-c-icon--add "
data-testid="addIconTest"
focusable="false"
id="icon-1"
viewBox="3 3 18 18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
/>
</svg>
`;
201 changes: 201 additions & 0 deletions packages/design-system/src/components/Icons/icons.stories.tsx
Original file line number Diff line number Diff line change
@@ -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: <AddIcon />,
name: 'AddIcon',
},
{
defaultTitle: 'Alert',
component: <AlertCircleIcon />,
name: 'AlertCircleIcon',
},
{
defaultTitle: 'Sort',
component: <ArrowsStackedIcon />,
name: 'ArrowsStackedIcon',
},
{
defaultTitle: '[direction of arrow]',
component: (
<>
<ArrowIcon direction="up" />
<ArrowIcon direction="down" />
<ArrowIcon direction="left" />
<ArrowIcon direction="right" />
</>
),
name: 'ArrowIcon',
notes:
'Component takes <code>direction</code> prop to determine if it is up, down, left or right.',
},
{
defaultTitle: 'Building in circle',
component: <BuildingCircleIcon />,
name: 'BuildingCircleIcon',
},
{
defaultTitle: 'Check mark in circle',
component: <CheckCircleIcon />,
name: 'CheckCircleIcon',
},
{
defaultTitle: 'Check mark',
component: <CheckIcon />,
name: 'CheckIcon',
},
{
defaultTitle: 'Close',
component: <CloseIcon />,
name: 'CloseIcon',
},
{
defaultTitle: 'Close',
component: <CloseIconThin />,
name: 'CloseIconThin',
},
{
defaultTitle: 'Download',
component: <DownloadIcon />,
name: 'DownloadIcon',
},
{
defaultTitle: 'External Link',
component: <ExternalLinkIcon />,
name: 'ExternalLinkIcon',
},
{
defaultTitle: 'Image',
component: <ImageIcon />,
name: 'ImageIcon',
},
{
defaultTitle: 'Information',
component: <InfoCircleIcon />,
name: 'InfoCircleIcon',
},
{
defaultTitle: 'Information',
component: <InfoCircleIconThin />,
name: 'InfoCircleIconThin',
},
{
defaultTitle: 'Lock in circle',
component: <LockCircleIcon />,
name: 'LockCircleIcon',
},
{
defaultTitle: 'Lock',
component: <LockIcon />,
name: 'LockIcon',
},
{
defaultTitle: 'Menu Icon',
component: <MenuIcon />,
name: 'MenuIcon',
},
{
defaultTitle: 'Menu',
component: <MenuIconThin />,
name: 'MenuIconThin',
},
{
defaultTitle: 'Next',
component: <NextIcon />,
name: 'NextIcon',
},
{
defaultTitle: 'Pdf',
component: <PdfIcon />,
name: 'PdfIcon',
},
{
defaultTitle: 'Remove',
component: <RemoveIcon />,
name: 'RemoveIcon',
},
{
defaultTitle: 'Star / Star Filled',
component: (
<>
<StarIcon />
<StarIcon isFilled />
</>
),
name: 'StarIcon',
notes: 'Component takes <code>isFilled</code> prop to determine star is filled or an outline.',
},
{
defaultTitle: 'U.S. flag',
component: <UsaFlagIcon />,
name: 'UsaFlagIcon',
},
{
defaultTitle: 'Warning',
component: <WarningIcon />,
name: 'WarningIcon',
},
];

export const AvailableIcons = () => (
<>
<table className="ds-c-table">
<thead>
<tr>
<th>Icon Component</th>
<th>Example</th>
<th>
Default <code>title</code> attribute
</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{iconData.map(({ defaultTitle, component, name, notes }) => (
<tr key={name}>
<td>
<code>{name}</code>
</td>
<td className="ds-u-text-align--center">{component}</td>
<td>{defaultTitle}</td>
<td dangerouslySetInnerHTML={{ __html: notes }} />
</tr>
))}
</tbody>
</table>
</>
);
Loading

0 comments on commit 438a483

Please sign in to comment.