From 872d0724e4af59745bad552bfb985f3f318ba849 Mon Sep 17 00:00:00 2001 From: Ali Amori Kadhim Date: Mon, 2 Sep 2024 13:02:28 +0200 Subject: [PATCH] wip --- components/combobox/src/_mixin.scss | 48 +++++ components/combobox/src/index.scss | 12 ++ components/combobox/src/tokens.json | 162 ++++++++++++++ components/listbox/src/_mixin.scss | 1 + .../component-library-react/jest.config.mjs | 2 +- packages/component-library-react/package.json | 11 +- .../packages/combobox-react/jest.config.mjs | 3 + .../packages/combobox-react/package.json | 49 +++++ .../packages/combobox-react/rollup.config.mjs | 3 + .../packages/combobox-react/src/css.tsx | 9 + .../combobox-react/src/index.test.tsx | 164 ++++++++++++++ .../packages/combobox-react/src/index.tsx | 164 ++++++++++++++ .../packages/combobox-react/tsconfig.json | 9 + .../combobox-react/tsconfig.test.json | 8 + .../packages/form-field-react/jest.config.mjs | 3 + .../packages/form-field-react/package.json | 41 ++++ .../form-field-react/rollup.config.mjs | 3 + .../packages/form-field-react/src/css.tsx | 9 + .../form-field-react/src/index.test.tsx} | 2 +- .../packages/form-field-react/src/index.tsx | 39 ++++ .../packages/form-field-react/tsconfig.json | 9 + .../form-field-react/tsconfig.test.json | 8 + .../packages/form-label-react/jest.config.mjs | 3 + .../packages/form-label-react/package.json | 41 ++++ .../form-label-react/rollup.config.mjs | 3 + .../packages/form-label-react/src/css.tsx | 9 + .../form-label-react/src/index.test.tsx} | 2 +- .../packages/form-label-react/src/index.tsx | 31 +++ .../packages/form-label-react/tsconfig.json | 9 + .../form-label-react/tsconfig.test.json | 8 + .../packages/listbox-react/jest.config.mjs | 3 + .../packages/listbox-react/package.json | 41 ++++ .../packages/listbox-react/rollup.config.mjs | 3 + .../packages/listbox-react/src/css.tsx | 9 + .../listbox-react/src/index.test.tsx} | 2 +- .../packages/listbox-react/src/index.tsx | 113 ++++++++++ .../packages/listbox-react/tsconfig.json | 9 + .../packages/listbox-react/tsconfig.test.json | 8 + .../packages/textbox-react/jest.config.mjs | 3 + .../packages/textbox-react/package.json | 41 ++++ .../packages/textbox-react/rollup.config.mjs | 3 + .../packages/textbox-react/src/css.tsx | 9 + .../textbox-react/src/index.test.tsx} | 2 +- .../packages/textbox-react/src/index.tsx | 63 ++++++ .../packages/textbox-react/tsconfig.json | 9 + .../packages/textbox-react/tsconfig.test.json | 8 + .../src/Combobox.test.tsx | 33 --- .../component-library-react/src/Combobox.tsx | 88 ++++---- .../component-library-react/src/FormField.tsx | 41 +--- .../component-library-react/src/FormLabel.tsx | 33 +-- .../component-library-react/src/Listbox.tsx | 110 +--------- .../component-library-react/src/Textbox.tsx | 65 +----- .../src/css-module/Combobox.tsx | 4 +- .../src/css-module/FormField.tsx | 4 +- .../src/css-module/FormLabel.tsx | 4 +- .../src/css-module/Listbox.tsx | 4 +- .../src/css-module/Textbox.tsx | 4 +- .../src/css-module/index.ts | 10 +- packages/component-library-react/src/index.ts | 6 +- packages/storybook-css/src/Combobox.tsx | 4 +- packages/storybook-react/package.json | 2 + .../src/stories/Combobox.stories.tsx | 76 +++++++ .../src/stories/Textbox.stories.tsx | 2 +- pnpm-lock.yaml | 204 +++++++++++++++--- 64 files changed, 1516 insertions(+), 369 deletions(-) create mode 100644 components/combobox/src/tokens.json create mode 100644 packages/component-library-react/packages/combobox-react/jest.config.mjs create mode 100644 packages/component-library-react/packages/combobox-react/package.json create mode 100644 packages/component-library-react/packages/combobox-react/rollup.config.mjs create mode 100644 packages/component-library-react/packages/combobox-react/src/css.tsx create mode 100644 packages/component-library-react/packages/combobox-react/src/index.test.tsx create mode 100644 packages/component-library-react/packages/combobox-react/src/index.tsx create mode 100644 packages/component-library-react/packages/combobox-react/tsconfig.json create mode 100644 packages/component-library-react/packages/combobox-react/tsconfig.test.json create mode 100644 packages/component-library-react/packages/form-field-react/jest.config.mjs create mode 100644 packages/component-library-react/packages/form-field-react/package.json create mode 100644 packages/component-library-react/packages/form-field-react/rollup.config.mjs create mode 100644 packages/component-library-react/packages/form-field-react/src/css.tsx rename packages/component-library-react/{src/FormField.test.tsx => packages/form-field-react/src/index.test.tsx} (98%) create mode 100644 packages/component-library-react/packages/form-field-react/src/index.tsx create mode 100644 packages/component-library-react/packages/form-field-react/tsconfig.json create mode 100644 packages/component-library-react/packages/form-field-react/tsconfig.test.json create mode 100644 packages/component-library-react/packages/form-label-react/jest.config.mjs create mode 100644 packages/component-library-react/packages/form-label-react/package.json create mode 100644 packages/component-library-react/packages/form-label-react/rollup.config.mjs create mode 100644 packages/component-library-react/packages/form-label-react/src/css.tsx rename packages/component-library-react/{src/FormLabel.test.tsx => packages/form-label-react/src/index.test.tsx} (99%) create mode 100644 packages/component-library-react/packages/form-label-react/src/index.tsx create mode 100644 packages/component-library-react/packages/form-label-react/tsconfig.json create mode 100644 packages/component-library-react/packages/form-label-react/tsconfig.test.json create mode 100644 packages/component-library-react/packages/listbox-react/jest.config.mjs create mode 100644 packages/component-library-react/packages/listbox-react/package.json create mode 100644 packages/component-library-react/packages/listbox-react/rollup.config.mjs create mode 100644 packages/component-library-react/packages/listbox-react/src/css.tsx rename packages/component-library-react/{src/Listbox.test.tsx => packages/listbox-react/src/index.test.tsx} (98%) create mode 100644 packages/component-library-react/packages/listbox-react/src/index.tsx create mode 100644 packages/component-library-react/packages/listbox-react/tsconfig.json create mode 100644 packages/component-library-react/packages/listbox-react/tsconfig.test.json create mode 100644 packages/component-library-react/packages/textbox-react/jest.config.mjs create mode 100644 packages/component-library-react/packages/textbox-react/package.json create mode 100644 packages/component-library-react/packages/textbox-react/rollup.config.mjs create mode 100644 packages/component-library-react/packages/textbox-react/src/css.tsx rename packages/component-library-react/{src/Textbox.test.tsx => packages/textbox-react/src/index.test.tsx} (99%) create mode 100644 packages/component-library-react/packages/textbox-react/src/index.tsx create mode 100644 packages/component-library-react/packages/textbox-react/tsconfig.json create mode 100644 packages/component-library-react/packages/textbox-react/tsconfig.test.json delete mode 100644 packages/component-library-react/src/Combobox.test.tsx create mode 100644 packages/storybook-react/src/stories/Combobox.stories.tsx diff --git a/components/combobox/src/_mixin.scss b/components/combobox/src/_mixin.scss index 4f6d39756be..fea39683df2 100644 --- a/components/combobox/src/_mixin.scss +++ b/components/combobox/src/_mixin.scss @@ -40,3 +40,51 @@ @mixin utrecht-combobox__popover--hidden { display: none !important; } + +@mixin utrecht-combobox__textbox-container { + align-items: center; + box-sizing: border-box; + display: flex; + inline-size: var(--utrecht-listbox-inline-size); + max-inline-size: 100%; + position: relative; +} +@mixin utrecht-combobox__toggle-button { + --utrecht-button-subtle-background-color: var(--utrecht-combobox-toggle-button-background-color, transparent); + --utrecht-button-subtle-color: var(--utrecht-combobox-toggle-button-background-color, currentColor); + --utrecht-button-subtle-hover-background-color: var( + --utrecht-combobox-toggle-button-hover-background-color, + transparent + ); + --utrecht-button-subtle-hover-border-color: transparent; + --utrecht-button-subtle-hover-color: var(--utrecht-combobox-toggle-button-hover-color, currentColor); + --utrecht-button-hover-scale: none; + --utrecht-button-subtle-active-border-color: transparent; + --utrecht-button-subtle-active-background-color: var( + --utrecht-combobox-toggle-button-active-background-color, + transparent + ); + --utrecht-button-subtle-active-color: var(--utrecht-combobox-toggle-button-active-color, currentColor); + --utrecht-button-subtle-focus-border-color: transparent; + --utrecht-button-subtle-focus-background-color: var( + --utrecht-combobox-toggle-button-focus-background-color, + transparent + ); + --utrecht-button-subtle-focus-color: var(--utrecht-combobox-toggle-button-focus-color, currentColor); + --utrecht-button-subtle-pressed-background-color: var( + --utrecht-combobox-toggle-button-pressed-background-color, + transparent + ); + --utrecht-button-subtle-pressed-border-color: transparent; + --utrecht-button-subtle-pressed-color: var(--utrecht-combobox-toggle-button-pressed-color); + + inset-block-start: 50%; + inset-inline-end: 0; + position: absolute; + transform: translateY(-50%); +} + +@mixin utrecht-combobox__icon { + block-size: var(--utrecht-combobox-icon-size, 32px); + inline-size: var(--utrecht-combobox-icon-size, 32px); +} diff --git a/components/combobox/src/index.scss b/components/combobox/src/index.scss index c266acc6513..82f337c4db1 100644 --- a/components/combobox/src/index.scss +++ b/components/combobox/src/index.scss @@ -20,3 +20,15 @@ .utrecht-combobox__popover--hidden { @include utrecht-combobox__popover--hidden; } + +.utrecht-combobox__textbox-container { + @include utrecht-combobox__textbox-container; +} + +.utrecht-combobox__toggle-button { + @include utrecht-combobox__toggle-button; +} + +.utrecht-combobox__icon { + @include utrecht-combobox__icon; +} diff --git a/components/combobox/src/tokens.json b/components/combobox/src/tokens.json new file mode 100644 index 00000000000..b6dbd119a0e --- /dev/null +++ b/components/combobox/src/tokens.json @@ -0,0 +1,162 @@ +{ + "utrecht": { + "combobox": { + "max-inline-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "spacing" + }, + "border-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "input": { + "container": { + "inline-size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "spacing" + } + } + }, + "toggle-button": { + "background-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "hover": { + "background-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + } + }, + "active": { + "background-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + } + }, + "focus": { + "background-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + } + }, + "pressed": { + "background-color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + }, + "color": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "color" + } + } + }, + "icon": { + "size": { + "$extensions": { + "nl.nldesignsystem.css.property": { + "syntax": "", + "inherits": true + }, + "nl.nldesignsystem.figma.supports-token": true + }, + "type": "spacing" + } + } + } + } +} diff --git a/components/listbox/src/_mixin.scss b/components/listbox/src/_mixin.scss index 777ed41b718..3aaa10b6012 100644 --- a/components/listbox/src/_mixin.scss +++ b/components/listbox/src/_mixin.scss @@ -31,6 +31,7 @@ flex-direction: column; inline-size: var(--utrecht-listbox-inline-size, var(--utrecht-form-input-max-inline-size)); max-block-size: var(--utrecht-listbox-max-block-size); + max-inline-size: 100%; min-block-size: 1em; overflow-block: auto; overflow-y: auto; diff --git a/packages/component-library-react/jest.config.mjs b/packages/component-library-react/jest.config.mjs index 85301222887..f8cd7c2c028 100644 --- a/packages/component-library-react/jest.config.mjs +++ b/packages/component-library-react/jest.config.mjs @@ -15,7 +15,7 @@ const customJestConfig = { testPathIgnorePatterns: ['/dist/'], // transformIgnorePatterns: ['node_modules/(?!@utrecht/web-component-library-react)'], moduleNameMapper: { - '^@utrecht/(.*)$': '/packages/$1/src/', + // '^@utrecht/(.*)$': '/packages/$1/src/', }, }; diff --git a/packages/component-library-react/package.json b/packages/component-library-react/package.json index 2ce0f9d9220..1e29b8ed47f 100644 --- a/packages/component-library-react/package.json +++ b/packages/component-library-react/package.json @@ -69,16 +69,16 @@ "@utrecht/badge-status-css": "workspace:*", "@utrecht/blockquote-css": "workspace:*", "@utrecht/breadcrumb-nav-css": "workspace:*", - "@utrecht/button-react": "workspace:*", "@utrecht/button-group-css": "workspace:*", "@utrecht/button-link-css": "workspace:*", + "@utrecht/button-react": "workspace:*", "@utrecht/calendar-react": "workspace:*", "@utrecht/checkbox-css": "workspace:*", "@utrecht/code-block-css": "workspace:*", "@utrecht/code-css": "workspace:*", "@utrecht/color-sample-css": "workspace:*", "@utrecht/column-layout-css": "workspace:*", - "@utrecht/combobox-css": "workspace:*", + "@utrecht/combobox-react": "workspace:*", "@utrecht/currency-data-css": "workspace:*", "@utrecht/custom-checkbox-css": "workspace:*", "@utrecht/data-list-css": "workspace:*", @@ -89,11 +89,11 @@ "@utrecht/emphasis-css": "workspace:*", "@utrecht/figure-css": "workspace:*", "@utrecht/focus-ring-css": "workspace:*", - "@utrecht/form-field-css": "workspace:*", + "@utrecht/form-field-react": "workspace:*", "@utrecht/form-field-description-css": "workspace:*", "@utrecht/form-field-error-message-css": "workspace:*", "@utrecht/form-fieldset-css": "workspace:*", - "@utrecht/form-label-css": "workspace:*", + "@utrecht/form-label-react": "workspace:*", "@utrecht/form-toggle-css": "workspace:*", "@utrecht/heading-1-css": "workspace:*", "@utrecht/heading-2-css": "workspace:*", @@ -113,6 +113,7 @@ "@utrecht/link-social-css": "workspace:*", "@utrecht/list-social-css": "workspace:*", "@utrecht/listbox-css": "workspace:*", + "@utrecht/listbox-react": "workspace:*", "@utrecht/logo-button-css": "workspace:*", "@utrecht/logo-css": "workspace:*", "@utrecht/logo-image-css": "workspace:*", @@ -142,7 +143,7 @@ "@utrecht/surface-css": "workspace:*", "@utrecht/table-css": "workspace:*", "@utrecht/textarea-css": "workspace:*", - "@utrecht/textbox-css": "workspace:*", + "@utrecht/textbox-react": "workspace:*", "@utrecht/top-task-link-css": "workspace:*", "@utrecht/top-task-nav-css": "workspace:*", "@utrecht/unordered-list-css": "workspace:*", diff --git a/packages/component-library-react/packages/combobox-react/jest.config.mjs b/packages/component-library-react/packages/combobox-react/jest.config.mjs new file mode 100644 index 00000000000..e26eb68cfd6 --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/jest.config.mjs @@ -0,0 +1,3 @@ +import { createJestConfig } from '../../jest-component.config.mjs'; + +export default createJestConfig(import.meta.url); diff --git a/packages/component-library-react/packages/combobox-react/package.json b/packages/component-library-react/packages/combobox-react/package.json new file mode 100644 index 00000000000..e3b0990c29f --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/package.json @@ -0,0 +1,49 @@ +{ + "version": "1.0.0", + "author": "Community for NL Design System", + "description": "Button component for the Municipality of Utrecht based on the NL Design System architecture", + "license": "EUPL-1.2", + "name": "@utrecht/combobox-react", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "sideEffects": false, + "scripts": { + "build": "rollup -c ./rollup.config.mjs", + "clean": "rimraf dist", + "test": "mkdir -p pages && NODE_OPTIONS=--experimental-vm-modules jest --coverage --verbose" + }, + "devDependencies": { + "@utrecht/combobox-css": "workspace:*", + "@utrecht/textbox-react": "workspace:*", + "@utrecht/listbox-react": "workspace:*", + "@utrecht/form-label-react": "workspace:*", + "@utrecht/form-field-react": "workspace:*", + "@utrecht/button-react": "workspace:*", + "jest": "29.7.0", + "rollup": "4.18.0" + }, + "keywords": [ + "nl-design-system" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git+ssh", + "url": "git@github.com:nl-design-system/utrecht.git", + "directory": "packages/component-library-react/packages/combobox-react" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.6", + "react": "18", + "react-dom": "18" + }, + "dependencies": { + "downshift": "9.0.8" + } +} diff --git a/packages/component-library-react/packages/combobox-react/rollup.config.mjs b/packages/component-library-react/packages/combobox-react/rollup.config.mjs new file mode 100644 index 00000000000..a97d32a0f9c --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/rollup.config.mjs @@ -0,0 +1,3 @@ +import { createComponentPackageConfig } from '../../rollup-component-package.mjs'; + +export default createComponentPackageConfig(import.meta.url); diff --git a/packages/component-library-react/packages/combobox-react/src/css.tsx b/packages/component-library-react/packages/combobox-react/src/css.tsx new file mode 100644 index 00000000000..3657d5ab8ed --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/src/css.tsx @@ -0,0 +1,9 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2025 Frameless B.V. + * Copyright (c) 2021-2025 Gemeente Utrecht + */ + +import '@utrecht/combobox-css/src/index.scss'; + +export * from './index'; diff --git a/packages/component-library-react/packages/combobox-react/src/index.test.tsx b/packages/component-library-react/packages/combobox-react/src/index.test.tsx new file mode 100644 index 00000000000..b2b203143b9 --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/src/index.test.tsx @@ -0,0 +1,164 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { createRef } from 'react'; +import React from 'react'; +import { Combobox } from './index'; + +const comboboxList = ['Option 1', 'Option 2', 'Option 3']; + +describe('Combobox', () => { + it('renders a div HTML element', () => { + const { container } = render(); + + const combobox = container.querySelector('div:only-child'); + + expect(combobox).toBeInTheDocument(); + expect(combobox).toBeVisible(); + }); + + it('renders a design system BEM block class name', () => { + const { container } = render(); + + const combobox = container.querySelector(':only-child'); + + expect(combobox).toHaveClass('utrecht-combobox'); + }); + + it('supports ForwardRef in React', () => { + const ref = createRef(); + + const { container } = render(); + + const combobox = container.querySelector(':only-child'); + + expect(ref.current).toBe(combobox); + }); + it('renders a custom CSS class', () => { + const { container } = render(); + + const combobox = container.querySelector(':only-child'); + + expect(combobox).toHaveClass('custom-class'); + }); + describe('Accessibility', () => { + it('renders a role of combobox', () => { + const { container } = render(); + const comboboxTextbox = container.querySelector('input'); + expect(comboboxTextbox).toHaveAttribute('role', 'combobox'); + }); + it('renders a role of listbox', () => { + const { container, debug } = render(); + const combobox = screen.getByRole('combobox'); + fireEvent.click(combobox); + const comboboxListbox = container.querySelector('.utrecht-listbox'); + expect(comboboxListbox).toHaveAttribute('role', 'listbox'); + }); + it('renders a role of option', () => { + const { container } = render(); + const combobox = screen.getByRole('combobox'); + fireEvent.click(combobox); + const listboxItems = container.querySelectorAll('.utrecht-listbox__option'); + listboxItems.forEach((item) => expect(item).toHaveAttribute('role', 'option')); + }); + }); + describe('options', () => { + it('renders a list of options', () => { + const { getByText } = render(); + const combobox = screen.getByRole('combobox'); + fireEvent.click(combobox); + comboboxList.forEach((option) => { + expect(getByText(option)).toBeInTheDocument(); + }); + }); + }); + describe('label', () => { + it('renders a label', () => { + const { getByText } = render(); + expect(getByText('Combobox Label')).toBeInTheDocument(); + }); + }); + describe('inputProps', () => { + it('renders inputProps', () => { + const { getByPlaceholderText } = render( + , + ); + expect(getByPlaceholderText('Search')).toBeInTheDocument(); + }); + it('renders aria-label', () => { + const { container } = render( + , + ); + const comboboxTextbox = container.querySelector('input'); + expect(comboboxTextbox).toHaveAttribute('aria-label', 'Search'); + }); + it('renders a disabled attribute', () => { + const { container } = render( + , + ); + const comboboxTextbox = container.querySelector('input'); + expect(comboboxTextbox).toBeDisabled(); + }); + it('renders a design system BEM block class name', () => { + const { container } = render(); + const comboboxTextbox = container.querySelector('input'); + expect(comboboxTextbox).toHaveClass('utrecht-combobox__textbox'); + expect(comboboxTextbox).toHaveClass('utrecht-textbox--html-input'); + expect(comboboxTextbox).toHaveClass('utrecht-textbox'); + }); + it('renders a custom CSS class', () => { + const { container } = render( + , + ); + const comboboxTextbox = container.querySelector('input'); + + expect(comboboxTextbox).toHaveClass('custom-class'); + expect(comboboxTextbox).toHaveClass('utrecht-combobox__textbox'); + expect(comboboxTextbox).toHaveClass('utrecht-textbox--html-input'); + expect(comboboxTextbox).toHaveClass('utrecht-textbox'); + }); + it('renders a required attribute', () => { + const { container } = render( + , + ); + const comboboxTextbox = container.querySelector('input'); + expect(comboboxTextbox).toBeRequired(); + }); + it('renders an aria-required attribute', () => { + const { container } = render( + , + ); + const comboboxTextbox = container.querySelector('input'); + expect(comboboxTextbox).toHaveAttribute('aria-required', 'true'); + }); + }); + describe('onComboboxChange', () => { + it('calls onComboboxChange when the input value changes', () => { + const onComboboxChange = jest.fn(); + const { container } = render( + , + ); + const comboboxTextbox = container.querySelector('input'); + fireEvent.change(comboboxTextbox as HTMLInputElement, { target: { value: 'Option 1' } }); + expect(onComboboxChange).toHaveBeenCalledWith('Option 1'); + expect(onComboboxChange).toHaveBeenCalledTimes(1); + }); + }); + describe('renderOptions', () => { + it('renders custom options', () => { + render( + {option}} + />, + ); + const combobox = screen.getByRole('combobox'); + fireEvent.click(combobox); + const listboxItem = screen.getAllByRole('option'); + const customOption = listboxItem[0].querySelector('.custom-option'); + + expect(customOption).toBeInTheDocument(); + expect(listboxItem[0]).toHaveTextContent('Option 1'); + }); + }); +}); diff --git a/packages/component-library-react/packages/combobox-react/src/index.tsx b/packages/component-library-react/packages/combobox-react/src/index.tsx new file mode 100644 index 00000000000..3355f9933eb --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/src/index.tsx @@ -0,0 +1,164 @@ +import type { ButtonProps } from '@utrecht/button-react'; +import { Button } from '@utrecht/button-react'; +import { FormField } from '@utrecht/form-field-react'; +import { FormLabel } from '@utrecht/form-label-react'; +import { Listbox, ListboxOption } from '@utrecht/listbox-react'; +import type { TextboxProps } from '@utrecht/textbox-react'; +import { Textbox } from '@utrecht/textbox-react'; +import clsx from 'clsx'; +import { useCombobox } from 'downshift'; +import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode, useId, useState } from 'react'; +export interface ComboboxTextboxProps { + label: string; + inputProps?: TextboxProps; + labelProps?: HTMLAttributes; +} + +export const ComboboxTextbox = forwardRef( + ( + { label, inputProps, labelProps, children, ...restProps }: PropsWithChildren, + ref: ForwardedRef, + ) => { + const inputID = useId(); + + return ( + +

+ {label && ( + + {label} + + )} +

+
+ + {children} +
+
+ ); + }, +); +ComboboxTextbox.displayName = 'ComboboxTextbox'; + +export interface ComboboxIconProps extends HTMLAttributes { + icon: 'chevron-up' | 'chevron-down'; +} + +export const ComboboxIcon = forwardRef( + ({ icon, ...restProps }: ComboboxIconProps, ref: ForwardedRef) => ( + + ), +); +ComboboxIcon.displayName = 'ComboboxIcon'; + +export interface ComboboxToggleButtonProps extends ButtonProps { + isOpen: boolean; +} + +export const ComboboxToggleButton = forwardRef( + ({ isOpen, ...restProps }: ComboboxToggleButtonProps, ref: ForwardedRef) => ( + + ), +); +ComboboxToggleButton.displayName = 'ComboboxToggleButton'; + +export function getOptionFilter(inputValue?: string) { + return function optionsFilter(searchedValue: string) { + return !inputValue || searchedValue.toLowerCase().includes(inputValue); + }; +} + +export interface ComboboxProps extends HTMLAttributes { + options: string[]; + label: string; + inputProps?: TextboxProps; + onComboboxChange?: (_value?: string) => void; + renderOptions?: (_option: string) => ReactNode; +} + +export const Combobox = forwardRef( + ( + { inputProps, label, className, options, onComboboxChange, renderOptions, ...restProps }: ComboboxProps, + ref: ForwardedRef, + ) => { + const [items, setItems] = useState(options); + + const { + isOpen, + getToggleButtonProps, + getLabelProps, + getMenuProps, + getInputProps, + highlightedIndex, + getItemProps, + selectedItem, + } = useCombobox({ + onInputValueChange({ inputValue }) { + setItems(options.filter(getOptionFilter(inputValue))); + if (onComboboxChange) { + onComboboxChange(inputValue); + } + }, + items, + itemToString: (option) => (option ? option : ''), + }); + + return ( +
+ + + + + {isOpen && + Array.isArray(items) && + items.map((option, index) => ( + + {renderOptions ? renderOptions(option) : option} + + ))} + +
+ ); + }, +); +Combobox.displayName = 'Combobox'; diff --git a/packages/component-library-react/packages/combobox-react/tsconfig.json b/packages/component-library-react/packages/combobox-react/tsconfig.json new file mode 100644 index 00000000000..2596c045e04 --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist/", + "rootDir": "./src/" + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/component-library-react/packages/combobox-react/tsconfig.test.json b/packages/component-library-react/packages/combobox-react/tsconfig.test.json new file mode 100644 index 00000000000..399411d20a7 --- /dev/null +++ b/packages/component-library-react/packages/combobox-react/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*"] +} diff --git a/packages/component-library-react/packages/form-field-react/jest.config.mjs b/packages/component-library-react/packages/form-field-react/jest.config.mjs new file mode 100644 index 00000000000..e26eb68cfd6 --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/jest.config.mjs @@ -0,0 +1,3 @@ +import { createJestConfig } from '../../jest-component.config.mjs'; + +export default createJestConfig(import.meta.url); diff --git a/packages/component-library-react/packages/form-field-react/package.json b/packages/component-library-react/packages/form-field-react/package.json new file mode 100644 index 00000000000..6529258c573 --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/package.json @@ -0,0 +1,41 @@ +{ + "version": "1.0.0", + "author": "Community for NL Design System", + "description": "Button component for the Municipality of Utrecht based on the NL Design System architecture", + "license": "EUPL-1.2", + "name": "@utrecht/form-field-react", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "sideEffects": false, + "scripts": { + "build": "rollup -c ./rollup.config.mjs", + "clean": "rimraf dist", + "test": "mkdir -p pages && NODE_OPTIONS=--experimental-vm-modules jest --coverage --verbose" + }, + "devDependencies": { + "@utrecht/form-field-css": "workspace:*", + "jest": "29.7.0", + "rollup": "4.18.0" + }, + "keywords": [ + "nl-design-system" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git+ssh", + "url": "git@github.com:nl-design-system/utrecht.git", + "directory": "packages/component-library-react/packages/form-field-react" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.6", + "react": "18", + "react-dom": "18" + } +} diff --git a/packages/component-library-react/packages/form-field-react/rollup.config.mjs b/packages/component-library-react/packages/form-field-react/rollup.config.mjs new file mode 100644 index 00000000000..a97d32a0f9c --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/rollup.config.mjs @@ -0,0 +1,3 @@ +import { createComponentPackageConfig } from '../../rollup-component-package.mjs'; + +export default createComponentPackageConfig(import.meta.url); diff --git a/packages/component-library-react/packages/form-field-react/src/css.tsx b/packages/component-library-react/packages/form-field-react/src/css.tsx new file mode 100644 index 00000000000..d1a00240005 --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/src/css.tsx @@ -0,0 +1,9 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2025 Frameless B.V. + * Copyright (c) 2021-2025 Gemeente Utrecht + */ + +import '@utrecht/form-field-css/src/index.scss'; + +export * from './index'; diff --git a/packages/component-library-react/src/FormField.test.tsx b/packages/component-library-react/packages/form-field-react/src/index.test.tsx similarity index 98% rename from packages/component-library-react/src/FormField.test.tsx rename to packages/component-library-react/packages/form-field-react/src/index.test.tsx index 8794182217a..0b13cf42935 100644 --- a/packages/component-library-react/src/FormField.test.tsx +++ b/packages/component-library-react/packages/form-field-react/src/index.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; import { createRef } from 'react'; -import { FormField } from './FormField'; +import { FormField } from './index'; import '@testing-library/jest-dom'; describe('Form field', () => { diff --git a/packages/component-library-react/packages/form-field-react/src/index.tsx b/packages/component-library-react/packages/form-field-react/src/index.tsx new file mode 100644 index 00000000000..b37010ccee0 --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/src/index.tsx @@ -0,0 +1,39 @@ +import clsx from 'clsx'; +import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; + +export interface FormFieldProps extends HTMLAttributes { + description?: ReactNode; + input?: ReactNode; // TODO: Should this be named `control` instead of `input`? + invalid?: boolean; + label?: ReactNode; + type?: string; +} + +export const FormField = forwardRef( + ( + { className, children, description, input, invalid, label, type, ...restProps }: PropsWithChildren, + ref: ForwardedRef, + ) => ( +
+ {label &&
{label}
} + {input &&
{input}
} + {description &&
{description}
} + {children} +
+ ), +); + +FormField.displayName = 'FormField'; diff --git a/packages/component-library-react/packages/form-field-react/tsconfig.json b/packages/component-library-react/packages/form-field-react/tsconfig.json new file mode 100644 index 00000000000..2596c045e04 --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist/", + "rootDir": "./src/" + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/component-library-react/packages/form-field-react/tsconfig.test.json b/packages/component-library-react/packages/form-field-react/tsconfig.test.json new file mode 100644 index 00000000000..399411d20a7 --- /dev/null +++ b/packages/component-library-react/packages/form-field-react/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*"] +} diff --git a/packages/component-library-react/packages/form-label-react/jest.config.mjs b/packages/component-library-react/packages/form-label-react/jest.config.mjs new file mode 100644 index 00000000000..e26eb68cfd6 --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/jest.config.mjs @@ -0,0 +1,3 @@ +import { createJestConfig } from '../../jest-component.config.mjs'; + +export default createJestConfig(import.meta.url); diff --git a/packages/component-library-react/packages/form-label-react/package.json b/packages/component-library-react/packages/form-label-react/package.json new file mode 100644 index 00000000000..74b3c29d928 --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/package.json @@ -0,0 +1,41 @@ +{ + "version": "1.0.0", + "author": "Community for NL Design System", + "description": "Button component for the Municipality of Utrecht based on the NL Design System architecture", + "license": "EUPL-1.2", + "name": "@utrecht/form-label-react", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "sideEffects": false, + "scripts": { + "build": "rollup -c ./rollup.config.mjs", + "clean": "rimraf dist", + "test": "mkdir -p pages && NODE_OPTIONS=--experimental-vm-modules jest --coverage --verbose" + }, + "devDependencies": { + "@utrecht/form-label-css": "workspace:*", + "jest": "29.7.0", + "rollup": "4.18.0" + }, + "keywords": [ + "nl-design-system" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git+ssh", + "url": "git@github.com:nl-design-system/utrecht.git", + "directory": "packages/component-library-react/packages/form-label-react" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.6", + "react": "18", + "react-dom": "18" + } +} diff --git a/packages/component-library-react/packages/form-label-react/rollup.config.mjs b/packages/component-library-react/packages/form-label-react/rollup.config.mjs new file mode 100644 index 00000000000..a97d32a0f9c --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/rollup.config.mjs @@ -0,0 +1,3 @@ +import { createComponentPackageConfig } from '../../rollup-component-package.mjs'; + +export default createComponentPackageConfig(import.meta.url); diff --git a/packages/component-library-react/packages/form-label-react/src/css.tsx b/packages/component-library-react/packages/form-label-react/src/css.tsx new file mode 100644 index 00000000000..c4f065eb182 --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/src/css.tsx @@ -0,0 +1,9 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2025 Frameless B.V. + * Copyright (c) 2021-2025 Gemeente Utrecht + */ + +import '@utrecht/form-label-css/src/index.scss'; + +export * from './index'; diff --git a/packages/component-library-react/src/FormLabel.test.tsx b/packages/component-library-react/packages/form-label-react/src/index.test.tsx similarity index 99% rename from packages/component-library-react/src/FormLabel.test.tsx rename to packages/component-library-react/packages/form-label-react/src/index.test.tsx index 394d3403c28..3bcd6008fca 100644 --- a/packages/component-library-react/src/FormLabel.test.tsx +++ b/packages/component-library-react/packages/form-label-react/src/index.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; import { createRef } from 'react'; -import { FormLabel } from './FormLabel'; +import { FormLabel } from './index'; import '@testing-library/jest-dom'; describe('Form label', () => { diff --git a/packages/component-library-react/packages/form-label-react/src/index.tsx b/packages/component-library-react/packages/form-label-react/src/index.tsx new file mode 100644 index 00000000000..6c2d5722d7e --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/src/index.tsx @@ -0,0 +1,31 @@ +import clsx from 'clsx'; +import { ForwardedRef, forwardRef, LabelHTMLAttributes, PropsWithChildren } from 'react'; + +export interface FormLabelProps extends LabelHTMLAttributes { + type?: 'checkbox' | 'radio'; + disabled?: boolean; + checked?: boolean; +} + +export const FormLabel = forwardRef( + ( + { children, className, type, disabled, checked, ...restProps }: PropsWithChildren, + ref: ForwardedRef, + ) => ( + + ), +); + +FormLabel.displayName = 'FormLabel'; diff --git a/packages/component-library-react/packages/form-label-react/tsconfig.json b/packages/component-library-react/packages/form-label-react/tsconfig.json new file mode 100644 index 00000000000..2596c045e04 --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist/", + "rootDir": "./src/" + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/component-library-react/packages/form-label-react/tsconfig.test.json b/packages/component-library-react/packages/form-label-react/tsconfig.test.json new file mode 100644 index 00000000000..399411d20a7 --- /dev/null +++ b/packages/component-library-react/packages/form-label-react/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*"] +} diff --git a/packages/component-library-react/packages/listbox-react/jest.config.mjs b/packages/component-library-react/packages/listbox-react/jest.config.mjs new file mode 100644 index 00000000000..e26eb68cfd6 --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/jest.config.mjs @@ -0,0 +1,3 @@ +import { createJestConfig } from '../../jest-component.config.mjs'; + +export default createJestConfig(import.meta.url); diff --git a/packages/component-library-react/packages/listbox-react/package.json b/packages/component-library-react/packages/listbox-react/package.json new file mode 100644 index 00000000000..7f8be69b9e6 --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/package.json @@ -0,0 +1,41 @@ +{ + "version": "1.0.0", + "author": "Community for NL Design System", + "description": "Button component for the Municipality of Utrecht based on the NL Design System architecture", + "license": "EUPL-1.2", + "name": "@utrecht/listbox-react", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "sideEffects": false, + "scripts": { + "build": "rollup -c ./rollup.config.mjs", + "clean": "rimraf dist", + "test": "mkdir -p pages && NODE_OPTIONS=--experimental-vm-modules jest --coverage --verbose" + }, + "devDependencies": { + "@utrecht/listbox-css": "workspace:*", + "jest": "29.7.0", + "rollup": "4.18.0" + }, + "keywords": [ + "nl-design-system" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git+ssh", + "url": "git@github.com:nl-design-system/utrecht.git", + "directory": "packages/component-library-react/packages/listbox-react" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.6", + "react": "18", + "react-dom": "18" + } +} diff --git a/packages/component-library-react/packages/listbox-react/rollup.config.mjs b/packages/component-library-react/packages/listbox-react/rollup.config.mjs new file mode 100644 index 00000000000..a97d32a0f9c --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/rollup.config.mjs @@ -0,0 +1,3 @@ +import { createComponentPackageConfig } from '../../rollup-component-package.mjs'; + +export default createComponentPackageConfig(import.meta.url); diff --git a/packages/component-library-react/packages/listbox-react/src/css.tsx b/packages/component-library-react/packages/listbox-react/src/css.tsx new file mode 100644 index 00000000000..db893adf650 --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/src/css.tsx @@ -0,0 +1,9 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2025 Frameless B.V. + * Copyright (c) 2021-2025 Gemeente Utrecht + */ + +import '@utrecht/listbox-css/src/index.scss'; + +export * from './index'; diff --git a/packages/component-library-react/src/Listbox.test.tsx b/packages/component-library-react/packages/listbox-react/src/index.test.tsx similarity index 98% rename from packages/component-library-react/src/Listbox.test.tsx rename to packages/component-library-react/packages/listbox-react/src/index.test.tsx index 473ea0524f7..66cbbbd29b4 100644 --- a/packages/component-library-react/src/Listbox.test.tsx +++ b/packages/component-library-react/packages/listbox-react/src/index.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import { createRef } from 'react'; -import { Listbox, ListboxOption, ListboxOptionGroup } from './Listbox'; +import { Listbox, ListboxOption, ListboxOptionGroup } from './index'; describe('Listbox', () => { it('renders a div HTML element', () => { diff --git a/packages/component-library-react/packages/listbox-react/src/index.tsx b/packages/component-library-react/packages/listbox-react/src/index.tsx new file mode 100644 index 00000000000..10c432b25fa --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/src/index.tsx @@ -0,0 +1,113 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2023 Robbert Broersma + */ + +import clsx from 'clsx'; +import { ForwardedRef, forwardRef, HTMLAttributes, LiHTMLAttributes, PropsWithChildren, ReactNode, useId } from 'react'; + +export interface ListboxProps extends HTMLAttributes { + disabled?: boolean; + invalid?: boolean; + multiple?: boolean; + readOnly?: boolean; + required?: boolean; +} + +export const Listbox = forwardRef( + ( + { + children, + className, + disabled, + invalid, + multiple, + readOnly, + required, + ...restProps + }: PropsWithChildren, + ref: ForwardedRef, + ) => ( +
+
    {children}
+
+ ), +); + +Listbox.displayName = 'Listbox'; + +export interface ListboxOptionGroupProps extends LiHTMLAttributes { + label?: ReactNode; +} + +export const ListboxOptionGroup = forwardRef( + ({ children, label, ...restProps }: PropsWithChildren, ref: ForwardedRef) => { + const id = useId(); + return ( +
  • + {label && ( +
    + {label} +
    + )} +
      {children}
    +
  • + ); + }, +); + +ListboxOptionGroup.displayName = 'ListboxOptionGroup'; + +export interface ListboxOptionProps extends HTMLAttributes { + active?: boolean; + disabled?: boolean; + selected?: boolean; +} + +export const ListboxOption = forwardRef( + ( + { active, className, disabled, selected, ...restProps }: PropsWithChildren, + ref: ForwardedRef, + ) => ( +
  • + ), +); + +ListboxOption.displayName = 'ListboxOption'; diff --git a/packages/component-library-react/packages/listbox-react/tsconfig.json b/packages/component-library-react/packages/listbox-react/tsconfig.json new file mode 100644 index 00000000000..2596c045e04 --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist/", + "rootDir": "./src/" + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/component-library-react/packages/listbox-react/tsconfig.test.json b/packages/component-library-react/packages/listbox-react/tsconfig.test.json new file mode 100644 index 00000000000..399411d20a7 --- /dev/null +++ b/packages/component-library-react/packages/listbox-react/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*"] +} diff --git a/packages/component-library-react/packages/textbox-react/jest.config.mjs b/packages/component-library-react/packages/textbox-react/jest.config.mjs new file mode 100644 index 00000000000..e26eb68cfd6 --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/jest.config.mjs @@ -0,0 +1,3 @@ +import { createJestConfig } from '../../jest-component.config.mjs'; + +export default createJestConfig(import.meta.url); diff --git a/packages/component-library-react/packages/textbox-react/package.json b/packages/component-library-react/packages/textbox-react/package.json new file mode 100644 index 00000000000..ae056db8ce3 --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/package.json @@ -0,0 +1,41 @@ +{ + "version": "1.0.0", + "author": "Community for NL Design System", + "description": "Button component for the Municipality of Utrecht based on the NL Design System architecture", + "license": "EUPL-1.2", + "name": "@utrecht/textbox-react", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "sideEffects": false, + "scripts": { + "build": "rollup -c ./rollup.config.mjs", + "clean": "rimraf dist", + "test": "mkdir -p pages && NODE_OPTIONS=--experimental-vm-modules jest --coverage --verbose" + }, + "devDependencies": { + "@utrecht/textbox-css": "workspace:*", + "jest": "29.7.0", + "rollup": "4.18.0" + }, + "keywords": [ + "nl-design-system" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git+ssh", + "url": "git@github.com:nl-design-system/utrecht.git", + "directory": "packages/component-library-react/packages/textbox-react" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.6", + "react": "18", + "react-dom": "18" + } +} diff --git a/packages/component-library-react/packages/textbox-react/rollup.config.mjs b/packages/component-library-react/packages/textbox-react/rollup.config.mjs new file mode 100644 index 00000000000..a97d32a0f9c --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/rollup.config.mjs @@ -0,0 +1,3 @@ +import { createComponentPackageConfig } from '../../rollup-component-package.mjs'; + +export default createComponentPackageConfig(import.meta.url); diff --git a/packages/component-library-react/packages/textbox-react/src/css.tsx b/packages/component-library-react/packages/textbox-react/src/css.tsx new file mode 100644 index 00000000000..18cb13ed359 --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/src/css.tsx @@ -0,0 +1,9 @@ +/** + * @license EUPL-1.2 + * Copyright (c) 2020-2025 Frameless B.V. + * Copyright (c) 2021-2025 Gemeente Utrecht + */ + +import '@utrecht/textbox-css/src/index.scss'; + +export * from './index'; diff --git a/packages/component-library-react/src/Textbox.test.tsx b/packages/component-library-react/packages/textbox-react/src/index.test.tsx similarity index 99% rename from packages/component-library-react/src/Textbox.test.tsx rename to packages/component-library-react/packages/textbox-react/src/index.test.tsx index b9dcf1e0a1a..e0ea1d60121 100644 --- a/packages/component-library-react/src/Textbox.test.tsx +++ b/packages/component-library-react/packages/textbox-react/src/index.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createRef } from 'react'; -import { Textbox } from './Textbox'; +import { Textbox } from './index'; import '@testing-library/jest-dom'; describe('Textbox', () => { diff --git a/packages/component-library-react/packages/textbox-react/src/index.tsx b/packages/component-library-react/packages/textbox-react/src/index.tsx new file mode 100644 index 00000000000..f52d1a7b6be --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/src/index.tsx @@ -0,0 +1,63 @@ +import clsx from 'clsx'; +import { ForwardedRef, forwardRef, InputHTMLAttributes } from 'react'; +export type TextboxTypes = + | 'date' + | 'datetime-local' + | 'email' + | 'month' + | 'number' + | 'password' + | 'search' + | 'tel' + | 'text' + | 'time' + | 'url' + | 'week'; + +export interface TextboxProps extends InputHTMLAttributes { + inputRequired?: boolean; + invalid?: boolean; + type?: string | TextboxTypes; +} + +export const Textbox = forwardRef( + ( + { + dir, + disabled, + invalid, + readOnly, + required, + inputRequired, + className, + type = 'text', + inputMode, + ...restProps + }: TextboxProps, + ref: ForwardedRef, + ) => ( + + ), +); + +Textbox.displayName = 'Textbox'; diff --git a/packages/component-library-react/packages/textbox-react/tsconfig.json b/packages/component-library-react/packages/textbox-react/tsconfig.json new file mode 100644 index 00000000000..2596c045e04 --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist/", + "rootDir": "./src/" + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/component-library-react/packages/textbox-react/tsconfig.test.json b/packages/component-library-react/packages/textbox-react/tsconfig.test.json new file mode 100644 index 00000000000..399411d20a7 --- /dev/null +++ b/packages/component-library-react/packages/textbox-react/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["**/node_modules/*"] +} diff --git a/packages/component-library-react/src/Combobox.test.tsx b/packages/component-library-react/src/Combobox.test.tsx deleted file mode 100644 index 23fd398a1f4..00000000000 --- a/packages/component-library-react/src/Combobox.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { render } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import { createRef } from 'react'; -import { Combobox } from './Combobox'; - -describe('Combobox', () => { - it('renders a div HTML element', () => { - const { container } = render(); - - const combobox = container.querySelector('div:only-child'); - - expect(combobox).toBeInTheDocument(); - expect(combobox).toBeVisible(); - }); - - it('renders a design system BEM block class name', () => { - const { container } = render(); - - const combobox = container.querySelector(':only-child'); - - expect(combobox).toHaveClass('utrecht-combobox'); - }); - - it('supports ForwardRef in React', () => { - const ref = createRef(); - - const { container } = render(); - - const combobox = container.querySelector(':only-child'); - - expect(ref.current).toBe(combobox); - }); -}); diff --git a/packages/component-library-react/src/Combobox.tsx b/packages/component-library-react/src/Combobox.tsx index 4b67ac9c2a2..c030dbed6c1 100644 --- a/packages/component-library-react/src/Combobox.tsx +++ b/packages/component-library-react/src/Combobox.tsx @@ -3,40 +3,54 @@ * Copyright (c) 2023 Robbert Broersma */ -import clsx from 'clsx'; -import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren } from 'react'; - -export type ComboboxProps = HTMLAttributes; - -export const Combobox = forwardRef( - ({ className, ...restProps }: PropsWithChildren, ref: ForwardedRef) => ( -
    - ), -); - -Combobox.displayName = 'Combobox'; - -export interface ComboboxPopoverProps extends HTMLAttributes { - position?: string | 'block-end' | 'block-start'; -} - -export const ComboboxPopover = forwardRef( - ( - { className, position, ...restProps }: PropsWithChildren, - ref: ForwardedRef, - ) => ( -
    - ), -); - -ComboboxPopover.displayName = 'ComboboxPopover'; +// import clsx from 'clsx'; +// import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren } from 'react'; + +// export type ComboboxProps = HTMLAttributes; + +// export const Combobox = forwardRef( +// ({ className, ...restProps }: PropsWithChildren, ref: ForwardedRef) => ( +//
    +// ), +// ); + +// Combobox.displayName = 'Combobox'; + +// export interface ComboboxPopoverProps extends HTMLAttributes { +// position?: string | 'block-end' | 'block-start'; +// } + +// export const ComboboxPopover = forwardRef( +// ( +// { className, position, ...restProps }: PropsWithChildren, +// ref: ForwardedRef, +// ) => ( +//
    +// ), +// ); + +// ComboboxPopover.displayName = 'ComboboxPopover'; + +export type { + ComboboxIconProps, + ComboboxProps, + ComboboxTextboxProps, + ComboboxToggleButtonProps, +} from '@utrecht/combobox-react/src/index'; +export { + Combobox, + ComboboxIcon, + ComboboxTextbox, + ComboboxToggleButton, + getOptionFilter, +} from '@utrecht/combobox-react/src/index'; diff --git a/packages/component-library-react/src/FormField.tsx b/packages/component-library-react/src/FormField.tsx index b37010ccee0..979ab7ffa68 100644 --- a/packages/component-library-react/src/FormField.tsx +++ b/packages/component-library-react/src/FormField.tsx @@ -1,39 +1,2 @@ -import clsx from 'clsx'; -import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; - -export interface FormFieldProps extends HTMLAttributes { - description?: ReactNode; - input?: ReactNode; // TODO: Should this be named `control` instead of `input`? - invalid?: boolean; - label?: ReactNode; - type?: string; -} - -export const FormField = forwardRef( - ( - { className, children, description, input, invalid, label, type, ...restProps }: PropsWithChildren, - ref: ForwardedRef, - ) => ( -
    - {label &&
    {label}
    } - {input &&
    {input}
    } - {description &&
    {description}
    } - {children} -
    - ), -); - -FormField.displayName = 'FormField'; +export type { FormFieldProps } from '@utrecht/form-field-react/src/index'; +export { FormField } from '@utrecht/form-field-react/src/index'; diff --git a/packages/component-library-react/src/FormLabel.tsx b/packages/component-library-react/src/FormLabel.tsx index 6c2d5722d7e..4d18a674c76 100644 --- a/packages/component-library-react/src/FormLabel.tsx +++ b/packages/component-library-react/src/FormLabel.tsx @@ -1,31 +1,2 @@ -import clsx from 'clsx'; -import { ForwardedRef, forwardRef, LabelHTMLAttributes, PropsWithChildren } from 'react'; - -export interface FormLabelProps extends LabelHTMLAttributes { - type?: 'checkbox' | 'radio'; - disabled?: boolean; - checked?: boolean; -} - -export const FormLabel = forwardRef( - ( - { children, className, type, disabled, checked, ...restProps }: PropsWithChildren, - ref: ForwardedRef, - ) => ( - - ), -); - -FormLabel.displayName = 'FormLabel'; +export type { FormLabelProps } from '@utrecht/form-label-react/src/index'; +export { FormLabel } from '@utrecht/form-label-react/src/index'; diff --git a/packages/component-library-react/src/Listbox.tsx b/packages/component-library-react/src/Listbox.tsx index 10c432b25fa..6cd9b80a9ad 100644 --- a/packages/component-library-react/src/Listbox.tsx +++ b/packages/component-library-react/src/Listbox.tsx @@ -3,111 +3,5 @@ * Copyright (c) 2023 Robbert Broersma */ -import clsx from 'clsx'; -import { ForwardedRef, forwardRef, HTMLAttributes, LiHTMLAttributes, PropsWithChildren, ReactNode, useId } from 'react'; - -export interface ListboxProps extends HTMLAttributes { - disabled?: boolean; - invalid?: boolean; - multiple?: boolean; - readOnly?: boolean; - required?: boolean; -} - -export const Listbox = forwardRef( - ( - { - children, - className, - disabled, - invalid, - multiple, - readOnly, - required, - ...restProps - }: PropsWithChildren, - ref: ForwardedRef, - ) => ( -
    -
      {children}
    -
    - ), -); - -Listbox.displayName = 'Listbox'; - -export interface ListboxOptionGroupProps extends LiHTMLAttributes { - label?: ReactNode; -} - -export const ListboxOptionGroup = forwardRef( - ({ children, label, ...restProps }: PropsWithChildren, ref: ForwardedRef) => { - const id = useId(); - return ( -
  • - {label && ( -
    - {label} -
    - )} -
      {children}
    -
  • - ); - }, -); - -ListboxOptionGroup.displayName = 'ListboxOptionGroup'; - -export interface ListboxOptionProps extends HTMLAttributes { - active?: boolean; - disabled?: boolean; - selected?: boolean; -} - -export const ListboxOption = forwardRef( - ( - { active, className, disabled, selected, ...restProps }: PropsWithChildren, - ref: ForwardedRef, - ) => ( -
  • - ), -); - -ListboxOption.displayName = 'ListboxOption'; +export type { ListboxOptionGroupProps, ListboxOptionProps, ListboxProps } from '@utrecht/listbox-react/src/index'; +export { Listbox, ListboxOption, ListboxOptionGroup } from '@utrecht/listbox-react/src/index'; diff --git a/packages/component-library-react/src/Textbox.tsx b/packages/component-library-react/src/Textbox.tsx index f52d1a7b6be..96d991adaa4 100644 --- a/packages/component-library-react/src/Textbox.tsx +++ b/packages/component-library-react/src/Textbox.tsx @@ -1,63 +1,2 @@ -import clsx from 'clsx'; -import { ForwardedRef, forwardRef, InputHTMLAttributes } from 'react'; -export type TextboxTypes = - | 'date' - | 'datetime-local' - | 'email' - | 'month' - | 'number' - | 'password' - | 'search' - | 'tel' - | 'text' - | 'time' - | 'url' - | 'week'; - -export interface TextboxProps extends InputHTMLAttributes { - inputRequired?: boolean; - invalid?: boolean; - type?: string | TextboxTypes; -} - -export const Textbox = forwardRef( - ( - { - dir, - disabled, - invalid, - readOnly, - required, - inputRequired, - className, - type = 'text', - inputMode, - ...restProps - }: TextboxProps, - ref: ForwardedRef, - ) => ( - - ), -); - -Textbox.displayName = 'Textbox'; +export type { TextboxProps, TextboxTypes } from '@utrecht/textbox-react/src/index'; +export { Textbox } from '@utrecht/textbox-react/src/index'; diff --git a/packages/component-library-react/src/css-module/Combobox.tsx b/packages/component-library-react/src/css-module/Combobox.tsx index cc192da27c0..704f9b4eae6 100644 --- a/packages/component-library-react/src/css-module/Combobox.tsx +++ b/packages/component-library-react/src/css-module/Combobox.tsx @@ -3,6 +3,4 @@ * Copyright (c) 2021 Robbert Broersma */ -import '@utrecht/combobox-css/src/index.scss'; - -export * from '../Combobox'; +export * from '@utrecht/combobox-react/src/css'; diff --git a/packages/component-library-react/src/css-module/FormField.tsx b/packages/component-library-react/src/css-module/FormField.tsx index a4322fe09fe..97ef1551cc8 100644 --- a/packages/component-library-react/src/css-module/FormField.tsx +++ b/packages/component-library-react/src/css-module/FormField.tsx @@ -3,6 +3,4 @@ * Copyright (c) 2021 Robbert Broersma */ -import '@utrecht/form-field-css/src/index.scss'; - -export * from '../FormField'; +export * from '@utrecht/form-field-react/src/css'; diff --git a/packages/component-library-react/src/css-module/FormLabel.tsx b/packages/component-library-react/src/css-module/FormLabel.tsx index e11ed9acb16..8b5995675e1 100644 --- a/packages/component-library-react/src/css-module/FormLabel.tsx +++ b/packages/component-library-react/src/css-module/FormLabel.tsx @@ -3,6 +3,4 @@ * Copyright (c) 2021 Robbert Broersma */ -import '@utrecht/form-label-css/src/index.scss'; - -export * from '../FormLabel'; +export * from '@utrecht/form-label-react/src/css'; diff --git a/packages/component-library-react/src/css-module/Listbox.tsx b/packages/component-library-react/src/css-module/Listbox.tsx index 40e16f691cd..716bf0ca2b2 100644 --- a/packages/component-library-react/src/css-module/Listbox.tsx +++ b/packages/component-library-react/src/css-module/Listbox.tsx @@ -3,6 +3,4 @@ * Copyright (c) 2021 Robbert Broersma */ -import '@utrecht/listbox-css/src/index.scss'; - -export * from '../Listbox'; +export * from '@utrecht/listbox-react/src/css'; diff --git a/packages/component-library-react/src/css-module/Textbox.tsx b/packages/component-library-react/src/css-module/Textbox.tsx index ca395f37f34..937ccaafcdc 100644 --- a/packages/component-library-react/src/css-module/Textbox.tsx +++ b/packages/component-library-react/src/css-module/Textbox.tsx @@ -3,6 +3,4 @@ * Copyright (c) 2021 Robbert Broersma */ -import '@utrecht/textbox-css/src/index.scss'; - -export * from '../Textbox'; +export * from '@utrecht/textbox-react/src/css'; diff --git a/packages/component-library-react/src/css-module/index.ts b/packages/component-library-react/src/css-module/index.ts index 0d5843bc7db..14743f91c48 100644 --- a/packages/component-library-react/src/css-module/index.ts +++ b/packages/component-library-react/src/css-module/index.ts @@ -20,8 +20,8 @@ export type { BlockquoteProps } from '../Blockquote'; export { Blockquote } from './Blockquote'; export type { BreadcrumbNavProps, BreadcrumbNavLinkProps, BreadcrumbNavSeparatorProps } from '../BreadcrumbNav'; export { BreadcrumbNav, BreadcrumbNavLink, BreadcrumbNavSeparator } from './BreadcrumbNav'; -export type { ButtonProps } from '@utrecht/button-react'; -export { Button, PrimaryActionButton, SecondaryActionButton, SubtleButton } from '@utrecht/button-react'; +export type { ButtonProps } from './Button'; +export { Button, PrimaryActionButton, SecondaryActionButton, SubtleButton } from './Button'; export type { ButtonGroupProps } from '../ButtonGroup'; export { ButtonGroup } from './ButtonGroup'; export type { ButtonLinkProps } from '../ButtonLink'; @@ -36,8 +36,10 @@ export type { ColorSampleProps } from '../ColorSample'; export { ColorSample } from './ColorSample'; export type { ColumnLayoutProps } from '../ColumnLayout'; export { ColumnLayout } from './ColumnLayout'; -export type { ComboboxProps, ComboboxPopoverProps } from './Combobox'; -export { Combobox, ComboboxPopover } from './Combobox'; +// export type { ComboboxProps, ComboboxPopoverProps } from './Combobox'; +// export { Combobox, ComboboxPopover } from './Combobox'; +export type { ComboboxProps, ComboboxIconProps, ComboboxTextboxProps, ComboboxToggleButtonProps } from './Combobox'; +export { Combobox, ComboboxIcon, ComboboxTextbox, ComboboxToggleButton, getOptionFilter } from './Combobox'; export type { CurrencyDataProps } from '../CurrencyData'; export { CurrencyData } from './CurrencyData'; export type { DataBadgeProps } from '../DataBadge'; diff --git a/packages/component-library-react/src/index.ts b/packages/component-library-react/src/index.ts index d02918b8953..840261c19ab 100644 --- a/packages/component-library-react/src/index.ts +++ b/packages/component-library-react/src/index.ts @@ -43,8 +43,10 @@ export type { ColorSampleProps } from './ColorSample'; export { ColorSample } from './ColorSample'; export type { ColumnLayoutProps } from './ColumnLayout'; export { ColumnLayout } from './ColumnLayout'; -export type { ComboboxProps, ComboboxPopoverProps } from './Combobox'; -export { Combobox, ComboboxPopover } from './Combobox'; +// export type { ComboboxProps, ComboboxPopoverProps } from './Combobox'; +// export { Combobox, ComboboxPopover } from './Combobox'; +export type { ComboboxProps, ComboboxIconProps, ComboboxToggleButtonProps, ComboboxTextboxProps } from './Combobox'; +export { Combobox, ComboboxIcon, ComboboxTextbox, ComboboxToggleButton, getOptionFilter } from './Combobox'; export type { CurrencyDataProps } from './CurrencyData'; export { CurrencyData } from './CurrencyData'; export type { DataBadgeProps } from './DataBadge'; diff --git a/packages/storybook-css/src/Combobox.tsx b/packages/storybook-css/src/Combobox.tsx index ae1f1444013..6e273bf4ef1 100644 --- a/packages/storybook-css/src/Combobox.tsx +++ b/packages/storybook-css/src/Combobox.tsx @@ -1,6 +1,6 @@ import { Combobox, - ComboboxPopoverProps, + // ComboboxPopoverProps, FormField, FormLabel, Listbox, @@ -43,7 +43,7 @@ const ComboboxListboxPopover = ({ expanded, position, ...restProps -}: PropsWithChildren<{ expanded?: boolean } & ComboboxPopoverProps>) => ( +}: PropsWithChildren<{ expanded?: boolean } & any>) => ( ; + +export default meta; + +type Story = StoryObj; +export const Default: Story = { + args: { + label: 'Select an option', + inputProps: { + placeholder: 'Type to search...', + }, + options: ['Option-1', 'Option-2', 'Option-3', 'Option-4', 'Option-5', 'Option-6'], + }, +}; + +// export const Info: Story = { +// args: { +// ...Default.args, +// type: 'info', +// }, +// }; + +// export const OK: Story = { +// args: { +// ...Default.args, +// type: 'ok', +// }, +// }; + +// export const Warning: Story = { +// args: { +// ...Default.args, +// type: 'warning', +// }, +// }; + +// export const Error: Story = { +// args: { +// ...Default.args, +// type: 'error', +// }, +// }; + +// export const WithIcon: Story = { +// args: { +// ...Default.args, +// type: 'info', +// icon: 'utrecht-icon-loupe', +// }, +// }; + +export const DesignTokens = designTokenStory(meta); diff --git a/packages/storybook-react/src/stories/Textbox.stories.tsx b/packages/storybook-react/src/stories/Textbox.stories.tsx index decfae79422..cbb17e31031 100644 --- a/packages/storybook-react/src/stories/Textbox.stories.tsx +++ b/packages/storybook-react/src/stories/Textbox.stories.tsx @@ -1,5 +1,5 @@ import { Meta, StoryObj } from '@storybook/react'; -import { Textbox } from '@utrecht/component-library-react/dist/css-module/index'; +import { Textbox } from '@utrecht/component-library-react/dist/css-module'; import tokens from '@utrecht/design-tokens/dist/index.json'; import readme from '@utrecht/textbox-css/README.md?raw'; import tokensDefinition from '@utrecht/textbox-css/src/tokens.json'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e3004a864a..80bbd65425e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1530,9 +1530,9 @@ importers: '@utrecht/column-layout-css': specifier: workspace:* version: link:../../components/column-layout - '@utrecht/combobox-css': + '@utrecht/combobox-react': specifier: workspace:* - version: link:../../components/combobox + version: link:packages/combobox-react '@utrecht/currency-data-css': specifier: workspace:* version: link:../../components/currency-data @@ -1563,21 +1563,21 @@ importers: '@utrecht/focus-ring-css': specifier: workspace:* version: link:../../components/focus-ring - '@utrecht/form-field-css': - specifier: workspace:* - version: link:../../components/form-field '@utrecht/form-field-description-css': specifier: workspace:* version: link:../../components/form-field-description '@utrecht/form-field-error-message-css': specifier: workspace:* version: link:../../components/form-field-error-message + '@utrecht/form-field-react': + specifier: workspace:* + version: link:packages/form-field-react '@utrecht/form-fieldset-css': specifier: workspace:* version: link:../../components/form-fieldset - '@utrecht/form-label-css': + '@utrecht/form-label-react': specifier: workspace:* - version: link:../../components/form-label + version: link:packages/form-label-react '@utrecht/form-toggle-css': specifier: workspace:* version: link:../../components/form-toggle @@ -1635,6 +1635,9 @@ importers: '@utrecht/listbox-css': specifier: workspace:* version: link:../../components/listbox + '@utrecht/listbox-react': + specifier: workspace:* + version: link:packages/listbox-react '@utrecht/logo-button-css': specifier: workspace:* version: link:../../components/logo-button @@ -1722,9 +1725,9 @@ importers: '@utrecht/textarea-css': specifier: workspace:* version: link:../../components/textarea - '@utrecht/textbox-css': + '@utrecht/textbox-react': specifier: workspace:* - version: link:../../components/textbox + version: link:packages/textbox-react '@utrecht/top-task-link-css': specifier: workspace:* version: link:../../components/toptask-link @@ -1857,6 +1860,134 @@ importers: specifier: 4.18.0 version: 4.18.0 + packages/component-library-react/packages/combobox-react: + dependencies: + '@babel/runtime': + specifier: ^7.23.6 + version: 7.24.7 + downshift: + specifier: 9.0.8 + version: 9.0.8(react@18.3.1) + react: + specifier: '18' + version: 18.3.1 + react-dom: + specifier: '18' + version: 18.3.1(react@18.3.1) + devDependencies: + '@utrecht/button-react': + specifier: workspace:* + version: link:../button-react + '@utrecht/combobox-css': + specifier: workspace:* + version: link:../../../../components/combobox + '@utrecht/form-field-react': + specifier: workspace:* + version: link:../form-field-react + '@utrecht/form-label-react': + specifier: workspace:* + version: link:../form-label-react + '@utrecht/listbox-react': + specifier: workspace:* + version: link:../listbox-react + '@utrecht/textbox-react': + specifier: workspace:* + version: link:../textbox-react + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.100)(@types/node@20.14.8)(typescript@4.9.5)) + rollup: + specifier: 4.18.0 + version: 4.18.0 + + packages/component-library-react/packages/form-field-react: + dependencies: + '@babel/runtime': + specifier: ^7.23.6 + version: 7.24.7 + react: + specifier: '18' + version: 18.3.1 + react-dom: + specifier: '18' + version: 18.3.1(react@18.3.1) + devDependencies: + '@utrecht/form-field-css': + specifier: workspace:* + version: link:../../../../components/form-field + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.100)(@types/node@20.14.8)(typescript@4.9.5)) + rollup: + specifier: 4.18.0 + version: 4.18.0 + + packages/component-library-react/packages/form-label-react: + dependencies: + '@babel/runtime': + specifier: ^7.23.6 + version: 7.24.7 + react: + specifier: '18' + version: 18.3.1 + react-dom: + specifier: '18' + version: 18.3.1(react@18.3.1) + devDependencies: + '@utrecht/form-label-css': + specifier: workspace:* + version: link:../../../../components/form-label + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.100)(@types/node@20.14.8)(typescript@4.9.5)) + rollup: + specifier: 4.18.0 + version: 4.18.0 + + packages/component-library-react/packages/listbox-react: + dependencies: + '@babel/runtime': + specifier: ^7.23.6 + version: 7.24.7 + react: + specifier: '18' + version: 18.3.1 + react-dom: + specifier: '18' + version: 18.3.1(react@18.3.1) + devDependencies: + '@utrecht/listbox-css': + specifier: workspace:* + version: link:../../../../components/listbox + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.100)(@types/node@20.14.8)(typescript@4.9.5)) + rollup: + specifier: 4.18.0 + version: 4.18.0 + + packages/component-library-react/packages/textbox-react: + dependencies: + '@babel/runtime': + specifier: ^7.23.6 + version: 7.24.7 + react: + specifier: '18' + version: 18.3.1 + react-dom: + specifier: '18' + version: 18.3.1(react@18.3.1) + devDependencies: + '@utrecht/textbox-css': + specifier: workspace:* + version: link:../../../../components/textbox + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.100)(@types/node@20.14.8)(typescript@4.9.5)) + rollup: + specifier: 4.18.0 + version: 4.18.0 + packages/component-library-vue: devDependencies: '@babel/core': @@ -3461,6 +3592,9 @@ importers: '@utrecht/color-sample-css': specifier: workspace:* version: link:../../components/color-sample + '@utrecht/combobox-css': + specifier: workspace:* + version: link:../../components/combobox '@utrecht/component-library-react': specifier: workspace:* version: link:../component-library-react @@ -3563,6 +3697,9 @@ importers: '@utrecht/list-social-css': specifier: workspace:* version: link:../../components/list-social + '@utrecht/listbox-css': + specifier: workspace:* + version: link:../../components/listbox '@utrecht/logo-css': specifier: workspace:* version: link:../../components/logo @@ -11926,6 +12063,9 @@ packages: resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} engines: {node: '>= 0.8.0'} + compute-scroll-into-view@3.1.0: + resolution: {integrity: sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==} + computeds@0.0.1: resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} @@ -12793,6 +12933,11 @@ packages: downloadjs@1.4.7: resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==} + downshift@9.0.8: + resolution: {integrity: sha512-59BWD7+hSUQIM1DeNPLirNNnZIO9qMdIK5GQ/Uo8q34gT4B78RBlb9dhzgnh0HfQTJj4T/JKYD8KoLAlMWnTsA==} + peerDependencies: + react: '>=16.12.0' + dragula@3.7.3: resolution: {integrity: sha512-/rRg4zRhcpf81TyDhaHLtXt6sEywdfpv1cRUMeFFy7DuypH2U0WUL0GTdyAQvXegviT4PJK4KuMmOaIDpICseQ==} @@ -21226,8 +21371,8 @@ packages: vue-component-type-helpers@1.8.25: resolution: {integrity: sha512-NCA6sekiJIMnMs4DdORxATXD+/NRkQpS32UC+I1KQJUasx+Z7MZUb3Y+MsKsFmX+PgyTYSteb73JW77AibaCCw==} - vue-component-type-helpers@2.0.29: - resolution: {integrity: sha512-58i+ZhUAUpwQ+9h5Hck0D+jr1qbYl4voRt5KffBx8qzELViQ4XdT/Tuo+mzq8u63teAG8K0lLaOiL5ofqW38rg==} + vue-component-type-helpers@2.1.4: + resolution: {integrity: sha512-aVqB3KxwpM76cYRkpnezl1J62E/1omzHQfx1yuz7zcbxmzmP/PeSgI20NEmkdeGnjZPVzm0V9fB4ZyRu5BBj4A==} vue-docgen-api@4.75.1: resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==} @@ -27162,8 +27307,8 @@ snapshots: '@jridgewell/source-map@0.3.5': dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/sourcemap-codec@1.4.15': {} @@ -28089,7 +28234,7 @@ snapshots: dependencies: detect-libc: 1.0.3 is-glob: 4.0.3 - micromatch: 4.0.5 + micromatch: 4.0.7 node-addon-api: 7.0.0 optionalDependencies: '@parcel/watcher-android-arm64': 2.3.0 @@ -30510,7 +30655,7 @@ snapshots: dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 - semver: 7.6.2 + semver: 7.5.4 store2: 2.14.2 telejson: 7.2.0 ts-dedent: 2.2.0 @@ -30979,7 +31124,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.3.11(typescript@4.9.5) - vue-component-type-helpers: 2.0.29 + vue-component-type-helpers: 2.1.4 transitivePeerDependencies: - encoding - supports-color @@ -31720,7 +31865,7 @@ snapshots: graphemer: 1.4.0 ignore: 5.3.0 natural-compare-lite: 1.4.0 - semver: 7.6.2 + semver: 7.5.4 tsutils: 3.21.0(typescript@4.9.5) optionalDependencies: typescript: 4.9.5 @@ -33923,6 +34068,8 @@ snapshots: transitivePeerDependencies: - supports-color + compute-scroll-into-view@3.1.0: {} + computeds@0.0.1: {} concat-map@0.0.1: {} @@ -34312,7 +34459,7 @@ snapshots: postcss-modules-scope: 3.0.0(postcss@8.4.38) postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 - semver: 7.5.4 + semver: 7.6.2 webpack: 5.89.0(@swc/core@1.3.100(@swc/helpers@0.5.2))(esbuild@0.18.20) css-loader@6.8.1(webpack@5.89.0(@swc/core@1.3.100(@swc/helpers@0.5.2))(esbuild@0.19.11)): @@ -34975,6 +35122,15 @@ snapshots: downloadjs@1.4.7: {} + downshift@9.0.8(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.7 + compute-scroll-into-view: 3.1.0 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.2.0 + tslib: 2.6.2 + dragula@3.7.3: dependencies: contra: 1.9.4 @@ -36415,13 +36571,13 @@ snapshots: find-yarn-workspace-root@2.0.0: dependencies: - micromatch: 4.0.5 + micromatch: 4.0.7 findup-sync@4.0.0: dependencies: detect-file: 1.0.0 is-glob: 4.0.3 - micromatch: 4.0.5 + micromatch: 4.0.7 resolve-dir: 1.0.1 firacode@6.2.0: {} @@ -37852,7 +38008,7 @@ snapshots: http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 - micromatch: 4.0.5 + micromatch: 4.0.7 optionalDependencies: '@types/express': 4.17.21 transitivePeerDependencies: @@ -39138,7 +39294,7 @@ snapshots: chalk: 4.1.2 flow-parser: 0.223.3 graceful-fs: 4.2.11 - micromatch: 4.0.5 + micromatch: 4.0.7 neo-async: 2.6.2 node-dir: 0.1.17 recast: 0.21.5 @@ -46839,7 +46995,7 @@ snapshots: vue-component-type-helpers@1.8.25: {} - vue-component-type-helpers@2.0.29: {} + vue-component-type-helpers@2.1.4: {} vue-docgen-api@4.75.1(vue@3.3.11(typescript@4.9.5)): dependencies: @@ -47660,7 +47816,7 @@ snapshots: yargs@16.2.0: dependencies: cliui: 7.0.4 - escalade: 3.1.1 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3