Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
This commit was merged in pull request #105.
This commit is contained in:
@@ -29,6 +29,20 @@
|
||||
height: 62px;
|
||||
}
|
||||
|
||||
/* A shown label sits 6 px under the circle, and the button grows to hold it. */
|
||||
.labelled {
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
width: auto;
|
||||
min-width: 62px;
|
||||
height: auto;
|
||||
min-height: 62px;
|
||||
color: var(--color-neutral-700);
|
||||
font-family: var(--font-body);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.face {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -57,6 +71,11 @@
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.neutral {
|
||||
background: var(--color-neutral-100);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.accent {
|
||||
background: var(--color-accent);
|
||||
color: var(--on-accent);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { declaredStyle } from '../test/cssRules.ts'
|
||||
import { hitArea, expectMinHitArea } from '../test/hitArea.ts'
|
||||
import { IconButton } from './IconButton.tsx'
|
||||
|
||||
@@ -31,4 +32,21 @@ describe('IconButton', () => {
|
||||
expectMinHitArea(button)
|
||||
expect(hitArea(button)).toEqual({ width: 62, height: 62 })
|
||||
})
|
||||
|
||||
it('shows its label as an 11 px caption under the circle, which names it', () => {
|
||||
render(
|
||||
<IconButton label="Pass" size={62} tone="neutral" showLabel>
|
||||
<svg />
|
||||
</IconButton>,
|
||||
)
|
||||
const button = screen.getByRole('button', { name: 'Pass' })
|
||||
expect(button).not.toHaveAttribute('aria-label')
|
||||
expect(button).toHaveTextContent('Pass')
|
||||
expect(getComputedStyle(button).fontSize).toBe('11px')
|
||||
expect(getComputedStyle(button).flexDirection).toBe('column')
|
||||
expectMinHitArea(button)
|
||||
const face = button.firstElementChild!
|
||||
expect(hitArea(face)).toEqual({ width: 62, height: 62 })
|
||||
expect(declaredStyle(face).background).toBe('var(--color-neutral-100)')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,32 +2,38 @@ import type { ComponentPropsWithRef } from 'react'
|
||||
import styles from './IconButton.module.css'
|
||||
|
||||
export type IconButtonSize = 40 | 62
|
||||
export type IconButtonTone = 'surface' | 'accent' | 'teal'
|
||||
export type IconButtonTone = 'surface' | 'neutral' | 'accent' | 'teal'
|
||||
|
||||
export type IconButtonProps = Omit<ComponentPropsWithRef<'button'>, 'aria-label'> & {
|
||||
// Accessible name; an icon alone never names a button.
|
||||
label: string
|
||||
size?: IconButtonSize
|
||||
tone?: IconButtonTone
|
||||
// Shows the label as an 11 px caption under the circle, which then names
|
||||
// the button.
|
||||
showLabel?: boolean
|
||||
}
|
||||
|
||||
// Round icon-only button. The 40 px size keeps a 44 px hit box (see the CSS);
|
||||
// the 62 px size is the round action button with --shadow-md.
|
||||
// Round icon button, named by its label and showing it only with showLabel.
|
||||
// The 40 px size keeps a 44 px hit box (see the CSS); the 62 px size is the
|
||||
// round action button with --shadow-md.
|
||||
export function IconButton({
|
||||
label,
|
||||
size = 40,
|
||||
tone = 'surface',
|
||||
showLabel = false,
|
||||
type = 'button',
|
||||
className,
|
||||
children,
|
||||
...rest
|
||||
}: IconButtonProps) {
|
||||
const classes = [styles.button, styles[`size${size}`], className].filter(Boolean).join(' ')
|
||||
const classes = [styles.button, styles[`size${size}`], showLabel && styles.labelled, className].filter(Boolean).join(' ')
|
||||
return (
|
||||
<button type={type} aria-label={label} className={classes} {...rest}>
|
||||
<button type={type} aria-label={showLabel ? undefined : label} className={classes} {...rest}>
|
||||
<span className={[styles.face, styles[tone]].join(' ')} aria-hidden="true">
|
||||
{children}
|
||||
</span>
|
||||
{showLabel && label}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user