I have the following component:
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
// Import all icon names
import { ICONS } from 'assets/icons';
// Styles
import css from './index.module.css';
// Empty SVG to fall back to until targeted icon is loaded
const FALLBACK = () => <svg aria-label="" data-testid="icon-fallback" viewBox="0 0 20 20" />;
// Default acceptable icon weights
const WEIGHTS = ['light', 'heavy'];
/**
* SVG Icons
*
* @param {string} ariaLabel aria-label for the icon. Used primarily when the icon is not accompanied by descriptive text.
* @param {bool} ariaHidden Should we hide the icon from assistive devices? Used when icon is not accompanied by descriptive text.
* @param {string} name Name of the icon to load. Must match a name in the ICONS array above.
* @param {weight} weight Icon weight. Can be light or heavy.
* @returns {Node}
*/
const Icon = ({
ariaLabel,
ariaHidden,
name,
weight,
}) => {
// When !isMounted don’t update the icon.
const isMounted = useRef(true);
const [icon, setIcon] = useState(null);
useEffect(() => {
if (
isMounted.current
&& ICONS.includes(name)
&& WEIGHTS.includes(weight)
) {
/* Use dynamic import to get corresponding icon as a module */
import(`assets/icons/${weight}/${name}.svg`)
.then(module => {
setIcon(module);
})
.catch(() => {
console.error(`Icon with name: ${name} not found!`);
setIcon(null);
});
}
}, [name, weight]);
useEffect(() => {
return () => { isMounted.current = false; };
}, []);
const Component = icon ? icon.default : icon;
return (
<i
aria-hidden={ariaHidden}
className={css.Icon}
>
{
icon ? (
<Component aria-labelledby={ariaLabel} data-testid="icon-module" />
) : (
<FALLBACK />
)
}
</i>
);
};
Icon.defaultProps = {
ariaLabel: '',
ariaHidden: false,
name: 'picture',
weight: 'light',
};
Icon.propTypes = {
/**
* If the icon is being displayed without accompanying text (like in a link or button), use `ariaLabel` to add descriptive text for screen readers
*/
ariaLabel: PropTypes.string,
/**
* If the icon is accompanied by descriptive text (like in a link or button) use `ariaHidden` to hide the icon from screen readers.
*/
ariaHidden: PropTypes.bool,
/**
* The name of the icon to use.
*/
name: PropTypes.oneOf(ICONS),
/**
* Size of the icon
*/
weight: PropTypes.oneOf(WEIGHTS),
};
export { Icon };
I’m trying to cover the catch() part of the dynamic import with a test, but I can’t seem to get the test to reach the catch, even when I pass in a faulty icon name.
My test:
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import {Icon} from '.';
test.only('loads fallback icon', async () => {
const { getByTestId } = render(<Icon name="bloop" />);
const iconModule = await waitFor(() => getByTestId('icon-fallback'));
expect(iconModule).toBeInTheDocument();
});
What gives?
Aucun commentaire:
Enregistrer un commentaire