Skip to content

Commit

Permalink
fix(CheckboxRadioBase): Resolve 'nested-interactive' axe v4 error
Browse files Browse the repository at this point in the history
This resolves the following error:

https://dequeuniversity.com/rules/axe/4.4/nested-interactive

This was occurring as we effectively had a checkbox within a checkbox, as we had a visually-hidden `<input>` contained within an element with `role="checkbox"`.

The removes the roles from the parent elements.

A further problem was that the 0px <input> caused a screen reader used with Firefox to behave oddly. This reorganises things slightly so that it has the same dimensions as`StyledCheckmark`.
  • Loading branch information
jpveooys committed Apr 13, 2022
1 parent e23dd40 commit 369d808
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react'
import { CheckboxRadioBase, CheckboxRadioBaseProps } from '../CheckboxRadioBase'
import { StyledCheckbox } from './partials/StyledCheckbox'
import { StyledCheckmark } from './partials/StyledCheckmark'
import { StyledCheckmarkWrapper } from './partials/StyledCheckmarkWrapper'

export type CheckboxProps = Omit<CheckboxRadioBaseProps, 'type' | 'partials'>

Expand All @@ -15,6 +16,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
partials={{
Root: StyledCheckbox,
Checkmark: StyledCheckmark,
CheckmarkWrapper: StyledCheckmarkWrapper,
}}
{...props}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ function getCheckboxActiveStyle() {
`
}

export const StyledCheckmark = styled.div<CheckmarkProps>`
position: absolute;
top: ${({ $hasContainer }) => ($hasContainer ? '12px' : '4px')};
left: ${({ $hasContainer }) => ($hasContainer ? '12px' : '4px')};
height: 18px;
width: 18px;
export const StyledCheckmark = styled.span<CheckmarkProps>`
display: block;
position: relative;
height: 100%;
width: 100%;
background-color: ${color('neutral', 'white')};
border: 1px solid ${color('neutral', '200')};
border-radius: 3px;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from 'styled-components'

import { CheckmarkWrapperProps } from '../../CheckboxRadioBase'

export const StyledCheckmarkWrapper = styled.span<CheckmarkWrapperProps>`
display: block;
position: absolute;
top: ${({ $hasContainer }) => ($hasContainer ? '12px' : '4px')};
left: ${({ $hasContainer }) => ($hasContainer ? '12px' : '4px')};
height: 18px;
width: 18px;
`
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const CheckboxRadioBase = React.forwardRef<
value,
variant = CHECKBOX_RADIO_VARIANT.DEFAULT,
type,
partials: { Root, Checkmark },
partials: { Root, Checkmark, CheckmarkWrapper },
...rest
},
ref
Expand Down Expand Up @@ -104,8 +104,6 @@ export const CheckboxRadioBase = React.forwardRef<
<StyledWrapper>
<Root
className={className}
role={type}
aria-checked={isChecked}
$isDisabled={isDisabled}
$hasContainer={hasContainer}
$isInvalid={isInvalid}
Expand All @@ -122,24 +120,26 @@ export const CheckboxRadioBase = React.forwardRef<
htmlFor={id}
data-testid={`${type}-label`}
>
<StyledInput
defaultChecked={defaultChecked}
ref={mergeRefs([localRef, ref])}
id={id}
type={type}
name={name}
value={value}
onChange={handleOnChange}
onBlur={onBlur}
disabled={isDisabled}
data-testid={`${type}-input`}
checked={isControlled ? isChecked : undefined}
{...rest}
/>
<Checkmark
$hasContainer={hasContainer}
$isDisabled={isDisabled}
/>
<CheckmarkWrapper $hasContainer={hasContainer}>
<StyledInput
defaultChecked={defaultChecked}
ref={mergeRefs([localRef, ref])}
id={id}
type={type}
name={name}
value={value}
onChange={handleOnChange}
onBlur={onBlur}
disabled={isDisabled}
data-testid={`${type}-input`}
checked={isControlled ? isChecked : undefined}
{...rest}
/>
<Checkmark
$hasContainer={hasContainer}
$isDisabled={isDisabled}
/>
</CheckmarkWrapper>
{label}
{description && (
<StyledDescription data-testid={`${type}-description`}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface CheckmarkProps {
$isDisabled?: boolean
}

export interface CheckmarkWrapperProps {
$hasContainer: boolean
}

export interface CheckboxRadioBaseProps
extends ComponentWithClass,
InputValidationProps {
Expand Down Expand Up @@ -72,5 +76,6 @@ export interface CheckboxRadioBaseProps
partials: {
Root: React.ComponentType<CheckboxRootProps>
Checkmark: React.ComponentType<CheckmarkProps>
CheckmarkWrapper: React.ComponentType<CheckmarkWrapperProps>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import styled from 'styled-components'

export const StyledInput = styled.input`
position: absolute;
height: 0;
width: 0;
height: 100%;
width: 100%;
opacity: 0;
cursor: pointer;
`
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react'
import { CheckboxRadioBase, CheckboxRadioBaseProps } from '../CheckboxRadioBase'
import { StyledRadio } from './partials/StyledRadio'
import { StyledCheckmark } from './partials/StyledCheckmark'
import { StyledCheckmarkWrapper } from './partials/StyledCheckmarkWrapper'

export type RadioProps = Omit<CheckboxRadioBaseProps, 'type' | 'partials'>

Expand All @@ -15,6 +16,7 @@ export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
partials={{
Root: StyledRadio,
Checkmark: StyledCheckmark,
CheckmarkWrapper: StyledCheckmarkWrapper,
}}
{...props}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { CheckmarkProps } from '../../CheckboxRadioBase'

const { color } = selectors

export const StyledCheckmark = styled.div<CheckmarkProps>`
position: absolute;
top: ${({ $hasContainer }) => ($hasContainer ? '13px' : '4px')};
left: ${({ $hasContainer }) => ($hasContainer ? '12px' : '4px')};
height: 16px;
width: 16px;
export const StyledCheckmark = styled.span<CheckmarkProps>`
display: block;
position: relative;
height: 100%;
width: 100%;
border-radius: 999px;
border: 1px solid
${({ $hasContainer, $isDisabled }) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from 'styled-components'

import { CheckmarkWrapperProps } from '../../CheckboxRadioBase'

export const StyledCheckmarkWrapper = styled.span<CheckmarkWrapperProps>`
display: block;
position: absolute;
top: ${({ $hasContainer }) => ($hasContainer ? '13px' : '4px')};
left: ${({ $hasContainer }) => ($hasContainer ? '12px' : '4px')};
height: 16px;
width: 16px;
`

0 comments on commit 369d808

Please sign in to comment.