diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..3c7361087 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,78 @@ +version: 2 + +updates: + # Update npm packages + - package-ecosystem: npm + directory: / + + # Group packages into shared PR + groups: + build: + patterns: + - '@babel/*' + - '@rollup/*' + - '@types/react' + - '@types/react-*' + - 'classnames' + - 'nhsuk-frontend' + - 'react' + - 'react-dom' + - 'rollup' + - 'tslib' + + lint: + patterns: + - '@eslint/*' + - '@types/*' + - 'eslint' + - 'eslint-*' + - 'globals' + - 'prettier' + - 'typescript' + - 'typescript-*' + + # Exclude packages in other groups + exclude-patterns: + - '@types/jest' + - '@types/jest-*' + - '@types/react' + - '@types/react-*' + + test: + patterns: + - '@testing-library/*' + - '@types/jest' + - '@types/jest-*' + - 'babel-*' + - 'jest' + - 'jest-*' + + tools: + patterns: + - '@storybook/*' + - 'lodash' + - 'outdent' + - 'sass-embedded' + - 'storybook' + - 'vite' + - 'vite-*' + + schedule: + interval: monthly + + versioning-strategy: increase + + allow: + # Include direct package.json updates + - dependency-type: direct + + # Include indirect browser data updates + # https://caniuse.com + - dependency-name: caniuse-lite + + # Update GitHub Actions + - package-ecosystem: github-actions + directory: / + + schedule: + interval: 'monthly' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0079701cb..aafa47b8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,30 +2,62 @@ name: CI Build on: push: - branches: main + branches: [main] + pull_request: jobs: build: runs-on: ubuntu-latest + env: + CHROMATIC_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }} + CHROMATIC_SHA: ${{ github.event.pull_request.head.sha || github.ref }} + CHROMATIC_SLUG: ${{ github.repository }} + CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v5 + + - name: Enable corepack + run: corepack enable + - name: Set up Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v5 with: + cache: yarn node-version-file: .nvmrc - - name: Enable corepack - run: corepack enable - - name: Set Yarn version - run: yarn set version stable + - name: Yarn Install run: yarn + - name: Lint run: yarn lint + - name: Jest Tests run: yarn test --coverage + - name: Typescript build run: yarn build + - name: Storybook build run: yarn build-storybook + + - name: Switch to PR branch + if: ${{ github.event_name == 'pull_request' && env.CHROMATIC_PROJECT_TOKEN }} + uses: actions/checkout@v5 + with: + clean: false + fetch-depth: 0 + ref: ${{ env.CHROMATIC_BRANCH }} + + - name: Storybook deploy + uses: chromaui/action@v13 + with: + autoAcceptChanges: main + branchName: ${{ env.CHROMATIC_BRANCH}} + onlyChanged: ${{ github.event_name == 'pull_request' }} + projectToken: ${{ env.CHROMATIC_PROJECT_TOKEN }} + token: ${{ env.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da2ef3182..54ad07efd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,20 +9,19 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version-file: .nvmrc - - name: Enable corepack run: corepack enable - - name: Set Yarn version - run: yarn set version stable + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + cache: yarn + node-version-file: .nvmrc - name: Yarn Install run: yarn diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml deleted file mode 100644 index 5e1b5da38..000000000 --- a/.github/workflows/storybook.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Build & Deploy Storybook - -on: - push: - branches: - - main - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version-file: .nvmrc - - - name: Enable corepack - run: corepack enable - - - name: Set Yarn version - run: yarn set version stable - - - name: Yarn Install - run: yarn install - - - name: Deploy Storybook - uses: chromaui/action@v1 - with: - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.storybook/main.ts b/.storybook/main.ts index 41dbb8186..193423c10 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,6 +1,7 @@ -import type { StorybookConfig } from '@storybook/react-vite'; -import { mergeConfig } from 'vite'; +import { type StorybookConfig } from '@storybook/react-vite'; +import { mergeConfig, type InlineConfig } from 'vite'; import tsConfigPaths from 'vite-tsconfig-paths'; +import { isLogIgnored } from '../rollup.config.js'; const config: StorybookConfig = { stories: ['../stories/**/*.stories.@(ts|tsx)', '../stories/**/*.mdx'], @@ -16,7 +17,17 @@ const config: StorybookConfig = { viteFinal(config) { return mergeConfig(config, { - plugins: [tsConfigPaths()], + build: { + rollupOptions: { + onwarn(warning, handler) { + if (isLogIgnored(warning)) { + return; + } + + handler(warning); + }, + }, + }, css: { preprocessorOptions: { scss: { @@ -25,7 +36,15 @@ const config: StorybookConfig = { }, }, }, - }); + esbuild: { + jsx: 'automatic', + }, + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.dev.json', './tsconfig.build.json'], + }), + ], + } satisfies InlineConfig); }, }; diff --git a/.yarnrc.yml b/.yarnrc.yml index 52c22f2df..b3fa21cff 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -7,11 +7,9 @@ packageExtensions: peerDependencies: "@types/react": "*" "@types/react-dom": "*" - "@storybook/react-vite@*": peerDependencies: typescript: "*" - - "storybook@*": + storybook@*: peerDependencies: "@testing-library/dom": "*" diff --git a/babel.config.cjs b/babel.config.cjs index 114f1daf9..26f171e80 100644 --- a/babel.config.cjs +++ b/babel.config.cjs @@ -1,4 +1,6 @@ -const { NODE_ENV } = process.env; +// Node.js environment with default +// https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production +const { NODE_ENV = 'development' } = process.env; /** * Babel config @@ -25,7 +27,7 @@ module.exports = { [ '@babel/preset-react', { - development: NODE_ENV === 'development', + development: NODE_ENV === 'test' || NODE_ENV === 'development', runtime: 'automatic', useBuiltIns: true, }, diff --git a/package.json b/package.json index d0cc1bd23..862ab6539 100644 --- a/package.json +++ b/package.json @@ -52,16 +52,16 @@ ], "scripts": { "cleanup": "rm -rf dist/ > /dev/null", - "build": "yarn cleanup && rollup -c", - "storybook": "NODE_ENV=development storybook dev -p 6006", + "build": "NODE_ENV=production yarn cleanup && rollup -c", + "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", - "test": "jest", - "test:watch": "jest --watch", + "test": "jest --color", + "test:watch": "yarn test --watch", "lint": "yarn lint:types && yarn lint:js && yarn lint:prettier", "lint:fix": "yarn lint:js:fix && yarn lint:prettier:fix", "lint:prettier": "prettier --check .", "lint:prettier:fix": "prettier --write .", - "lint:js": "eslint . --max-warnings 0", + "lint:js": "eslint . --color --max-warnings 0", "lint:js:fix": "yarn lint:js --fix", "lint:types": "tsc --build tsconfig.json --pretty", "prepublishOnly": "yarn lint && yarn test && yarn storybook --smoke-test" diff --git a/rollup.config.js b/rollup.config.js index fadfee86c..3de4a7c56 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -40,7 +40,7 @@ export default defineConfig( }, ], external: ['react/jsx-runtime', ...external], - jsx: /** @type {const} */ ('react-jsx'), + jsx: 'react-jsx', treeshake: false, plugins: [ nodeResolve({ @@ -63,10 +63,7 @@ export default defineConfig( // Handle warnings as errors onwarn(warning) { - const { code, message } = warning; - - // Skip warnings about "use client" directives - if (code === 'MODULE_LEVEL_DIRECTIVE' && message.includes(`"use client"`)) { + if (isLogIgnored(warning)) { return; } @@ -77,5 +74,21 @@ export default defineConfig( ); /** - * @import { OutputOptions, RollupOptions } from 'rollup' + * Whether to ignore Rollup log messages + * + * @param {RollupLog} warning + */ +export function isLogIgnored(warning) { + const { code, message } = warning; + + // Skip warnings related to "use client" directives including + // source map issues when directives are bundled by Storybook + return ( + code === 'SOURCEMAP_ERROR' || + (code === 'MODULE_LEVEL_DIRECTIVE' && message.includes('"use client"')) + ); +} + +/** + * @import { OutputOptions, RollupLog, RollupOptions } from 'rollup' */