Fix/SW-1563 accessibility

* fix(SW-1563): Added new IconButton component to the design system and removed Icon variant inside the Button component
* fix(SW-1563): Added buttons around clickable images and changed to design system components
* fix(SW-1563): Renamed variants to match Figma
* fix(SW-1563): Renamed AriaButton to ButtonRAC

Approved-by: Michael Zetterberg
Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-05-02 06:27:30 +00:00
parent efcbde1647
commit 8b32abbefc
32 changed files with 909 additions and 547 deletions

View File

@@ -0,0 +1,141 @@
import type { Meta, StoryObj } from '@storybook/react'
import { fn } from '@storybook/test'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { IconButton } from './IconButton'
import { config } from './variants'
const meta: Meta<typeof IconButton> = {
title: 'Components/IconButton',
component: IconButton,
argTypes: {
onPress: {
table: {
disable: true,
},
},
theme: {
control: 'select',
options: Object.keys(config.variants.theme),
default: 'Primary',
},
style: {
control: 'select',
options: Object.keys(config.variants.style),
default: 'Normal',
type: 'string',
description: `The style variant is only applied on certain variants. The examples below shows the possible combinations of variants and style variants.`,
},
},
}
export default meta
type Story = StoryObj<typeof IconButton>
export const PrimaryDefault: Story = {
args: {
onPress: fn(),
children: <MaterialIcon icon="search" size={24} color="CurrentColor" />,
theme: 'Primary',
},
}
export const PrimaryDisabled: Story = {
args: {
...PrimaryDefault.args,
isDisabled: true,
},
}
export const InvertedDefault: Story = {
args: {
onPress: fn(),
children: (
<MaterialIcon icon="arrow_forward" size={24} color="CurrentColor" />
),
theme: 'Inverted',
},
}
export const InvertedDisabled: Story = {
args: {
...InvertedDefault.args,
isDisabled: true,
},
}
export const InvertedElevated: Story = {
args: {
...InvertedDefault.args,
style: 'Elevated',
},
}
export const InvertedElevatedDisabled: Story = {
args: {
...InvertedElevated.args,
isDisabled: true,
},
}
export const InvertedMuted: Story = {
args: {
...InvertedDefault.args,
children: <MaterialIcon icon="close" size={24} color="CurrentColor" />,
style: 'Muted',
},
}
export const InvertedMutedDisabled: Story = {
args: {
...InvertedMuted.args,
isDisabled: true,
},
}
export const InvertedFaded: Story = {
args: {
...InvertedDefault.args,
style: 'Faded',
},
}
export const InvertedFadedDisabled: Story = {
args: {
...InvertedFaded.args,
isDisabled: true,
},
}
export const TertiaryElevated: Story = {
args: {
onPress: fn(),
children: <MaterialIcon icon="arrow_back" size={24} color="CurrentColor" />,
theme: 'Tertiary',
style: 'Elevated',
},
}
export const TertiaryDisabled: Story = {
args: {
...TertiaryElevated.args,
isDisabled: true,
},
}
export const BlackMuted: Story = {
args: {
onPress: fn(),
children: <MaterialIcon icon="close" size={24} color="CurrentColor" />,
theme: 'Black',
},
}
export const BlackMutedDisabled: Story = {
args: {
...BlackMuted.args,
isDisabled: true,
},
}

View File

@@ -0,0 +1,20 @@
import { Button as ButtonRAC } from 'react-aria-components'
import { variants } from './variants'
import type { IconButtonProps } from './types'
export function IconButton({
theme,
style,
className,
...props
}: IconButtonProps) {
const classNames = variants({
theme,
style,
className,
})
return <ButtonRAC {...props} className={classNames} />
}

View File

@@ -0,0 +1,102 @@
.iconButton {
border-radius: var(--Corner-radius-rounded);
border-width: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
&:disabled {
cursor: unset;
}
}
.theme-primary {
background-color: var(--Component-Button-Brand-Primary-Fill-Default);
color: var(--Component-Button-Brand-Primary-On-fill-Default);
&:hover:not(:disabled) {
background-color: var(--Component-Button-Brand-Primary-Fill-Hover);
color: var(--Component-Button-Brand-Primary-On-fill-Hover);
}
&:disabled {
background-color: var(--Component-Button-Brand-Primary-Fill-Disabled);
color: var(--Component-Button-Brand-Primary-On-fill-Disabled);
}
}
.theme-inverted {
background-color: var(--Component-Button-Inverted-Fill-Default);
color: var(--Component-Button-Inverted-On-fill-Default);
&:hover:not(:disabled) {
background-color: var(--Component-Button-Inverted-Fill-Hover);
color: var(--Component-Button-Inverted-On-fill-Hover);
}
&:disabled {
background-color: var(--Component-Button-Inverted-Fill-Disabled);
color: var(--Component-Button-Inverted-On-fill-Disabled);
}
&.style-muted {
color: var(--Component-Button-Muted-On-fill-Inverted);
&:hover:not(:disabled) {
color: var(--Component-Button-Muted-On-fill-Inverted);
}
&:disabled {
color: var(--Component-Button-Muted-On-fill-Disabled);
}
}
}
.theme-tertiary {
background-color: var(--Component-Button-Brand-Tertiary-Fill-Default);
color: var(--Component-Button-Brand-Tertiary-On-fill-Default);
&:hover:not(:disabled) {
background-color: var(--Component-Button-Brand-Tertiary-Fill-Hover);
color: var(--Component-Button-Brand-Tertiary-On-fill-Hover);
}
&:disabled {
background-color: var(--Component-Button-Brand-Tertiary-Fill-Disabled);
color: var(--Component-Button-Brand-Tertiary-On-fill-Disabled);
}
}
.theme-black {
color: var(--Component-Button-Muted-On-fill-Default);
&:hover:not(:disabled) {
color: var(--Component-Button-Muted-On-fill-Hover-Inverted);
}
&:disabled {
color: var(--Component-Button-Muted-On-fill-Disabled);
}
}
.style-elevated {
box-shadow: 0px 0px 8px 1px #0000001a;
}
.style-faded {
background-color: var(--Component-Button-Inverted-Fill-Faded);
}
.style-muted {
background-color: var(--Component-Button-Muted-Fill-Default);
&:hover:not(:disabled) {
background-color: var(--Component-Button-Muted-Fill-Hover-inverted);
}
&:disabled {
background-color: var(--Component-Button-Muted-Fill-Disabled-inverted);
}
}

View File

@@ -0,0 +1 @@
export { IconButton } from './IconButton'

View File

@@ -0,0 +1,10 @@
import { Button } from 'react-aria-components'
import type { VariantProps } from 'class-variance-authority'
import type { ComponentProps } from 'react'
import type { variants } from './variants'
export interface IconButtonProps
extends Omit<ComponentProps<typeof Button>, 'style'>,
VariantProps<typeof variants> {}

View File

@@ -0,0 +1,78 @@
import { cva } from 'class-variance-authority'
import styles from './iconButton.module.css'
const variantKeys = {
theme: {
Primary: 'Primary',
Tertiary: 'Tertiary',
Inverted: 'Inverted',
Black: 'Black',
},
style: {
Normal: 'Normal',
Muted: 'Muted',
Elevated: 'Elevated',
Faded: 'Faded',
},
} as const
export const config = {
variants: {
theme: {
[variantKeys.theme.Primary]: styles['theme-primary'],
[variantKeys.theme.Tertiary]: styles['theme-tertiary'],
[variantKeys.theme.Inverted]: styles['theme-inverted'],
[variantKeys.theme.Black]: styles['theme-black'],
},
// Some variants cannot be used in combination with certain style variants.
// The style variant will be applied using the compoundVariants.
style: {
[variantKeys.style.Normal]: '',
[variantKeys.style.Muted]: '',
[variantKeys.style.Elevated]: '',
[variantKeys.style.Faded]: '',
},
},
compoundVariants: [
// Primary should only use Normal
{ theme: variantKeys.theme.Primary, className: styles['style-normal'] },
// Tertiary should only use Elevated
{
theme: variantKeys.theme.Tertiary,
className: styles['style-elevated'],
},
// Black should only use Muted
{ theme: variantKeys.theme.Black, className: styles['style-muted'] },
// Inverted can use any style variant
{
theme: variantKeys.theme.Inverted,
style: variantKeys.style.Normal,
className: styles['style-normal'],
},
{
theme: variantKeys.theme.Inverted,
style: variantKeys.style.Muted,
className: styles['style-muted'],
},
{
theme: variantKeys.theme.Inverted,
style: variantKeys.style.Elevated,
className: styles['style-elevated'],
},
{
theme: variantKeys.theme.Inverted,
style: variantKeys.style.Faded,
className: styles['style-faded'],
},
],
defaultVariants: {
theme: variantKeys.theme.Primary,
style: variantKeys.style.Normal,
},
}
export const variants = cva(styles.iconButton, config)