From aade0256da8969967b62a0f02a472703a33f999d Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Wed, 16 Aug 2023 17:23:29 +0200 Subject: [PATCH] SONAR-19709 fix facets a11y --- .../design-system/src/components/FacetBox.tsx | 5 +- .../src/components/FacetItem.tsx | 97 ++++++++++--------- .../components/__tests__/FacetBox-test.tsx | 6 +- .../src/main/js/apps/issues/test-utils.tsx | 4 +- .../apps/projects/components/AllProjects.tsx | 4 +- .../apps/projects/filters/RangeFacetBase.tsx | 2 +- 6 files changed, 62 insertions(+), 56 deletions(-) diff --git a/server/sonar-web/design-system/src/components/FacetBox.tsx b/server/sonar-web/design-system/src/components/FacetBox.tsx index ae0cd7412d5..c0aa8cd4913 100644 --- a/server/sonar-web/design-system/src/components/FacetBox.tsx +++ b/server/sonar-web/design-system/src/components/FacetBox.tsx @@ -73,7 +73,7 @@ export function FacetBox(props: FacetBoxProps) { open = false, } = props; - const clearable = !disabled && Boolean(onClear) && count; + const clearable = !disabled && Boolean(onClear) && count !== undefined && count > 0; const counter = count ?? 0; const expandable = !disabled && Boolean(onClick); const id = React.useMemo(() => idProp ?? uniqueId('filter-facet-'), [idProp]); @@ -84,7 +84,6 @@ export function FacetBox(props: FacetBoxProps) { data-property={dataProperty} hasEmbeddedFacets={hasEmbeddedFacets} inner={inner} - role="listitem" >
{open && ( -
+
{children}
)} diff --git a/server/sonar-web/design-system/src/components/FacetItem.tsx b/server/sonar-web/design-system/src/components/FacetItem.tsx index 42c6e16a7dc..bc6306b5d12 100644 --- a/server/sonar-web/design-system/src/components/FacetItem.tsx +++ b/server/sonar-web/design-system/src/components/FacetItem.tsx @@ -19,6 +19,7 @@ */ import styled from '@emotion/styled'; +import classNames from 'classnames'; import * as React from 'react'; import tw from 'twin.macro'; import { themeBorder, themeColor, themeContrast } from '../helpers'; @@ -42,7 +43,7 @@ export type FacetItemProps = Omit & { const STATBAR_MAX_WIDTH = 60; export function BaseFacetItem({ - active, + active = false, className, disabled: disabledProp = false, disableZero = true, @@ -66,33 +67,34 @@ export function BaseFacetItem({ }; return ( - -
- {name} -
- {stat} - {isDefined(statBarPercent) && ( - - - - )} + + +
+ {name} +
+ {stat} + {isDefined(statBarPercent) && ( + + + + )} +
-
- + + ); } @@ -113,20 +115,7 @@ const StyledButton = styled(ButtonSecondary)<{ active?: boolean; small?: boolean --background: ${({ active }) => (active ? themeColor('facetItemSelected') : 'transparent')}; --backgroundHover: ${({ active }) => (active ? themeColor('facetItemSelected') : 'transparent')}; - --border: ${({ active }) => - active - ? themeBorder('default', 'facetItemSelectedBorder') - : themeBorder('default', 'transparent')}; - - &:hover { - --border: ${themeBorder('default', 'facetItemSelectedBorder')}; - } - - &:hover, - &:active, - &:focus { - border-color: ${themeColor('facetItemSelectedBorder')}; - } + --border: none; & div.container { ${tw`sw-container`}; @@ -164,6 +153,25 @@ const StyledButton = styled(ButtonSecondary)<{ active?: boolean; small?: boolean } `; +/*&:hover { + --border: ${themeBorder('default', 'facetItemSelectedBorder')}; + }*/ + +const StyledItem = styled.span<{ active: boolean }>` + border: ${({ active }) => + active + ? themeBorder('default', 'facetItemSelectedBorder') + : themeBorder('default', 'transparent')}; + + border-radius: 0.25rem; + + &:hover, + &:active, + &:focus { + border-color: ${themeColor('facetItemSelectedBorder')}; + } +`; + const FacetStatBar = styled.div` ${tw`sw-inline-block`} ${tw`sw-ml-2`} @@ -185,22 +193,23 @@ export const HighlightedFacetItems = styled.div` width: 100%; ${FacetItem} { - padding-top: 1px; - padding-bottom: 1px; - &:is(:hover, .active) { border-color: ${themeColor('facetItemSelectedBorder')}; + padding-bottom: 1px; border-bottom-width: 0; border-bottom-right-radius: 0rem; border-bottom-left-radius: 0rem; &:last-of-type { + padding-bottom: 0; border-bottom-width: 1px; border-radius: 0.25rem; } & ~ ${FacetItem} { border-color: ${themeColor('facetItemSelectedBorder')}; + padding-bottom: 1px; + padding-top: 1px; border-top-width: 0; border-bottom-width: 0; border-radius: 0; diff --git a/server/sonar-web/design-system/src/components/__tests__/FacetBox-test.tsx b/server/sonar-web/design-system/src/components/__tests__/FacetBox-test.tsx index a7146855abf..9d8981cc02b 100644 --- a/server/sonar-web/design-system/src/components/__tests__/FacetBox-test.tsx +++ b/server/sonar-web/design-system/src/components/__tests__/FacetBox-test.tsx @@ -30,9 +30,7 @@ it('should render an empty disabled facet box', async () => { renderComponent({ disabled: true, hasEmbeddedFacets: true, onClick }); - expect(screen.getByRole('listitem')).toBeInTheDocument(); - - expect(screen.queryByRole('list')).not.toBeInTheDocument(); + expect(screen.queryByRole('group')).not.toBeInTheDocument(); expect(screen.getByText('Test FacetBox')).toBeInTheDocument(); @@ -58,7 +56,7 @@ it('should render an inner expanded facet box with count', async () => { open: true, }); - expect(screen.getByRole('list')).toBeInTheDocument(); + expect(screen.getByRole('group')).toBeInTheDocument(); expect(screen.getByRole('button', { expanded: true })).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx index a5e8d9e7ee3..6a524e9ab45 100644 --- a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx +++ b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx @@ -124,8 +124,8 @@ export const ui = { authorFacetSearch: byPlaceholderText('search.search_for_authors'), inNewCodeFilter: byRole('checkbox', { name: 'issues.new_code' }), - languageFacetList: byRole('list', { name: 'issues.facet.languages' }), - ruleFacetList: byRole('list', { name: 'issues.facet.rules' }), + languageFacetList: byRole('group', { name: 'issues.facet.languages' }), + ruleFacetList: byRole('group', { name: 'issues.facet.rules' }), ruleFacetSearch: byPlaceholderText('search.search_for_rules'), tagFacetSearch: byPlaceholderText('search.search_for_tags'), diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx index 654ef2df955..9abce887cb0 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx @@ -233,7 +233,7 @@ export class AllProjects extends React.PureComponent { {({ top }) => ( -
- + )} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/RangeFacetBase.tsx b/server/sonar-web/src/main/js/apps/projects/filters/RangeFacetBase.tsx index c6eb173ccd5..d3202c1dc0e 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/RangeFacetBase.tsx +++ b/server/sonar-web/src/main/js/apps/projects/filters/RangeFacetBase.tsx @@ -111,7 +111,7 @@ export default class RangeFacetBase extends React.PureComponent { disableZero={false} aria-label={this.props.renderAccessibleLabel(option)} key={option} - className={classNames({ active }, optionClassName)} + className={classNames(optionClassName)} data-key={option} onClick={this.handleClick} name={this.props.renderOption(option, this.isSelected(option) || isUnderSelectedOption)} -- 2.39.5