feat(BOOK-113): Synced hover/focus states for buttons and added better examples to storybook
* fix(BOOK-113): Updated hover colors after blend/mix has been removed Approved-by: Christel Westerberg
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||
|
||||
import { expect } from 'storybook/test'
|
||||
|
||||
import { BackToTopButton } from '.'
|
||||
import { config as backToTopButtonConfig } from './variants'
|
||||
|
||||
const meta: Meta<typeof BackToTopButton> = {
|
||||
title: 'Components/BackToTopButton',
|
||||
component: BackToTopButton,
|
||||
argTypes: {
|
||||
onPress: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
position: {
|
||||
control: 'select',
|
||||
options: Object.keys(backToTopButtonConfig.variants.position),
|
||||
table: {
|
||||
type: {
|
||||
summary: 'string',
|
||||
detail: Object.keys(backToTopButtonConfig.variants.position).join(
|
||||
' | '
|
||||
),
|
||||
},
|
||||
defaultValue: {
|
||||
summary: backToTopButtonConfig.defaultVariants.position,
|
||||
},
|
||||
},
|
||||
},
|
||||
label: {
|
||||
control: 'text',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof BackToTopButton>
|
||||
|
||||
const globalStoryPropsInverted = {
|
||||
backgrounds: { value: 'scandicPrimaryDark' },
|
||||
}
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
onPress: () => alert('Back to top button pressed!'),
|
||||
label: 'Back to top',
|
||||
},
|
||||
play: async ({ canvas, userEvent, args }) => {
|
||||
await userEvent.click(await canvas.findByRole('button'))
|
||||
expect(args.onPress).toHaveBeenCalledTimes(1)
|
||||
},
|
||||
}
|
||||
|
||||
export const PositionLeft: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
position: 'left',
|
||||
},
|
||||
play: async ({ canvas, userEvent, args }) => {
|
||||
await userEvent.click(await canvas.findByRole('button'))
|
||||
expect(args.onPress).toHaveBeenCalledTimes(1)
|
||||
},
|
||||
}
|
||||
|
||||
export const PositionCenter: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
position: 'center',
|
||||
},
|
||||
play: async ({ canvas, userEvent, args }) => {
|
||||
await userEvent.click(await canvas.findByRole('button'))
|
||||
expect(args.onPress).toHaveBeenCalledTimes(1)
|
||||
},
|
||||
}
|
||||
|
||||
export const PositionRight: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
position: 'right',
|
||||
},
|
||||
play: async ({ canvas, userEvent, args }) => {
|
||||
await userEvent.click(await canvas.findByRole('button'))
|
||||
expect(args.onPress).toHaveBeenCalledTimes(1)
|
||||
},
|
||||
}
|
||||
|
||||
export const OnDarkBackground: Story = {
|
||||
globals: globalStoryPropsInverted,
|
||||
args: {
|
||||
onPress: () => alert('Back to top button pressed!'),
|
||||
label: 'Back to top',
|
||||
},
|
||||
play: async ({ canvas, userEvent, args }) => {
|
||||
await userEvent.click(await canvas.findByRole('button'))
|
||||
expect(args.onPress).toHaveBeenCalledTimes(1)
|
||||
},
|
||||
}
|
||||
@@ -1,24 +1,43 @@
|
||||
.backToTopButton {
|
||||
display: inline-flex;
|
||||
padding: var(--Space-x1);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--Space-x05);
|
||||
width: max-content;
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
|
||||
background-color: var(--Component-Button-Brand-Secondary-Fill-Inverted);
|
||||
border: 2px solid var(--Component-Button-Brand-Secondary-Border-Default);
|
||||
border-radius: var(--Corner-radius-Rounded);
|
||||
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
||||
border-radius: var(--Corner-radius-rounded);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--Space-x05);
|
||||
|
||||
padding: var(--Space-x1);
|
||||
width: max-content;
|
||||
background-color: var(--Component-Button-Brand-Secondary-Fill-Inverted);
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
|
||||
border: 2px solid var(--Component-Button-Brand-Secondary-Border-Default);
|
||||
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
bottom: var(--Space-x2);
|
||||
|
||||
&:hover {
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Inverted);
|
||||
background-color: var(
|
||||
--Component-Button-Brand-Secondary-Fill-Hover-Inverted
|
||||
);
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
background-color: var(
|
||||
--Component-Button-Brand-Secondary-Fill-Hover-Inverted
|
||||
);
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Inverted);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--Border-Interactive-Focus);
|
||||
outline-offset: 2px;
|
||||
|
||||
/* This button is able to be on top of dark background colors,
|
||||
so we need to create an illusion that it also has an inverted border on focus */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -4px;
|
||||
border: 2px solid var(--Border-Inverted);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { type Button, Button as ButtonRAC } from 'react-aria-components'
|
||||
import { Button as ButtonRAC } from 'react-aria-components'
|
||||
|
||||
import { MaterialIcon } from '../Icons/MaterialIcon'
|
||||
import { Typography } from '../Typography'
|
||||
|
||||
import { backToTopButtonVariants } from './variants'
|
||||
import { variants } from './variants'
|
||||
|
||||
import styles from './backToTopButton.module.css'
|
||||
|
||||
@@ -13,8 +13,8 @@ import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ComponentProps } from 'react'
|
||||
|
||||
interface BackToTopButtonProps
|
||||
extends ComponentProps<typeof Button>,
|
||||
VariantProps<typeof backToTopButtonVariants> {
|
||||
extends ComponentProps<typeof ButtonRAC>,
|
||||
VariantProps<typeof variants> {
|
||||
label: string
|
||||
}
|
||||
|
||||
@@ -23,13 +23,11 @@ export function BackToTopButton({
|
||||
label,
|
||||
...props
|
||||
}: BackToTopButtonProps) {
|
||||
const classNames = variants({ position })
|
||||
|
||||
return (
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<ButtonRAC
|
||||
className={backToTopButtonVariants({ position })}
|
||||
aria-label={label}
|
||||
{...props}
|
||||
>
|
||||
<ButtonRAC className={classNames} aria-label={label} {...props}>
|
||||
<MaterialIcon icon="arrow_upward" color="CurrentColor" size={20} />
|
||||
<span className={styles.text}>{label}</span>
|
||||
</ButtonRAC>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cva } from 'class-variance-authority'
|
||||
|
||||
import styles from './backToTopButton.module.css'
|
||||
|
||||
export const backToTopButtonVariants = cva(styles.backToTopButton, {
|
||||
export const config = {
|
||||
variants: {
|
||||
position: {
|
||||
left: styles.left,
|
||||
@@ -13,4 +13,6 @@ export const backToTopButtonVariants = cva(styles.backToTopButton, {
|
||||
defaultVariants: {
|
||||
position: 'right',
|
||||
},
|
||||
})
|
||||
} as const
|
||||
|
||||
export const variants = cva(styles.backToTopButton, config)
|
||||
|
||||
Reference in New Issue
Block a user