Skip to content

Commit

Permalink
feat: create form-field-react package
Browse files Browse the repository at this point in the history
  • Loading branch information
AliKdhim87 authored and Robbert committed Oct 21, 2024
1 parent 9fc1c3b commit dce26e7
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-kings-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@utrecht/form-field-react": major
---

Create form-field-react package
8 changes: 8 additions & 0 deletions packages/components-react/form-field-react/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"env": {
"jest/globals": true
},
"rules": {
"react/react-in-jsx-scope": "off"
}
}
12 changes: 12 additions & 0 deletions packages/components-react/form-field-react/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
presets: [
'@babel/preset-env',
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
};
8 changes: 8 additions & 0 deletions packages/components-react/form-field-react/jest.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
testPathIgnorePatterns: ['/dist/'],
moduleNameMapper: {
'^@utrecht/(.*)$': '<rootDir>/../$1/src/',
},
};
68 changes: 68 additions & 0 deletions packages/components-react/form-field-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "@utrecht/form-field-react",
"version": "0.0.0",
"author": "Community for NL Design System",
"description": "Form Field 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-field-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-field-react"
},
"peerDependencies": {
"@babel/runtime": "*",
"react": "18",
"react-dom": "18"
},
"dependencies": {
"clsx": "2.1.1"
}
}
87 changes: 87 additions & 0 deletions packages/components-react/form-field-react/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -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(),
],
},
];
9 changes: 9 additions & 0 deletions packages/components-react/form-field-react/src/css.tsx
Original file line number Diff line number Diff line change
@@ -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';
78 changes: 78 additions & 0 deletions packages/components-react/form-field-react/src/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { render, screen } from '@testing-library/react';
import { createRef } from 'react';
import { FormField } from './index';
import '@testing-library/jest-dom';

describe('Form field', () => {
it('renders an HTML div element', () => {
const { container } = render(<FormField />);

const field = container.querySelector('div');

expect(field).toBeInTheDocument();
});

it('renders a design system BEM class name', () => {
const { container } = render(<FormField />);

const field = container.querySelector('div');

expect(field).toHaveClass('utrecht-form-field');
});

it('displays as CSS block element (or equivalent)', () => {
const { container } = render(<FormField />);

const field = container.querySelector('div');

expect(field).toBeVisible();
expect(field).not.toHaveStyle({ display: 'inline' });
expect(field).not.toHaveStyle({ display: 'inline-block' });
});

it('renders rich text content', () => {
render(
<FormField>
<input type="text" />
</FormField>,
);

const textbox = screen.getByRole('textbox');

expect(textbox).toBeInTheDocument();
});

it('can be hidden', () => {
const { container } = render(<FormField hidden />);

const field = container.querySelector('div');

expect(field).not.toBeVisible();
});

it('can have a custom class name', () => {
const { container } = render(<FormField className="invalid" />);

const field = container.querySelector('div');

expect(field).toHaveClass('invalid');
});
it('can have a additional class name', () => {
const { container } = render(<FormField className="large" />);

const field = container.querySelector(':only-child');

expect(field).toHaveClass('large');
expect(field).toHaveClass('utrecht-form-field');
});

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLDivElement>();

const { container } = render(<FormField ref={ref} />);

const div = container.querySelector('div');

expect(ref.current).toBe(div);
});
});
51 changes: 51 additions & 0 deletions packages/components-react/form-field-react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import clsx from 'clsx';
import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react';

export interface FormFieldProps extends HTMLAttributes<HTMLDivElement> {
description?: ReactNode;
errorMessage?: 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,
errorMessage,
input,
invalid,
label,
type,
...restProps
}: PropsWithChildren<FormFieldProps>,
ref: ForwardedRef<HTMLDivElement>,
) => (
<div
{...restProps}
ref={ref}
className={clsx(
'utrecht-form-field',
{
'utrecht-form-field--invalid': invalid,
'utrecht-form-field--checkbox': type === 'checkbox',
'utrecht-form-field--radio': type === 'radio',
'utrecht-form-field--text': !type || type === 'text',
},
className,
)}
>
{label && <div className="utrecht-form-field__label">{label}</div>}
{description && <div className="utrecht-form-field__description">{description}</div>}
{input && <div className="utrecht-form-field__input">{input}</div>}
{errorMessage && <div className="utrecht-form-field__error-message">{errorMessage}</div>}
{children}
</div>
),
);

FormField.displayName = 'FormField';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"composite": true
}
}
9 changes: 9 additions & 0 deletions packages/components-react/form-field-react/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../tsconfig.base.json",
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "**/*.test.ts", "**/*.test.tsx"],
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
}
}
8 changes: 8 additions & 0 deletions packages/components-react/form-field-react/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"noEmit": true
}
}
Loading

0 comments on commit dce26e7

Please sign in to comment.