diff --git a/.husky/pre-commit b/.husky/pre-commit index 5ee7abd..c27d889 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -pnpm exec lint-staged +lint-staged diff --git a/eslint.config.mjs b/eslint.config.mjs index dccfa1f..764c770 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,10 +1,11 @@ -import globals from 'globals'; -import json from '@eslint/json'; import js from '@eslint/js'; -import tseslint from 'typescript-eslint'; +import json from '@eslint/json'; import eslintConfigPrettier from 'eslint-config-prettier'; import eslintPluginJsxA11y from 'eslint-plugin-jsx-a11y'; +import eslintPluginPerfectionist from 'eslint-plugin-perfectionist'; import eslintPluginReact from 'eslint-plugin-react'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { @@ -35,8 +36,10 @@ export default tseslint.config( ...json.configs.recommended, }, ...tseslint.configs.recommended, + eslintPluginPerfectionist.configs['recommended-natural'], { files: ['**/*.tsx'], + ignores: ['**/*.test.tsx'], ...eslintPluginReact.configs.flat.recommended, ...eslintPluginReact.configs.flat['jsx-runtime'], ...eslintPluginJsxA11y.flatConfigs.strict, @@ -47,5 +50,11 @@ export default tseslint.config( '@typescript-eslint/no-require-imports': 'off', }, }, + { + files: ['**/*.test.ts', '**/*.test.tsx'], + languageOptions: { + globals: { ...globals.jest }, + }, + }, eslintConfigPrettier, ); diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs index 447af1b..da7c60e 100644 --- a/lint-staged.config.mjs +++ b/lint-staged.config.mjs @@ -1,7 +1,7 @@ export default { - '*': 'prettier --ignore-unknown --write', + '*.{css,scss}': ['stylelint --allow-empty-input --fix', 'prettier --write'], + '*.{js,cjs,mjs,jsx,ts,tsx}': ['eslint --no-error-on-unmatched-pattern --fix', 'prettier --write'], + '*.{yml,yaml}': 'prettier --write', + '*.md': ['markdownlint', 'prettier --write'], 'package.json': 'npmPkgJsonLint --allowEmptyTargets', - '*.md': 'markdownlint', - '*.{js,cjs,mjs,json,jsx,mdx,ts,tsx}': 'eslint --no-error-on-unmatched-pattern', - '*.{css,scss}': 'stylelint --allow-empty-input', }; diff --git a/npmpackagejsonlint.config.cjs b/npmpackagejsonlint.config.cjs index 93454e8..b6d06fd 100644 --- a/npmpackagejsonlint.config.cjs +++ b/npmpackagejsonlint.config.cjs @@ -1,4 +1,12 @@ module.exports = { + overrides: [ + { + patterns: ['proprietary/**/package.json'], + rules: { + 'valid-values-license': ['error', ['SEE LICENSE IN LICENSE.md']], + }, + }, + ], rules: { 'no-caret-version-dependencies': 'error', 'no-caret-version-devDependencies': 'error', @@ -8,12 +16,4 @@ module.exports = { 'valid-values-license': ['error', ['EUPL-1.2']], 'valid-values-name-scope': ['error', ['@nl-design-system-candidate']], }, - overrides: [ - { - patterns: ['proprietary/**/package.json'], - rules: { - 'valid-values-license': ['error', ['SEE LICENSE IN LICENSE.md']], - }, - }, - ], }; diff --git a/package.json b/package.json index 01458dd..a4fb010 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "eslint": "9.12.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jsx-a11y": "6.10.0", + "eslint-plugin-perfectionist": "3.9.1", "eslint-plugin-react": "7.37.1", "globals": "15.11.0", "husky": "9.1.6", diff --git a/packages/components-react/paragraph-react/src/paragraph.test.tsx b/packages/components-react/paragraph-react/src/paragraph.test.tsx index dd0eec5..c964ed4 100644 --- a/packages/components-react/paragraph-react/src/paragraph.test.tsx +++ b/packages/components-react/paragraph-react/src/paragraph.test.tsx @@ -1,5 +1,6 @@ import '@testing-library/jest-dom'; import { render } from '@testing-library/react'; + import { Paragraph } from './paragraph'; describe('Paragraph', () => { diff --git a/packages/components-react/paragraph-react/src/paragraph.tsx b/packages/components-react/paragraph-react/src/paragraph.tsx index f125640..01f9125 100644 --- a/packages/components-react/paragraph-react/src/paragraph.tsx +++ b/packages/components-react/paragraph-react/src/paragraph.tsx @@ -1,6 +1,7 @@ +import type { HTMLAttributes } from 'react'; + import { clsx } from 'clsx'; import { forwardRef } from 'react'; -import type { HTMLAttributes } from 'react'; export interface ParagraphProps extends HTMLAttributes { appearance?: 'lead' | 'small'; diff --git a/packages/storybook/config/main.ts b/packages/storybook/config/main.ts index ff04df4..a12065b 100644 --- a/packages/storybook/config/main.ts +++ b/packages/storybook/config/main.ts @@ -1,7 +1,6 @@ import type { StorybookConfig } from '@storybook/react-vite'; const config: StorybookConfig = { - stories: ['../src/**/*stories.@(js|jsx|ts|tsx)', '../src/**/*.mdx'], addons: [ '@storybook/addon-a11y', '@storybook/addon-docs', @@ -9,10 +8,6 @@ const config: StorybookConfig = { '@whitespace/storybook-addon-html', '@etchteam/storybook-addon-status', ], - framework: { - name: '@storybook/react-vite', - options: {}, - }, core: { disableTelemetry: true, disableWhatsNewNotifications: true, @@ -20,6 +15,11 @@ const config: StorybookConfig = { docs: { autodocs: 'tag', }, + framework: { + name: '@storybook/react-vite', + options: {}, + }, + stories: ['../src/**/*stories.@(js|jsx|ts|tsx)', '../src/**/*.mdx'], }; export default config; diff --git a/packages/storybook/config/preview.ts b/packages/storybook/config/preview.ts index 8e8dfbb..c89488f 100644 --- a/packages/storybook/config/preview.ts +++ b/packages/storybook/config/preview.ts @@ -1,17 +1,19 @@ import type { Preview } from '@storybook/react'; + import { ParametersArgsDecorator } from './ParametersArgsDecorator'; const preview: Preview = { + decorators: [ParametersArgsDecorator], parameters: { controls: { expanded: false }, options: { panelPosition: 'right' }, status: { statuses: { - PRODUCTION: { - background: '#088008', - color: '#ffffff', + ALPHA: { + background: '#e0bc2e', + color: '#000000', description: - 'Used in production in a variety of situations, well tested, stable APIs, mostly patches and minor releases.', + 'Used in prototypes and in projects that are still in development, breaking changes occur frequently and are not communicated.', }, BETA: { background: '#3065ee', @@ -19,11 +21,11 @@ const preview: Preview = { description: 'Used in production in a specific situation, evolving APIs based on feedback, breaking changes are still likely.', }, - ALPHA: { - background: '#e0bc2e', - color: '#000000', + PRODUCTION: { + background: '#088008', + color: '#ffffff', description: - 'Used in prototypes and in projects that are still in development, breaking changes occur frequently and are not communicated.', + 'Used in production in a variety of situations, well tested, stable APIs, mostly patches and minor releases.', }, 'WORK IN PROGRESS': { background: '#cc0000', @@ -34,7 +36,6 @@ const preview: Preview = { }, }, }, - decorators: [ParametersArgsDecorator], }; export default preview; diff --git a/packages/storybook/src/paragraph.stories.tsx b/packages/storybook/src/paragraph.stories.tsx index c1ecb96..0142d16 100644 --- a/packages/storybook/src/paragraph.stories.tsx +++ b/packages/storybook/src/paragraph.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from '@storybook/react'; + import { Paragraph } from '../../components-react/paragraph-react/src/paragraph'; const meta = { - component: Paragraph, args: { children: 'Op brute wijze ving de schooljuf de quasi-kalme lynx.', }, + component: Paragraph, } satisfies Meta; export default meta; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0bfa47..4060d65 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,9 @@ importers: eslint-plugin-jsx-a11y: specifier: 6.10.0 version: 6.10.0(eslint@9.12.0) + eslint-plugin-perfectionist: + specifier: 3.9.1 + version: 3.9.1(eslint@9.12.0)(typescript@5.6.3) eslint-plugin-react: specifier: 7.37.1 version: 7.37.1(eslint@9.12.0) @@ -2841,6 +2844,25 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-perfectionist@3.9.1: + resolution: {integrity: sha512-9WRzf6XaAxF4Oi5t/3TqKP5zUjERhasHmLFHin2Yw6ZAp/EP/EVA2dr3BhQrrHWCm5SzTMZf0FcjDnBkO2xFkA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + astro-eslint-parser: ^1.0.2 + eslint: '>=8.0.0' + svelte: '>=3.0.0' + svelte-eslint-parser: ^0.41.1 + vue-eslint-parser: '>=9.0.0' + peerDependenciesMeta: + astro-eslint-parser: + optional: true + svelte: + optional: true + svelte-eslint-parser: + optional: true + vue-eslint-parser: + optional: true + eslint-plugin-react@7.37.1: resolution: {integrity: sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==} engines: {node: '>=4'} @@ -4126,6 +4148,10 @@ packages: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -4159,6 +4185,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -9029,6 +9058,17 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.1 + eslint-plugin-perfectionist@3.9.1(eslint@9.12.0)(typescript@5.6.3): + dependencies: + '@typescript-eslint/types': 8.9.0 + '@typescript-eslint/utils': 8.9.0(eslint@9.12.0)(typescript@5.6.3) + eslint: 9.12.0 + minimatch: 9.0.5 + natural-compare-lite: 1.4.0 + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-react@7.37.1(eslint@9.12.0): dependencies: array-includes: 3.1.8 @@ -10601,6 +10641,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + minimist-options@4.1.0: dependencies: arrify: 1.0.1 @@ -10625,6 +10669,8 @@ snapshots: nanoid@3.3.7: {} + natural-compare-lite@1.4.0: {} + natural-compare@1.4.0: {} negotiator@0.6.3: {} diff --git a/prettier.config.mjs b/prettier.config.mjs index ee96c3e..26177ae 100644 --- a/prettier.config.mjs +++ b/prettier.config.mjs @@ -2,8 +2,6 @@ * @type {import('prettier').Config} */ export default { - printWidth: 120, - singleQuote: true, overrides: [ { files: ['*.yml', '*.yaml'], @@ -18,4 +16,6 @@ export default { }, }, ], + printWidth: 120, + singleQuote: true, }; diff --git a/stylelint.config.mjs b/stylelint.config.mjs index e94f30d..186f1a9 100644 --- a/stylelint.config.mjs +++ b/stylelint.config.mjs @@ -1,67 +1,54 @@ export default { extends: ['stylelint-config-standard-scss'], - plugins: ['stylelint-order'], ignoreFiles: ['**/dist/'], + plugins: ['stylelint-order'], rules: { - 'order/order': ['custom-properties', 'declarations'], - 'order/properties-alphabetical-order': true, - 'scss/at-mixin-pattern': '^[a-z][a-z0-9-_]*$', - 'scss/at-rule-no-unknown': true, - 'scss/dollar-variable-default': true, - 'scss/dollar-variable-first-in-block': [true, { ignore: ['comments', 'imports'] }], - 'scss/dollar-variable-pattern': '^nl-[a-z0-9-]+$', - 'scss/percent-placeholder-pattern': '^nl-[a-z0-9-]+$', - 'scss/operator-no-newline-after': null, - 'scss/at-extend-no-missing-placeholder': null, - 'custom-property-pattern': '^_?nl-[a-z0-9-]+$', - 'selector-class-pattern': '^nl-[a-z0-9_-]+$', - 'keyframes-name-pattern': '^nl-[a-z0-9-]+$', + 'alpha-value-notation': ['percentage'], + 'at-rule-empty-line-before': null, 'at-rule-no-unknown': null, 'block-no-empty': [true], + 'color-function-notation': ['modern'], 'color-no-invalid-hex': [true], 'comment-no-empty': [true], + 'custom-property-pattern': '^_?nl-[a-z0-9-]+$', 'declaration-block-no-duplicate-properties': [true, { ignore: ['consecutive-duplicates-with-different-values'] }], + 'declaration-block-no-redundant-longhand-properties': null, 'declaration-block-no-shorthand-property-overrides': [true], - 'font-family-no-duplicate-names': [true], - 'font-family-no-missing-generic-family-keyword': [true], - 'function-calc-no-unspaced-operator': [true], - 'function-linear-gradient-no-nonstandard-direction': [true], - 'keyframe-declaration-no-important': [true], - 'media-feature-name-no-unknown': [true], - 'no-descending-specificity': [true], - 'no-duplicate-at-import-rules': [true], - 'no-duplicate-selectors': [true], - 'no-empty-source': [true], - 'no-invalid-double-slash-comments': [true], - 'property-no-unknown': [true], - 'selector-pseudo-class-no-unknown': [true], - 'selector-pseudo-element-no-unknown': [true], - 'string-no-newline': [true], - 'unit-no-unknown': [true], - 'alpha-value-notation': ['percentage'], - 'hue-degree-notation': ['number'], - 'color-function-notation': ['modern'], - 'length-zero-no-unit': [ - true, + 'declaration-property-value-disallowed-list': [ { - ignore: ['custom-properties'], + 'text-align': ['left', 'right'], }, ], + 'font-family-name-quotes': ['always-unless-keyword'], + 'font-family-no-duplicate-names': [true], + 'font-family-no-missing-generic-family-keyword': [true], 'font-weight-notation': [ 'numeric', { ignore: ['relative'], }, ], + 'function-calc-no-unspaced-operator': [true], + 'function-linear-gradient-no-nonstandard-direction': [true], 'function-url-no-scheme-relative': [true], - 'unit-disallowed-list': [['s']], - 'font-family-name-quotes': ['always-unless-keyword'], 'function-url-quotes': ['always'], - 'declaration-property-value-disallowed-list': [ + 'hue-degree-notation': ['number'], + 'keyframe-declaration-no-important': [true], + 'keyframes-name-pattern': '^nl-[a-z0-9-]+$', + 'length-zero-no-unit': [ + true, { - 'text-align': ['left', 'right'], + ignore: ['custom-properties'], }, ], + 'media-feature-name-no-unknown': [true], + 'no-descending-specificity': [true], + 'no-duplicate-at-import-rules': [true], + 'no-duplicate-selectors': [true], + 'no-empty-source': [true], + 'no-invalid-double-slash-comments': [true], + 'order/order': ['custom-properties', 'declarations'], + 'order/properties-alphabetical-order': true, 'property-disallowed-list': [ [ 'border-bottom', @@ -116,12 +103,25 @@ export default { 'width', ], ], - 'selector-max-id': [0], + 'property-no-unknown': [true], + 'property-no-vendor-prefix': null, + 'rule-empty-line-before': null, + 'scss/at-extend-no-missing-placeholder': null, + 'scss/at-mixin-pattern': '^[a-z][a-z0-9-_]*$', + 'scss/at-rule-no-unknown': true, + 'scss/dollar-variable-default': true, + 'scss/dollar-variable-first-in-block': [true, { ignore: ['comments', 'imports'] }], + 'scss/dollar-variable-pattern': '^nl-[a-z0-9-]+$', + 'scss/operator-no-newline-after': null, + 'scss/percent-placeholder-pattern': '^nl-[a-z0-9-]+$', 'selector-attribute-quotes': ['always'], + 'selector-class-pattern': '^nl-[a-z0-9_-]+$', + 'selector-max-id': [0], + 'selector-pseudo-class-no-unknown': [true], + 'selector-pseudo-element-no-unknown': [true], + 'string-no-newline': [true], + 'unit-disallowed-list': [['s']], + 'unit-no-unknown': [true], 'value-keyword-case': ['lower', { camelCaseSvgKeywords: true }], - 'declaration-block-no-redundant-longhand-properties': null, - 'at-rule-empty-line-before': null, - 'rule-empty-line-before': null, - 'property-no-vendor-prefix': null, }, };