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:
@@ -36,8 +36,7 @@ const meta: Meta<typeof Button> = {
|
||||
control: 'select',
|
||||
options: Object.keys(buttonConfig.variants.size),
|
||||
type: 'string',
|
||||
description:
|
||||
'The size of the button. Defaults to `Large`. This variant does not apply to the `Icon` variant.',
|
||||
description: 'The size of the button. Defaults to `Large`.',
|
||||
},
|
||||
wrapping: {
|
||||
control: 'radio',
|
||||
@@ -351,25 +350,3 @@ export const TextWithIconInverted: Story = {
|
||||
color: 'Inverted',
|
||||
},
|
||||
}
|
||||
|
||||
export const Icon: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: <MaterialIcon icon="favorite" size={24} />,
|
||||
variant: 'Icon',
|
||||
},
|
||||
}
|
||||
|
||||
export const IconWithColor: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: (
|
||||
<MaterialIcon
|
||||
icon="check_circle"
|
||||
size={24}
|
||||
color="Icon/Feedback/Success"
|
||||
/>
|
||||
),
|
||||
variant: 'Icon',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use client'
|
||||
|
||||
import { Button as ButtonRAC } from 'react-aria-components'
|
||||
|
||||
import { variants } from './variants'
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--Space-x05);
|
||||
|
||||
&:disabled {
|
||||
cursor: unset;
|
||||
@@ -166,11 +167,3 @@
|
||||
.variant-text.color-inverted:disabled {
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Disabled);
|
||||
}
|
||||
|
||||
.variant-icon {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ export const config = {
|
||||
Tertiary: styles['variant-tertiary'],
|
||||
Inverted: styles['variant-inverted'],
|
||||
Text: styles['variant-text'],
|
||||
Icon: styles['variant-icon'],
|
||||
},
|
||||
color: {
|
||||
Primary: styles['color-primary'],
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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} />
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { IconButton } from './IconButton'
|
||||
10
packages/design-system/lib/components/IconButton/types.ts
Normal file
10
packages/design-system/lib/components/IconButton/types.ts
Normal 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> {}
|
||||
78
packages/design-system/lib/components/IconButton/variants.ts
Normal file
78
packages/design-system/lib/components/IconButton/variants.ts
Normal 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)
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Typography } from '../../Typography'
|
||||
import { Rate, RateTermDetails } from '../types'
|
||||
|
||||
import { Button } from '../../Button'
|
||||
import { IconButton } from '../../IconButton'
|
||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||
import Modal from '../Modal'
|
||||
import styles from '../rate-card.module.css'
|
||||
@@ -67,13 +67,13 @@ export default function CampaignRateCard({
|
||||
title={rateTitle}
|
||||
subtitle={paymentTerm}
|
||||
trigger={
|
||||
<Button variant="Icon" size="Small">
|
||||
<IconButton theme="Black" style="Muted">
|
||||
<MaterialIcon
|
||||
icon="info"
|
||||
size={20}
|
||||
color="Icon/Default"
|
||||
/>
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
{rateTermDetails.map((termGroup) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Rate, RateTermDetails } from '../types'
|
||||
|
||||
import { Button } from '../../Button'
|
||||
import { IconButton } from '../../IconButton'
|
||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||
import { Typography } from '../../Typography'
|
||||
import Modal from '../Modal'
|
||||
@@ -63,13 +63,13 @@ export default function CodeRateCard({
|
||||
title={rateTitle}
|
||||
subtitle={paymentTerm}
|
||||
trigger={
|
||||
<Button variant="Icon" size="Small">
|
||||
<IconButton theme="Black" style="Muted">
|
||||
<MaterialIcon
|
||||
icon="info"
|
||||
size={20}
|
||||
color="Icon/Default"
|
||||
/>
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
{rateTermDetails.map((termGroup) => (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '../../Button'
|
||||
import { IconButton } from '../../IconButton'
|
||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||
import { Typography } from '../../Typography'
|
||||
import styles from '../rate-card.module.css'
|
||||
@@ -34,9 +34,9 @@ export default function NoRateAvailableCard({
|
||||
<header>
|
||||
<Typography variant="Tag/sm">
|
||||
<h3 className={`${styles.title} ${styles.textDisabled}`}>
|
||||
<Button variant="Icon" size="Small">
|
||||
<IconButton theme="Black" style="Muted">
|
||||
<MaterialIcon icon="info" size={20} color="Icon/Default" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
{`${rateTitle} / ${paymentTerm}`}
|
||||
</h3>
|
||||
</Typography>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Typography } from '../../Typography'
|
||||
import { RatePointsOption, RateTermDetails } from '../types'
|
||||
|
||||
import { RadioGroup } from 'react-aria-components'
|
||||
import { Button } from '../../Button'
|
||||
import { IconButton } from '../../IconButton'
|
||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||
import { Radio } from '../../Radio'
|
||||
import Modal from '../Modal'
|
||||
@@ -49,9 +49,9 @@ export default function PointsRateCard({
|
||||
title={rateTitle}
|
||||
subtitle={paymentTerm}
|
||||
trigger={
|
||||
<Button variant="Icon" size="Small">
|
||||
<IconButton theme="Black" style="Muted">
|
||||
<MaterialIcon icon="info" size={20} color="Icon/Default" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
{rateTermDetails.map((termGroup) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Rate, RateTermDetails } from '../types'
|
||||
|
||||
import { Button } from '../../Button'
|
||||
import { IconButton } from '../../IconButton'
|
||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||
import { Typography } from '../../Typography'
|
||||
import Modal from '../Modal'
|
||||
@@ -56,13 +56,13 @@ export default function RegularRateCard({
|
||||
title={rateTitle}
|
||||
subtitle={paymentTerm}
|
||||
trigger={
|
||||
<Button variant="Icon" size="Small">
|
||||
<IconButton theme="Black" style="Muted">
|
||||
<MaterialIcon
|
||||
icon="info"
|
||||
size={20}
|
||||
color="Icon/Default"
|
||||
/>
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
{rateTermDetails.map((termGroup) => (
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"./CodeRateCard": "./dist/components/RateCard/Code/index.js",
|
||||
"./PointsRateCard": "./dist/components/RateCard/Points/index.js",
|
||||
"./NoRateAvailableCard": "./dist/components/RateCard/NoRateAvailable/index.js",
|
||||
"./IconButton": "./dist/components/IconButton/index.js",
|
||||
"./Icons": "./dist/components/Icons/index.js",
|
||||
"./Icons/BathroomCabinetIcon": "./dist/components/Icons/Nucleo/Amenities_Facilities/bathroom-cabinet-2.js",
|
||||
"./Icons/BedHotelIcon": "./dist/components/Icons/Customised/Amenities_Facilities/BedHotel.js",
|
||||
|
||||
Reference in New Issue
Block a user