diff --git a/documentation/storybook.md b/documentation/storybook.md index 37bc245ccf..c98f275901 100644 --- a/documentation/storybook.md +++ b/documentation/storybook.md @@ -22,9 +22,11 @@ We write our documentation in Dutch. ## Best practices for controls -1. Use radio buttons rather than a dropdown for props with 5 options or less. +1. For props offering five options or less, use radio buttons rather than a select. This makes it easier to compare the options. It saves the user a click to select each option and shows everything up front. +2. Don’t use inline radios. + Their options appear rather small, making them difficult to target with a pointing device. More to follow. diff --git a/packages/css/src/components/badge/README.md b/packages/css/src/components/badge/README.md new file mode 100644 index 0000000000..740d4831b0 --- /dev/null +++ b/packages/css/src/components/badge/README.md @@ -0,0 +1,10 @@ +# Badge + +A prominently coloured box containing 1 or 2 words. +Guides the user in taking a specific action or describes its surrounding content. + +## Design + +The badge can contain a short text or a number. +The default background colour is dark green. +Suggestions on when to use the other colours will follow soon. diff --git a/packages/css/src/components/badge/badge.scss b/packages/css/src/components/badge/badge.scss new file mode 100644 index 0000000000..1c20a45492 --- /dev/null +++ b/packages/css/src/components/badge/badge.scss @@ -0,0 +1,58 @@ +/** + * @license EUPL-1.2+ + * Copyright (c) 2024 Gemeente Amsterdam + */ + +.amsterdam-badge { + display: inline-block; + font-family: var(--amsterdam-badge-font-family); + font-size: var(--amsterdam-badge-spacious-font-size); + font-weight: var(--amsterdam-badge-font-weight); + line-height: var(--amsterdam-badge-spacious-line-height); + padding-inline: var(--amsterdam-badge-padding-inline); + + .amsterdam-theme--compact & { + font-size: var(--amsterdam-badge-compact-font-size); + line-height: var(--amsterdam-badge-compact-line-height); + } +} + +.amsterdam-badge--blue { + background-color: var(--amsterdam-badge-blue-background-color); + color: var(--amsterdam-badge-blue-color); +} + +.amsterdam-badge--dark-blue { + background-color: var(--amsterdam-badge-dark-blue-background-color); + color: var(--amsterdam-badge-dark-blue-color); +} + +.amsterdam-badge--dark-green { + background-color: var(--amsterdam-badge-dark-green-background-color); + color: var(--amsterdam-badge-dark-green-color); +} + +.amsterdam-badge--green { + background-color: var(--amsterdam-badge-green-background-color); + color: var(--amsterdam-badge-green-color); +} + +.amsterdam-badge--magenta { + background-color: var(--amsterdam-badge-magenta-background-color); + color: var(--amsterdam-badge-magenta-color); +} + +.amsterdam-badge--orange { + background-color: var(--amsterdam-badge-orange-background-color); + color: var(--amsterdam-badge-orange-color); +} + +.amsterdam-badge--purple { + background-color: var(--amsterdam-badge-purple-background-color); + color: var(--amsterdam-badge-purple-color); +} + +.amsterdam-badge--yellow { + background-color: var(--amsterdam-badge-yellow-background-color); + color: var(--amsterdam-badge-yellow-color); +} diff --git a/packages/css/src/components/index.scss b/packages/css/src/components/index.scss index f4374cd68d..247c622e19 100644 --- a/packages/css/src/components/index.scss +++ b/packages/css/src/components/index.scss @@ -4,6 +4,7 @@ */ /* Append here */ +@import "./badge/badge"; @import "./table/table"; @import "./mega-menu/mega-menu"; @import "./icon-button/icon-button"; diff --git a/packages/react/package.json b/packages/react/package.json index 072de123b3..274851c20d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -63,6 +63,7 @@ "react-dom": "18.2.0", "rollup": "4.9.4", "rollup-plugin-delete": "2.0.0", + "rollup-plugin-dts": "6.1.0", "rollup-plugin-filesize": "10.0.0", "rollup-plugin-node-externals": "6.1.2", "rollup-plugin-node-polyfills": "0.2.1", diff --git a/packages/react/rollup.config.mjs b/packages/react/rollup.config.mjs index b8dd0d2570..fa3a4fdee0 100644 --- a/packages/react/rollup.config.mjs +++ b/packages/react/rollup.config.mjs @@ -7,6 +7,8 @@ import nodeExternal from 'rollup-plugin-node-externals' import nodePolyfills from 'rollup-plugin-node-polyfills' import peerDepsExternal from 'rollup-plugin-peer-deps-external' import typescript from 'rollup-plugin-typescript2' +import dts from 'rollup-plugin-dts' +import del from 'rollup-plugin-delete' const packageJson = JSON.parse(readFileSync('./package.json', 'utf8')) @@ -46,7 +48,7 @@ export default [ include: /node_modules/, }), nodePolyfills(), - typescript({ includeDependencies: false }), + typescript({ includeDependencies: false, useTsconfigDeclarationDir: true }), babel({ presets: ['@babel/preset-react'], babelHelpers: 'runtime', @@ -58,4 +60,15 @@ export default [ filesize(), ], }, + { + input: './dist/dts/index.d.ts', + output: [{ file: 'dist/index.d.ts', format: 'es' }], + plugins: [ + dts(), + del({ + targets: 'dist/dts', + hook: 'buildEnd', + }), + ], + }, ] diff --git a/packages/react/src/Badge/Badge.test.tsx b/packages/react/src/Badge/Badge.test.tsx new file mode 100644 index 0000000000..5d029e2d57 --- /dev/null +++ b/packages/react/src/Badge/Badge.test.tsx @@ -0,0 +1,67 @@ +import { render } from '@testing-library/react' +import { createRef } from 'react' +import { Badge, badgeColors } from './Badge' +import '@testing-library/jest-dom' + +describe('Badge', () => { + it('renders', () => { + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(component).toBeInTheDocument() + expect(component).toBeVisible() + }) + + it('renders a design system BEM class name', () => { + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(component).toHaveClass('amsterdam-badge') + }) + + it('renders an additional class name', () => { + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(component).toHaveClass('amsterdam-badge extra') + }) + + it('supports ForwardRef in React', () => { + const ref = createRef() + + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(ref.current).toBe(component) + }) + + it('renders with a number label', () => { + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(component).toHaveTextContent('1') + }) + + it('renders with default color', () => { + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(component).toHaveClass('amsterdam-badge--dark-green') + }) + + badgeColors.map((color) => + it(`renders with ${color} color`, () => { + const { container } = render() + + const component = container.querySelector(':only-child') + + expect(component).toHaveClass(`amsterdam-badge--${color}`) + }), + ) +}) diff --git a/packages/react/src/Badge/Badge.tsx b/packages/react/src/Badge/Badge.tsx new file mode 100644 index 0000000000..56a1571aaa --- /dev/null +++ b/packages/react/src/Badge/Badge.tsx @@ -0,0 +1,36 @@ +/** + * @license EUPL-1.2+ + * Copyright (c) 2024 Gemeente Amsterdam + */ + +import clsx from 'clsx' +import { forwardRef } from 'react' +import type { ForwardedRef, HTMLAttributes } from 'react' + +export const badgeColors = [ + 'blue', + 'dark-blue', + 'dark-green', + 'green', + 'magenta', + 'orange', + 'purple', + 'yellow', +] as const + +type BadgeColor = (typeof badgeColors)[number] + +export type BadgeProps = { + color?: BadgeColor + label: string | number +} & HTMLAttributes + +export const Badge = forwardRef( + ({ label, className, color = 'dark-green', ...restProps }: BadgeProps, ref: ForwardedRef) => ( + + {label} + + ), +) + +Badge.displayName = 'Badge' diff --git a/packages/react/src/Badge/README.md b/packages/react/src/Badge/README.md new file mode 100644 index 0000000000..b34e77c7c1 --- /dev/null +++ b/packages/react/src/Badge/README.md @@ -0,0 +1,3 @@ +# React Badge component + +[Badge documentation](../../../css/src/badge/README.md) diff --git a/packages/react/src/Badge/index.ts b/packages/react/src/Badge/index.ts new file mode 100644 index 0000000000..292a59dbf2 --- /dev/null +++ b/packages/react/src/Badge/index.ts @@ -0,0 +1,2 @@ +export { Badge } from './Badge' +export type { BadgeProps } from './Badge' diff --git a/packages/react/src/Breadcrumb/Breadcrumb.test.tsx b/packages/react/src/Breadcrumb/Breadcrumb.test.tsx index d5420454df..591dc45b36 100644 --- a/packages/react/src/Breadcrumb/Breadcrumb.test.tsx +++ b/packages/react/src/Breadcrumb/Breadcrumb.test.tsx @@ -4,6 +4,25 @@ import { Breadcrumb } from './Breadcrumb' import '@testing-library/jest-dom' describe('Breadcrumb', () => { + it('renders', () => { + render() + const component = screen.getByRole('navigation') + expect(component).toBeInTheDocument() + expect(component).toBeVisible() + }) + + it('renders a design system BEM class name', () => { + render() + const component = screen.getByRole('navigation') + expect(component).toHaveClass('amsterdam-breadcrumb') + }) + + it('renders an additional class name', () => { + render() + const component = screen.getByRole('navigation') + expect(component).toHaveClass('amsterdam-breadcrumb extra') + }) + it('renders Breadcrumb component with children', () => { const breadcrumbItems = [ { label: 'Item 1', href: '/item-1' }, diff --git a/packages/react/src/Breadcrumb/Breadcrumb.tsx b/packages/react/src/Breadcrumb/Breadcrumb.tsx index 814b4470bc..eb50c94944 100644 --- a/packages/react/src/Breadcrumb/Breadcrumb.tsx +++ b/packages/react/src/Breadcrumb/Breadcrumb.tsx @@ -3,8 +3,10 @@ * Copyright (c) 2023 Gemeente Amsterdam */ +import { clsx } from 'clsx' import { forwardRef } from 'react' import type { ForwardedRef, ForwardRefExoticComponent, HTMLAttributes, PropsWithChildren, RefAttributes } from 'react' +import { BreadcrumbItem } from './BreadcrumbItem' export type BreadcrumbProps = PropsWithChildren> @@ -12,31 +14,15 @@ interface BreadcrumbComponent extends ForwardRefExoticComponent) => { - return ( -