From dda41cb93c45062eec0d47b59c35ddf961e5c842 Mon Sep 17 00:00:00 2001 From: Ali Amori Kadhim Date: Mon, 21 Oct 2024 12:57:20 +0200 Subject: [PATCH] feat: create FormLabel react component package --- .changeset/afraid-boxes-boil.md | 5 + .../form-label-react/.eslintrc.json | 8 + .../form-label-react/babel.config.cjs | 12 ++ .../form-label-react/jest.config.mjs | 8 + .../form-label-react/package.json | 68 ++++++++ .../form-label-react/rollup.config.mjs | 87 +++++++++++ .../form-label-react/src/css.tsx | 9 ++ .../form-label-react/src/index.test.tsx | 147 ++++++++++++++++++ .../form-label-react/src/index.tsx | 37 +++++ .../form-label-react/tsconfig.build.json | 6 + .../form-label-react/tsconfig.json | 9 ++ .../form-label-react/tsconfig.test.json | 8 + pnpm-lock.yaml | 111 +++++++++++-- 13 files changed, 505 insertions(+), 10 deletions(-) create mode 100644 .changeset/afraid-boxes-boil.md create mode 100644 packages/components-react/form-label-react/.eslintrc.json create mode 100644 packages/components-react/form-label-react/babel.config.cjs create mode 100644 packages/components-react/form-label-react/jest.config.mjs create mode 100644 packages/components-react/form-label-react/package.json create mode 100644 packages/components-react/form-label-react/rollup.config.mjs create mode 100644 packages/components-react/form-label-react/src/css.tsx create mode 100644 packages/components-react/form-label-react/src/index.test.tsx create mode 100644 packages/components-react/form-label-react/src/index.tsx create mode 100644 packages/components-react/form-label-react/tsconfig.build.json create mode 100644 packages/components-react/form-label-react/tsconfig.json create mode 100644 packages/components-react/form-label-react/tsconfig.test.json diff --git a/.changeset/afraid-boxes-boil.md b/.changeset/afraid-boxes-boil.md new file mode 100644 index 00000000000..364b661fe6c --- /dev/null +++ b/.changeset/afraid-boxes-boil.md @@ -0,0 +1,5 @@ +--- +"@utrecht/form-label-react": major +--- + +Create FormLabel react component package diff --git a/packages/components-react/form-label-react/.eslintrc.json b/packages/components-react/form-label-react/.eslintrc.json new file mode 100644 index 00000000000..5ab00b0ee18 --- /dev/null +++ b/packages/components-react/form-label-react/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "env": { + "jest/globals": true + }, + "rules": { + "react/react-in-jsx-scope": "off" + } +} diff --git a/packages/components-react/form-label-react/babel.config.cjs b/packages/components-react/form-label-react/babel.config.cjs new file mode 100644 index 00000000000..7c9f0f55a12 --- /dev/null +++ b/packages/components-react/form-label-react/babel.config.cjs @@ -0,0 +1,12 @@ +module.exports = { + presets: [ + '@babel/preset-env', + [ + '@babel/preset-react', + { + runtime: 'automatic', + }, + ], + '@babel/preset-typescript', + ], +}; diff --git a/packages/components-react/form-label-react/jest.config.mjs b/packages/components-react/form-label-react/jest.config.mjs new file mode 100644 index 00000000000..004e8e2a748 --- /dev/null +++ b/packages/components-react/form-label-react/jest.config.mjs @@ -0,0 +1,8 @@ +export default { + moduleDirectories: ['node_modules', '/'], + testEnvironment: 'jest-environment-jsdom', + testPathIgnorePatterns: ['/dist/'], + moduleNameMapper: { + '^@utrecht/(.*)$': '/../$1/src/', + }, +}; diff --git a/packages/components-react/form-label-react/package.json b/packages/components-react/form-label-react/package.json new file mode 100644 index 00000000000..ce67cd7e6b0 --- /dev/null +++ b/packages/components-react/form-label-react/package.json @@ -0,0 +1,68 @@ +{ + "name": "@utrecht/form-label-react", + "version": "0.0.0", + "author": "Community for NL Design System", + "description": "Form Label component for the Municipality of Utrecht based on the NL Design System architecture", + "license": "EUPL-1.2", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "sideEffects": false, + "scripts": { + "clean": "rimraf dist *.tsbuildinfo .rollup.cache coverage", + "build": "rollup --config ./rollup.config.mjs", + "test": "mkdir -p pages && jest --coverage --verbose", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.24.7", + "@babel/preset-react": "7.24.7", + "@babel/preset-typescript": "7.24.7", + "@rollup/plugin-babel": "6.0.4", + "@rollup/plugin-commonjs": "26.0.1", + "@rollup/plugin-json": "6.1.0", + "@rollup/plugin-node-resolve": "15.2.3", + "@rollup/plugin-typescript": "12.1.0", + "@testing-library/dom": "8.20.1", + "@testing-library/jest-dom": "6.5.0", + "@testing-library/react": "16.0.1", + "@testing-library/user-event": "14.5.1", + "@types/jest": "29.5.13", + "@types/react": "18.3.3", + "@types/testing-library__jest-dom": "5.14.9", + "@utrecht/form-label-css": "workspace:*", + "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", + "react": "18.3.1", + "rollup": "4.23.0", + "rollup-plugin-filesize": "10.0.0", + "rollup-plugin-node-externals": "7.1.2", + "rollup-plugin-peer-deps-external": "2.2.4", + "rollup-plugin-postcss": "4.0.2", + "typescript": "5.6.2" + }, + "keywords": [ + "nl-design-system" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git+ssh", + "url": "git@github.com:nl-design-system/utrecht.git", + "directory": "packages/components-react/form-label-react" + }, + "peerDependencies": { + "@babel/runtime": "*", + "react": "18", + "react-dom": "18" + }, + "dependencies": { + "clsx": "2.1.1" + } +} diff --git a/packages/components-react/form-label-react/rollup.config.mjs b/packages/components-react/form-label-react/rollup.config.mjs new file mode 100644 index 00000000000..97d452b8ca0 --- /dev/null +++ b/packages/components-react/form-label-react/rollup.config.mjs @@ -0,0 +1,87 @@ +import babel from '@rollup/plugin-babel'; +import commonjs from '@rollup/plugin-commonjs'; +import json from '@rollup/plugin-json'; +import resolve from '@rollup/plugin-node-resolve'; +import typescript from '@rollup/plugin-typescript'; +import { readFileSync } from 'fs'; +import filesize from 'rollup-plugin-filesize'; +import nodeExternal from 'rollup-plugin-node-externals'; +import peerDepsExternal from 'rollup-plugin-peer-deps-external'; +import postcss from 'rollup-plugin-postcss'; + +const packagePath = new URL('./package.json', import.meta.url).pathname; +const packageJson = JSON.parse(readFileSync(packagePath, 'utf8')); + +/** + * @type {import('rollup').RollupOptions} + */ +export default [ + { + input: './src/index.tsx', + output: [ + { + file: packageJson.main, + format: 'cjs', + sourcemap: true, + }, + { + file: packageJson.module, + format: 'es', + sourcemap: true, + }, + ], + plugins: [ + peerDepsExternal(), + commonjs(), + nodeExternal(), + resolve({ browser: true }), + json(), + typescript({ tsconfig: './tsconfig.build.json' }), + babel({ + presets: ['@babel/preset-react'], + babelHelpers: 'runtime', + exclude: ['node_modules/**', 'dist/**'], + extensions: ['.ts', '.tsx'], + inputSourceMap: true, + plugins: ['@babel/plugin-transform-runtime'], + }), + filesize(), + ], + }, + { + input: './src/css.tsx', + output: [ + { + file: './dist/css.js', + format: 'cjs', + sourcemap: true, + }, + { + file: './dist/css.mjs', + format: 'es', + sourcemap: true, + }, + ], + plugins: [ + peerDepsExternal(), + commonjs(), + nodeExternal(), + resolve({ browser: true }), + json(), + postcss({ + extensions: ['.css', '.scss'], + minimize: true, + }), + typescript({ tsconfig: './tsconfig.build.json' }), + babel({ + presets: ['@babel/preset-react'], + babelHelpers: 'runtime', + exclude: ['node_modules/**', 'dist/**'], + extensions: ['.ts', '.tsx'], + inputSourceMap: true, + plugins: ['@babel/plugin-transform-runtime'], + }), + filesize(), + ], + }, +]; diff --git a/packages/components-react/form-label-react/src/css.tsx b/packages/components-react/form-label-react/src/css.tsx new file mode 100644 index 00000000000..c4f065eb182 --- /dev/null +++ b/packages/components-react/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/components-react/form-label-react/src/index.test.tsx b/packages/components-react/form-label-react/src/index.test.tsx new file mode 100644 index 00000000000..3bcd6008fca --- /dev/null +++ b/packages/components-react/form-label-react/src/index.test.tsx @@ -0,0 +1,147 @@ +import { render, screen } from '@testing-library/react'; +import { createRef } from 'react'; +import { FormLabel } from './index'; +import '@testing-library/jest-dom'; + +describe('Form label', () => { + it('renders an HTML label element', () => { + const { container } = render(); + + const label = container.querySelector('label:only-child'); + + expect(label).toBeInTheDocument(); + expect(label).toBeVisible(); + }); + + it('renders an HTML label element with for attribute', () => { + const { container } = render(); + + const label = container.querySelector('label[for="form-control"]:only-child'); + + expect(label).toBeInTheDocument(); + }); + + it('renders a design system BEM class name', () => { + const { container } = render(); + + const label = container.querySelector(':only-child'); + + expect(label).toHaveClass('utrecht-form-label'); + }); + + describe('variant for radio button in radio group', () => { + it('renders a design system BEM class name', () => { + const { container } = render(); + + const label = container.querySelector(':only-child'); + + expect(label).toHaveClass('utrecht-form-label--radio'); + }); + }); + + describe('variant for checkbox in checkbox group', () => { + it('renders a design system BEM class name', () => { + const { container } = render(); + + const label = container.querySelector(':only-child'); + + expect(label).toHaveClass('utrecht-form-label--checkbox'); + }); + }); + + describe('variant for a disabled form control', () => { + it('renders a design system BEM class name', () => { + const { container } = render(); + + const label = container.querySelector(':only-child'); + + expect(label).toHaveClass('utrecht-form-label--disabled'); + }); + }); + + describe('variant for a selected checkbox or radio button', () => { + it('renders a design system BEM class name', () => { + const { container } = render(); + + const label = container.querySelector(':only-child'); + + expect(label).toHaveClass('utrecht-form-label--checked'); + }); + }); + + // Skipped because in js-dom the element unexpectedly does not have an associated `display` style + it.skip('displays as CSS inline element', () => { + const { container } = render(); + + const label = container.querySelector(':only-child'); + + expect(label).toBeVisible(); + expect(label).toHaveStyle({ display: 'inline' }); + }); + + it('renders rich text content', () => { + const { container } = render( + + Current password + , + ); + + const label = container.querySelector(':only-child'); + + const richText = label?.querySelector('strong'); + + expect(richText).toBeInTheDocument(); + }); + + it('can be associated with an HTML form input', () => { + render( + <> + Email + + , + ); + + const textbox = screen.getByRole('textbox', { + name: 'Email', + }); + + expect(textbox).toBeInTheDocument(); + expect(textbox).toBeVisible(); + }); + + it('can be hidden', () => { + const { container } = render(