diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6c33db7 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,31 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs + +name: CI + +on: + push: + branches: [ "**" ] + pull_request: + branches: [ "**" ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [22.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm run ci-project + #- run: npm run build --if-present + - run: npm test diff --git a/.vscode/settings.json b/.vscode/settings.json index 2f5dfff..ad440a7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,4 +19,5 @@ "editor.trimAutoWhitespace": false, "files.eol": "\n", "javascript.preferences.quoteStyle": "single", + "typescript.tsdk": "node_modules/typescript/lib", // Use the TypeScript SDK from the current project } diff --git a/package-lock.json b/package-lock.json index 7e80b82..7601969 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@storybook/react": "^8.4.4", "@storybook/react-vite": "^8.4.4", "@storybook/test": "^8.4.4", + "@types/node": "^22.9.0", "@types/react": "npm:types-react@rc", "@types/react-dom": "npm:types-react-dom@rc", "@vitejs/plugin-react": "^4.3.3", @@ -3713,13 +3714,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", - "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, - "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@types/postcss-modules-local-by-default": { diff --git a/package.json b/package.json index 9003c98..3671374 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "scripts": { "gen-package": "node package.json.js", "install-project": "npm run gen-package && npm install --force", + "ci-project": "npm ci --force", "node": "node --import=tsx", "repl": "tsx", "plop": "NODE_OPTIONS=\"--import tsx\" plop", @@ -41,11 +42,11 @@ "build": "vite --config=./vite.config.ts --emptyOutDir build", "storybook:serve": "storybook dev -p 6006", "storybook:build": "storybook build --docs", - "check-types": "tsc --noEmit", + "check:types": "tsc --noEmit", "lint:style": "stylelint 'src/**/*.scss'", - "lint:script": "biome", - "lint": "npm run list:style && npm run lint:script", - "test": "vitest run --root=.", + "lint:script": "biome lint", + "lint": "npm run lint:style && npm run lint:script", + "test": "npm run check:types && npm run lint:style", "test-ui": "vitest --ui", "coverage": "vitest run --coverage", "start": "npm run storybook:serve", @@ -61,6 +62,7 @@ "vite-plugin-lib-inject-css": "^2.1.1", "vite-plugin-svg-icons": "^2.0.1", "typescript": "^5.6.3", + "@types/node": "^22.9.0", "stylelint": "^16.10.0", "stylelint-config-standard-scss": "^13.1.0", "@biomejs/biome": "^1.9.4", diff --git a/package.json.js b/package.json.js index 20ca9b2..6e92c5e 100644 --- a/package.json.js +++ b/package.json.js @@ -38,7 +38,9 @@ const packageConfig = { scripts: { // Utilities 'gen-package': 'node package.json.js', // Update `package.json` + // Use --force currently since we're using React v19 rc. Once peer deps are updated remove this. 'install-project': 'npm run gen-package && npm install --force', + 'ci-project': 'npm ci --force', // CLI 'node': 'node --import=tsx', @@ -59,14 +61,15 @@ const packageConfig = { 'storybook:build': 'storybook build --docs', // Static analysis - 'check-types': 'tsc --noEmit', + 'check:types': 'tsc --noEmit', 'lint:style': `stylelint 'src/**/*.scss'`, - 'lint:script': 'biome', - 'lint': 'npm run list:style && npm run lint:script', + 'lint:script': 'biome lint', + 'lint': 'npm run lint:style && npm run lint:script', // Test // Note: use `vitest run --root=. src/...` to run a single test file - 'test': 'vitest run --root=.', // Need to specify `--root=.` since the vite root is set to `./app` + //'test': 'vitest run --root=.', // Need to specify `--root=.` since the vite root is set to `./app` + 'test': 'npm run check:types && npm run lint:style', 'test-ui': 'vitest --ui', 'coverage': 'vitest run --coverage', @@ -93,6 +96,7 @@ const packageConfig = { // Static analysis 'typescript': '^5.6.3', + '@types/node': '^22.9.0', 'stylelint': '^16.10.0', 'stylelint-config-standard-scss': '^13.1.0', '@biomejs/biome': '^1.9.4', diff --git a/scripts/import.ts b/scripts/import.ts index b0c579f..31645dc 100755 --- a/scripts/import.ts +++ b/scripts/import.ts @@ -26,8 +26,8 @@ const getServices = () => { type ScriptArgs = { values: { - help: boolean | undefined, - silent: boolean | undefined, + help?: undefined | boolean, + silent?: undefined | boolean, }, positionals: Array, }; diff --git a/src/components/actions/Link/Link.stories.tsx b/src/components/actions/Link/Link.stories.tsx index cfe244f..85ce645 100644 --- a/src/components/actions/Link/Link.stories.tsx +++ b/src/components/actions/Link/Link.stories.tsx @@ -45,7 +45,7 @@ export const Descenders: Story = { export const Scroll: Story = { render: (args) => ( <> - + { evt.preventDefault(); }}>Anchor diff --git a/src/components/containers/Alert/Alert.stories.tsx b/src/components/containers/Alert/Alert.stories.tsx index 1c1c17c..01e6b41 100644 --- a/src/components/containers/Alert/Alert.stories.tsx +++ b/src/components/containers/Alert/Alert.stories.tsx @@ -31,10 +31,10 @@ export default { export const Standard: Story = {}; -export const Info: Story = { args: { kind: 'info' } }; -export const Warning: Story = { args: { kind: 'warning' } }; -export const Error: Story = { args: { kind: 'error' } }; -export const Success: Story = { args: { kind: 'success' } }; +export const InfoAlert: Story = { args: { kind: 'info' } }; +export const WarningAlert: Story = { args: { kind: 'warning' } }; +export const ErrorAlert: Story = { args: { kind: 'error' } }; +export const SuccessAlert: Story = { args: { kind: 'success' } }; /** Multiple alerts stacked. */ export const Stacked: Story = { diff --git a/src/components/forms/context/Form/Form.stories.tsx b/src/components/forms/context/Form/Form.stories.tsx index 4878417..f20efb3 100644 --- a/src/components/forms/context/Form/Form.stories.tsx +++ b/src/components/forms/context/Form/Form.stories.tsx @@ -19,9 +19,9 @@ type FormArgs = React.ComponentProps; type Story = StoryObj; const FormWithState = (props: React.ComponentProps) => { - const action = async (previousState: unknown, formData: FormData) => { + const action = async (previousState: unknown, formData: FormData): Promise => { if (typeof props.action === 'function') { - return props.action?.(formData); + return props.action?.(formData) ?? null; } return null; }; diff --git a/src/components/forms/context/Form/FormOptics.tsx b/src/components/forms/context/Form/FormOptics.tsx index 7649346..3686afa 100644 --- a/src/components/forms/context/Form/FormOptics.tsx +++ b/src/components/forms/context/Form/FormOptics.tsx @@ -16,7 +16,7 @@ export { cl as FormClassNames }; export type FormContext = { formId: string, - accessor: O.OpticFor, + //accessor: O.OpticFor, }; export const FormContext = React.createContext>(null); export const useFormContext = (): FormContext => { @@ -43,7 +43,7 @@ export const Form = (props: FormProps) => { const [wrapperRef, setWrapperRef] = React.useState>(null); // Memoize to keep a stable reference - const context: FormContext = React.useMemo(() => ({ formId }), [formId]); + const context: FormContext = React.useMemo(() => ({ formId }), [formId]); const renderForm = ({ children }: { children: React.ReactNode }) => { return ( diff --git a/src/components/forms/context/SubmitButton/SubmitButton.tsx b/src/components/forms/context/SubmitButton/SubmitButton.tsx index 35ef7d8..3f9627f 100644 --- a/src/components/forms/context/SubmitButton/SubmitButton.tsx +++ b/src/components/forms/context/SubmitButton/SubmitButton.tsx @@ -45,13 +45,13 @@ export const SubmitButton = (props: SubmitButtonProps) => { const isDisabled = !isInteractive; const isNonactive = propsButton.nonactive || isPending; - const handlePress = () => { + const handlePress = React.useCallback(() => { if (typeof onPress !== 'function') { return; } startPressTransition(async () => { await Promise.race([onPress(), timeout(asyncTimeout)]); }); - }; + }, [onPress, asyncTimeout]); const handleClick = React.useCallback(async (event: React.MouseEvent) => { // `onClick` should not be used in most cases, only if the consumer needs low level control over click events. diff --git a/src/components/forms/controls/SegmentedControl/SegmentedControl.tsx b/src/components/forms/controls/SegmentedControl/SegmentedControl.tsx index 898f5bb..fbb961b 100644 --- a/src/components/forms/controls/SegmentedControl/SegmentedControl.tsx +++ b/src/components/forms/controls/SegmentedControl/SegmentedControl.tsx @@ -83,7 +83,8 @@ export const SegmentedControl = (props: SegmentedControlProps) => { [cl['bk-segmented-control--disabled']]: disabled, }, propsRest.className)} > - {formattedOptions.map((option, index) => + {formattedOptions.map((option, index) => + // biome-ignore lint/suspicious/noArrayIndexKey: no other unique identifier available