Skip to content

Commit cb4315d

Browse files
committed
feat: enhance Switch component with accessibility improvements and new padding constants
1 parent 0228618 commit cb4315d

File tree

4 files changed

+37
-13
lines changed

4 files changed

+37
-13
lines changed

src/Shared/Components/Switch/Switch.component.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { AriaAttributes, HTMLAttributes } from 'react'
1+
import { AriaAttributes, useRef } from 'react'
22
import { AnimatePresence, motion } from 'framer-motion'
33

44
import { Tooltip } from '@Common/Tooltip'
55
import { ComponentSizeType } from '@Shared/constants'
6+
import { getUniqueId } from '@Shared/Helpers'
67

78
import { Icon } from '../Icon'
8-
import { INDETERMINATE_ICON_WIDTH_MAP, LOADING_COLOR_MAP, SQUARE_ICON_DIMENSION_MAP } from './constants'
9+
import { INDETERMINATE_ICON_WIDTH_MAP, LOADING_COLOR_MAP } from './constants'
910
import { SwitchProps } from './types'
1011
import { getSwitchContainerClass, getSwitchIconColor, getSwitchThumbClass, getSwitchTrackColor } from './utils'
1112

@@ -22,8 +23,11 @@ const Switch = ({
2223
iconName,
2324
indeterminate = false,
2425
size = ComponentSizeType.medium,
26+
name,
2527
onChange,
2628
}: SwitchProps) => {
29+
const inputId = useRef(getUniqueId())
30+
2731
const getAriaCheckedValue = (): AriaAttributes['aria-checked'] => {
2832
if (!isChecked) {
2933
return false
@@ -35,7 +39,6 @@ const Switch = ({
3539
const ariaCheckedValue = getAriaCheckedValue()
3640

3741
const showIndeterminateIcon = ariaCheckedValue === 'mixed'
38-
const role: HTMLAttributes<HTMLButtonElement>['role'] = showIndeterminateIcon ? 'checkbox' : 'switch'
3942

4043
const renderContent = () => {
4144
if (isLoading) {
@@ -69,7 +72,7 @@ const Switch = ({
6972
iconName && (
7073
<motion.span
7174
key="icon"
72-
className={`${SQUARE_ICON_DIMENSION_MAP[size]} flex dc__fill-available-space dc__no-shrink`}
75+
className="icon-dim-12 flex dc__fill-available-space dc__no-shrink"
7376
initial={{ scale: 0.8, opacity: 0 }}
7477
animate={{ scale: 1, opacity: 1 }}
7578
exit={{ scale: 0.8, opacity: 0 }}
@@ -91,23 +94,37 @@ const Switch = ({
9194
)
9295
}
9396

94-
// TODO: Can add hidden input for accessibility in case name [for forms] is given
9597
return (
9698
<Tooltip alwaysShowTippyOnHover={!!tooltipContent} content={tooltipContent}>
97-
<div className={`${getSwitchContainerClass({ shape, size })} flex dc__no-shrink py-2`}>
99+
<label
100+
htmlFor={inputId.current}
101+
className={`${getSwitchContainerClass({ shape, size })} flex dc__no-shrink py-2`}
102+
>
103+
<input
104+
type="checkbox"
105+
id={inputId.current}
106+
name={name}
107+
checked={isChecked}
108+
disabled={isDisabled}
109+
readOnly
110+
hidden
111+
/>
112+
98113
<button
99114
type="button"
100-
role={role}
115+
role="checkbox"
101116
aria-checked={ariaCheckedValue}
117+
aria-labelledby={inputId.current}
102118
aria-label={isLoading ? 'Loading...' : ariaLabel}
103119
data-testid={dataTestId}
104120
disabled={isDisabled || isLoading}
121+
aria-disabled={isDisabled}
105122
className={`p-0-imp h-100 flex flex-grow-1 dc__transparent ${isDisabled ? 'dc__disabled' : ''} dc__fill-available-space`}
106123
onClick={onChange}
107124
>
108125
{renderContent()}
109126
</button>
110-
</div>
127+
</label>
111128
</Tooltip>
112129
)
113130
}

src/Shared/Components/Switch/constants.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const INDETERMINATE_ICON_WIDTH_MAP: Record<SwitchProps['size'], string> =
5353
[ComponentSizeType.small]: 'w-10',
5454
}
5555

56-
export const SQUARE_ICON_DIMENSION_MAP: Record<SwitchProps['size'], string> = {
57-
[ComponentSizeType.medium]: 'icon-dim-12',
58-
[ComponentSizeType.small]: 'icon-dim-8',
56+
export const SWITCH_THUMB_PADDING_MAP: Record<SwitchProps['size'], string> = {
57+
[ComponentSizeType.medium]: 'p-3',
58+
[ComponentSizeType.small]: 'p-1',
5959
}

src/Shared/Components/Switch/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,14 @@ type SwitchShapeProps =
6666
export type SwitchProps = {
6767
/**
6868
* The ARIA label for the switch, used for accessibility purposes.
69+
* Please provide this when there is no clear label for the switch.
6970
*/
70-
ariaLabel: string
71+
ariaLabel?: string
72+
73+
/**
74+
* Used in forms to identify the switch.
75+
*/
76+
name: string
7177

7278
/**
7379
* A unique identifier for testing purposes.

src/Shared/Components/Switch/utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
SQUARE_SWITCH_SIZE_MAP,
88
SQUARE_SWITCH_TRACK_COLOR_MAP,
99
SWITCH_HEIGHT_MAP,
10+
SWITCH_THUMB_PADDING_MAP,
1011
} from './constants'
1112
import { SwitchProps } from './types'
1213

@@ -34,7 +35,7 @@ export const getSwitchThumbClass = ({
3435
return 'w-100 h-100 flex'
3536
}
3637

37-
return `flex p-3 ${shape === 'rounded' ? `dc__border-radius-50-per ${ROUNDED_SWITCH_THUMB_SIZE_MAP[size]}` : 'br-3'} bg__white`
38+
return `flex ${SWITCH_THUMB_PADDING_MAP[size]} ${shape === 'rounded' ? `dc__border-radius-50-per ${ROUNDED_SWITCH_THUMB_SIZE_MAP[size]}` : 'br-3'} bg__white`
3839
}
3940

4041
export const getSwitchIconColor = ({

0 commit comments

Comments
 (0)