From 0853c4078875666374e83f978cf74d53346b9445 Mon Sep 17 00:00:00 2001 From: daniel-zamora Date: Wed, 5 Feb 2025 15:26:35 -0500 Subject: [PATCH] EDSC-4342: Adds features facet icons (#1850) * EDSC-4342 adds features facet icons * EDSC-4342 adds icon variant * EDSC-4342 pr comments * EDSC-4342 alligns new icons with text * EDSC-4342: Fixing label to use aira-label * EDSC-4342 pr comments --------- Co-authored-by: Ed Olivares <34591886+eudoroolivares2016@users.noreply.github.com> --- .../src/js/components/EDSCIcon/EDSCIcon.jsx | 15 ++++- .../src/js/components/EDSCIcon/EDSCIcon.scss | 5 ++ .../EDSCIcon/__tests__/EDSCIcon.test.jsx | 12 +++- static/src/js/components/Facets/Facets.jsx | 14 +++++ .../src/js/components/Facets/FacetsItem.jsx | 63 ++++++++++++------- .../src/js/components/Facets/FacetsItem.scss | 6 ++ .../Facets/__tests__/Facets.test.jsx | 16 ++++- 7 files changed, 104 insertions(+), 27 deletions(-) diff --git a/static/src/js/components/EDSCIcon/EDSCIcon.jsx b/static/src/js/components/EDSCIcon/EDSCIcon.jsx index b19ea89fc8..086547b233 100644 --- a/static/src/js/components/EDSCIcon/EDSCIcon.jsx +++ b/static/src/js/components/EDSCIcon/EDSCIcon.jsx @@ -11,6 +11,9 @@ import './EDSCIcon.scss' * @param {String} className - An optional classname. * @param {Object} context - Optional object to pass to `react-icons/IconContext.Provider` * @param {String} title - Optional string used as the `title` attribute + * @param {String} size - Optional string used as the `size` attribute + * @param {String} variant - Optional string that determines the icon's wrapper element and styling. + * @param {Object} ariaLabel - Optional string used as the `aria-label` attribute */ export const EDSCIcon = ({ icon, @@ -20,6 +23,7 @@ export const EDSCIcon = ({ size, title, variant, + ariaLabel, ...props }) => { if (!icon) return null @@ -37,6 +41,7 @@ export const EDSCIcon = ({ className={iconClassNames} title={title} data-testid="edsc-icon-simple" + aria-label={ariaLabel} // eslint-disable-next-line react/jsx-props-no-spreading {...props} /> @@ -55,6 +60,7 @@ export const EDSCIcon = ({ title={title} size={size} data-testid="edsc-icon" + aria-label={ariaLabel} {...props} /> {children} @@ -70,6 +76,7 @@ export const EDSCIcon = ({ title={title} size={size} data-testid="edsc-icon-details" + aria-label={ariaLabel} {...props} /> {children} @@ -84,6 +91,7 @@ export const EDSCIcon = ({ className={iconClassNames} title={title} size={size} + aria-label={ariaLabel} data-testid="edsc-icon-details" {...props} /> @@ -98,6 +106,7 @@ export const EDSCIcon = ({ className={iconClassNames} title={title} size={size} + aria-label={ariaLabel} data-testid="edsc-icon" {...props} /> @@ -113,7 +122,8 @@ EDSCIcon.defaultProps = { context: null, size: '1rem', title: null, - variant: null + variant: null, + ariaLabel: null } EDSCIcon.propTypes = { @@ -123,7 +133,8 @@ EDSCIcon.propTypes = { context: PropTypes.shape({}), size: PropTypes.string, title: PropTypes.string, - variant: PropTypes.string + variant: PropTypes.string, + ariaLabel: PropTypes.string } export default EDSCIcon diff --git a/static/src/js/components/EDSCIcon/EDSCIcon.scss b/static/src/js/components/EDSCIcon/EDSCIcon.scss index 99967d920b..d5c5243a53 100644 --- a/static/src/js/components/EDSCIcon/EDSCIcon.scss +++ b/static/src/js/components/EDSCIcon/EDSCIcon.scss @@ -3,4 +3,9 @@ color: $color__carbon--40; margin-left: 0.25rem; } + + &--facet { + color: $color__carbon--40; + margin-right: 0.25rem; + } } diff --git a/static/src/js/components/EDSCIcon/__tests__/EDSCIcon.test.jsx b/static/src/js/components/EDSCIcon/__tests__/EDSCIcon.test.jsx index 0b35186115..4f1f69f7d9 100644 --- a/static/src/js/components/EDSCIcon/__tests__/EDSCIcon.test.jsx +++ b/static/src/js/components/EDSCIcon/__tests__/EDSCIcon.test.jsx @@ -33,7 +33,7 @@ describe('EDSCIcon component', () => { }) describe('when a variant is supplied', () => { - test('should add the class name', async () => { + test('should add the variant', async () => { render() const icon = await screen.findByTestId('edsc-icon') @@ -42,6 +42,16 @@ describe('EDSCIcon component', () => { }) }) + describe('when an aria-label is supplied', () => { + test('should add the aria-label', async () => { + render() + + const icon = await screen.findByTestId('edsc-icon') + + expect(icon.getAttribute('aria-label')).toEqual('test-aria-label') + }) + }) + describe('when children are provided', () => { test('should render the children', async () => { render( diff --git a/static/src/js/components/Facets/Facets.jsx b/static/src/js/components/Facets/Facets.jsx index 03569ead46..342ce4e210 100644 --- a/static/src/js/components/Facets/Facets.jsx +++ b/static/src/js/components/Facets/Facets.jsx @@ -2,6 +2,8 @@ import React from 'react' import PropTypes from 'prop-types' import { camelCase } from 'lodash-es' +import { FaMap } from 'react-icons/fa' +import { CloudFill, Settings } from '@edsc/earthdata-react-icons/horizon-design-system/hds/ui' import { changeFeatureFacet, changeCmrFacet } from '../../util/facets' import FacetsGroup from './FacetsGroup' @@ -47,6 +49,10 @@ const Facets = (props) => { featuresFacet.children.push({ applied: featureFacets.availableInEarthdataCloud, title: 'Available in Earthdata Cloud', + iconProps: { + icon: CloudFill, + ariaLabel: 'A cloud icon' + }, type: 'feature' }) } @@ -55,6 +61,10 @@ const Facets = (props) => { featuresFacet.children.push({ applied: featureFacets.customizable, title: 'Customizable', + iconProps: { + icon: Settings, + ariaLabel: 'A gear icon' + }, description: 'Include only collections that support customization (temporal, spatial, or variable subsetting, reformatting, etc.)', type: 'feature' }) @@ -63,6 +73,10 @@ const Facets = (props) => { if (showMapImagery) { featuresFacet.children.push({ applied: featureFacets.mapImagery, + iconProps: { + icon: FaMap, + ariaLabel: 'A map icon' + }, title: 'Map Imagery', type: 'feature' }) diff --git a/static/src/js/components/Facets/FacetsItem.jsx b/static/src/js/components/Facets/FacetsItem.jsx index f5e9949259..0bfa95ebd7 100644 --- a/static/src/js/components/Facets/FacetsItem.jsx +++ b/static/src/js/components/Facets/FacetsItem.jsx @@ -86,6 +86,8 @@ class FacetsItem extends Component { `facets-item--level-${level}` ) + const { iconProps } = facet + return (
  • { children &&
      {children}
    } @@ -149,7 +164,11 @@ FacetsItem.propTypes = { children: PropTypes.arrayOf(PropTypes.shape({})), count: PropTypes.number, title: PropTypes.string, - description: PropTypes.string + description: PropTypes.string, + iconProps: PropTypes.shape({ + icon: PropTypes.elementType, + ariaLabel: PropTypes.string + }) }).isRequired, facetCategory: PropTypes.string.isRequired, level: PropTypes.number.isRequired, diff --git a/static/src/js/components/Facets/FacetsItem.scss b/static/src/js/components/Facets/FacetsItem.scss index b49cb8b895..6fc99624b9 100644 --- a/static/src/js/components/Facets/FacetsItem.scss +++ b/static/src/js/components/Facets/FacetsItem.scss @@ -46,6 +46,12 @@ } } + &__title-container { + display: flex; + align-items: center; + flex: 1; + } + &__title { display: inline-block; text-overflow: ellipsis; diff --git a/static/src/js/components/Facets/__tests__/Facets.test.jsx b/static/src/js/components/Facets/__tests__/Facets.test.jsx index 0428bddf36..7c2a6217d9 100644 --- a/static/src/js/components/Facets/__tests__/Facets.test.jsx +++ b/static/src/js/components/Facets/__tests__/Facets.test.jsx @@ -223,7 +223,7 @@ function setup(overrideProps = {}) { } describe('Facets Features Map Imagery component', () => { - test('only renders enabled feature FacetsGroup', async () => { + test('allows toggling Map Imagery checkbox', async () => { setup({ portal: { features: { @@ -246,7 +246,7 @@ describe('Facets Features Map Imagery component', () => { expect(screen.getAllByRole('checkbox', { checked: true })).toHaveLength(1) }) - test('only renders enabled feature FacetsGroup', async () => { + test('renders feature facets with tooltips and icons', async () => { setup({ portal: { features: { @@ -261,6 +261,18 @@ describe('Facets Features Map Imagery component', () => { const user = userEvent.setup() + // Check for Map Imagery icon + const mapImageryIcon = screen.getByLabelText('A map icon') + expect(mapImageryIcon).toBeInTheDocument() + + // Check for Customizable icon + const customizableIcon = screen.getByLabelText('A gear icon') + expect(customizableIcon).toBeInTheDocument() + + // Check for Cloud icon + const cloudIcon = screen.queryByLabelText('A cloud icon') + expect(cloudIcon).not.toBeInTheDocument() + const featuresElements = screen.getAllByText('Features') expect(featuresElements).toHaveLength(1) expect(featuresElements[0]).toBeInTheDocument()