diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85a37fa887..6c4b4f543c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: | pnpm install @@ -36,12 +36,30 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: | pnpm install pnpm typecheck + oxlint: + runs-on: ubuntu-24.04 + env: + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_REMOTE_ONLY: true + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4.0.0 + - name: Use Node.js + uses: actions/setup-node@v4.1.0 + with: + node-version: 22 + cache: "pnpm" + - run: | + pnpm install + pnpm build + pnpm oxlint -c .oxlintrc.json + lint: runs-on: ubuntu-24.04 env: @@ -53,7 +71,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: | pnpm install @@ -71,7 +89,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: | pnpm install @@ -115,7 +133,7 @@ jobs: # - name: Use Node.js # uses: actions/setup-node@v4.0.2 # with: - # node-version: 20 + # node-version: 22 # cache: 'pnpm' # - run: pnpm install # - run: pnpm run build @@ -152,7 +170,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: | pnpm install diff --git a/.github/workflows/fonts-upload.yml b/.github/workflows/fonts-upload.yml index 68bb0f2ce2..b6a8d6ab08 100644 --- a/.github/workflows/fonts-upload.yml +++ b/.github/workflows/fonts-upload.yml @@ -26,7 +26,7 @@ jobs: - uses: pnpm/action-setup@v4.0.0 - uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: pnpm install diff --git a/.github/workflows/illustrations-update.yml b/.github/workflows/illustrations-update.yml index 75d670f394..273d4e95ed 100644 --- a/.github/workflows/illustrations-update.yml +++ b/.github/workflows/illustrations-update.yml @@ -28,7 +28,7 @@ jobs: - uses: pnpm/action-setup@v4.0.0 - uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: pnpm install diff --git a/.github/workflows/illustrations-upload.yml b/.github/workflows/illustrations-upload.yml index f5b792fee9..bf999b3708 100644 --- a/.github/workflows/illustrations-upload.yml +++ b/.github/workflows/illustrations-upload.yml @@ -26,7 +26,7 @@ jobs: - uses: pnpm/action-setup@v4.0.0 - uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: pnpm install diff --git a/.github/workflows/pull_request_title.yml b/.github/workflows/pull_request_title.yml index f6294552f7..88676982a3 100644 --- a/.github/workflows/pull_request_title.yml +++ b/.github/workflows/pull_request_title.yml @@ -12,7 +12,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: pnpm install --prod - name: Check PR title diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6dbb236948..13e235598f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: pnpm install - name: Create Release Pull Request or Publish to npm diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index 8e794cfdaf..d10fd5a238 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -6,7 +6,7 @@ jobs: size: strategy: matrix: - node: ["20"] + node: ["22"] runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/sync-design-tokens.yml b/.github/workflows/sync-design-tokens.yml index 9890c4294b..76b0a907c5 100644 --- a/.github/workflows/sync-design-tokens.yml +++ b/.github/workflows/sync-design-tokens.yml @@ -16,7 +16,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4.1.0 with: - node-version: 20 + node-version: 22 cache: "pnpm" - run: pnpm install - name: Generate json files diff --git a/.gitignore b/.gitignore index e67a454539..e123c0b021 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,7 @@ vitest.config.ts.* examples/*/.turbo packages/*/.turbo tools/*/.turbo + +# typescript +*.tsbuildinfo +.tsbuildinfo diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..7fffd833cf --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +save-prefix="" +engine-strict=true +package-manager-strict-version=true +manage-package-manager-versions=true diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000000..b0e543c0c9 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,160 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "deny", + "style": "deny", + "suspicious": "deny", + "perf": "deny", + "pedantic": "deny", + "restriction": "deny", + "nursery": "off" + }, + "plugins": [ + "import", + "node", + "react", + "security", + "n", + "tree_shaking", + "typescript", + "unicorn", + "vitest" + ], + "overrides": [ + { + "files": ["**/__stories__/**/*.{ts,tsx}"], + "rules": { + "react/jsx-key": "off", + "no-console": "off", + "no-alert": "off" + } + } + ], + "rules": { + "@typescript-eslint/ban-tslint-comment": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/consistent-type-definitions": ["error", "type"], + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-magic-numbers": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript/no-explicit-any": "warn", + "@typescript-eslint/prefer-enum-initializers": "off", + "@typescript-eslint/prefer-function-type": "off", + "@typescript-eslint/prefer-literal-enum-member": "off", + "@typescript-eslint/prefer-ts-expect-error": "off", + "eslint/default-param-last": "off", + "eslint/max-lines": "off", + "eslint/max-params": "off", + "eslint/no-await-in-loop": "off", + "eslint/no-duplicate-imports": "off", + "eslint/no-empty-function": "off", + "eslint/no-magic-numbers": "off", + "eslint/no-ternary": "off", + "eslint/no-undef": "off", + "eslint/no-undefined": "off", + "eslint/sort-keys": "off", + "eslint/react-in-jsx-scope": "off", + "eslint/no-unused-vars": [ + "error", + { + "ignoreRestSiblings": true + } + ], + "eslint/require-await": "off", + "eslint/sort-imports": [ + "error", + { + "ignoreDeclarationSort": true, + "memberSyntaxSortOrder": ["single", "multiple", "all", "none"] + } + ], + "import/export": "off", + "import/import-no-namespace": "off", + "import/max-dependencies": "off", + "import/namespace": "off", + "import/no-default-export": "off", + "import/no-deprecated": "warn", + "import/no-duplicates": "off", + "import/no-unused-modules": "off", + "import/unambiguous": "warn", + "jest/no-conditional-expect": "off", + "jest/no-confusing-set-timeout": "off", + "jest/no-hooks": "off", + "jest/no-restricted-jest-methods": "off", + "jest/no-restricted-matchers": "off", + "jest/no-standalone-expect": "off", + "jest/no-untyped-mock-factory": "off", + "jest/prefer-called-with": "off", + "jest/prefer-comparison-matcher": "off", + "jest/prefer-equality-matcher": "off", + "jest/prefer-expect-resolves": "off", + "jest/prefer-lowercase-title": "off", + "jest/prefer-mock-promise-shorthand": "off", + "jest/prefer-spy-on": "off", + "jest/prefer-strict-equal": "off", + "jest/prefer-to-be": "off", + "jest/prefer-to-contain": "off", + "jest/prefer-to-have-length": "off", + "jest/prefer-todo": "error", + "jest/require-hook": "off", + "jest/require-to-throw-message": "off", + "oxc/no-accumulating-spread": "off", + "oxc/no-async-await": "off", + "oxc/no-barrel-file": "off", + "oxc/no-optional-chaining": "off", + "oxc/no-rest-spread-properties": "off", + "react-perf/jsx-no-jsx-as-prop": "off", + "react-perf/jsx-no-new-array-as-prop": "off", + "react-perf/jsx-no-new-function-as-prop": "off", + "react-perf/jsx-no-new-object-as-prop": "off", + "react/jsx-no-useless-fragment": "off", + "react/exhaustive-deps": "warn", + "react/iframe-missing-sandbox": "warn", + "react/jsx-no-target-blank": "off", + "react/no-set-state": "off", + "react/react-in-jsx-scope": "off", + "unicorn/error-message": "off", + "unicorn/filename-case": "off", + "unicorn/no-anonymous-default-export": "off", + "unicorn/no-array-for-each": "off", + "unicorn/no-array-reduce": "off", + "unicorn/no-await-expression-member": "off", + "unicorn/no-await-in-promise-methods": "off", + "unicorn/no-lonely-if": "off", + "unicorn/prefer-set-has": "off", + "unicorn/no-document-cookie": "off", + "unicorn/no-invalid-remove-event-listener": "off", + "unicorn/no-magic-array-flat-depth": "off", + "unicorn/no-negated-condition": "off", + "unicorn/no-new-array": "off", + "unicorn/no-null": "off", + "unicorn/no-object-as-default-parameter": "off", + "unicorn/no-process-exit": "off", + "unicorn/no-single-promise-in-promise-methods": "off", + "unicorn/no-useless-promise-resolve-reject": "off", + "unicorn/no-useless-undefined": "off", + "unicorn/no-zero-fractions": "off", + "unicorn/number-literal-case": "off", + "unicorn/numeric-separators-style": "off", + "unicorn/prefer-add-event-listener": "off", + "unicorn/prefer-array-some": "off", + "unicorn/prefer-blob-reading-methods": "off", + "unicorn/prefer-code-point": "off", + "unicorn/prefer-dom-node-append": "off", + "unicorn/prefer-dom-node-remove": "off", + "unicorn/prefer-logical-operator-over-ternary": "off", + "unicorn/prefer-node-protocol": "off", + "unicorn/prefer-query-selector": "off", + "unicorn/prefer-string-replace-all": "off", + "unicorn/prefer-string-slice": "off" + }, + "settings": { + "jsx-a11y": { + "components": { + "polymorphicPropName": "as" + } + }, + "react": {} + } +} diff --git a/eslint.config.mjs b/eslint.config.mjs index 216e3cd4e3..913f25dc8d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,6 +4,7 @@ import { FlatCompat } from '@eslint/eslintrc' import scwEmotion from '@scaleway/eslint-config-react/emotion' import scwJavascript from '@scaleway/eslint-config-react/javascript' import scwTypescript from '@scaleway/eslint-config-react/typescript' +import oxlint from 'eslint-plugin-oxlint' import globals from 'globals' import path from 'node:path' import { fileURLToPath } from 'node:url' @@ -148,4 +149,5 @@ export default [ 'react/jsx-props-no-spreading': 'off', }, }, + oxlint.configs['flat/all'], ] diff --git a/examples/next-advanced/next.config.js b/examples/next-advanced/next.config.js index dbb306a4d5..a1cd5604ee 100644 --- a/examples/next-advanced/next.config.js +++ b/examples/next-advanced/next.config.js @@ -30,4 +30,4 @@ const nextConfig = () => { return config } -module.exports = nextConfig() +export default nextConfig() diff --git a/examples/next-advanced/src/pages/_document.tsx b/examples/next-advanced/src/pages/_document.tsx index ae15ef666c..9d110914c9 100644 --- a/examples/next-advanced/src/pages/_document.tsx +++ b/examples/next-advanced/src/pages/_document.tsx @@ -7,7 +7,7 @@ class MyDocument extends Document { return ( - + { return config } -module.exports = nextConfig() +export default nextConfig() diff --git a/examples/next-login/src/pages/_app.tsx b/examples/next-login/src/pages/_app.tsx index ed6cc75180..c0244b2538 100644 --- a/examples/next-login/src/pages/_app.tsx +++ b/examples/next-login/src/pages/_app.tsx @@ -1,6 +1,6 @@ -import { ThemeProvider, Global, css } from '@emotion/react' +import { Global, ThemeProvider, css } from '@emotion/react' import { Button } from '@ultraviolet/ui' -import { darkTheme, theme, normalize } from '@ultraviolet/ui' +import { darkTheme, normalize, theme } from '@ultraviolet/ui' import type { AppProps } from 'next/app' import { useState } from 'react' diff --git a/examples/next-login/src/pages/_document.tsx b/examples/next-login/src/pages/_document.tsx index ae15ef666c..9d110914c9 100644 --- a/examples/next-login/src/pages/_document.tsx +++ b/examples/next-login/src/pages/_document.tsx @@ -7,7 +7,7 @@ class MyDocument extends Document { return ( - + { let tabLoaded = undefined switch (props.tab) { - case 'login': + case 'login': { tabLoaded = break - default: + } + default: { tabLoaded = + } } return tabLoaded } diff --git a/examples/next-login/src/pages/home/login.tsx b/examples/next-login/src/pages/home/login.tsx index e4e921b794..9e2c6b63e9 100644 --- a/examples/next-login/src/pages/home/login.tsx +++ b/examples/next-login/src/pages/home/login.tsx @@ -1,10 +1,10 @@ -import { Text, Link, Stack } from '@ultraviolet/ui' +import { Link, Stack, Text } from '@ultraviolet/ui' import { IdIcon } from '@ultraviolet/icons' import { + CheckboxField, Form, - TextInputField, Submit, - CheckboxField, + TextInputField, useForm, useWatch, } from '@ultraviolet/form' @@ -49,6 +49,8 @@ const LogIn = () => { `, ) setTimeout(() => setLoginText(''), 3000) + + // oxlint-disable-next-line eslint/no-console console.log('Values :', values) } diff --git a/examples/next-login/src/pages/home/signup.tsx b/examples/next-login/src/pages/home/signup.tsx index 91a2b19463..ed61091c83 100644 --- a/examples/next-login/src/pages/home/signup.tsx +++ b/examples/next-login/src/pages/home/signup.tsx @@ -1,12 +1,12 @@ -import { Text, Stack, Alert } from '@ultraviolet/ui' +import { Alert, Stack, Text } from '@ultraviolet/ui' import { ProfileIcon } from '@ultraviolet/icons' import { + DateField, Form, - TextInputFieldV2, + RadioGroupField, Submit, + TextInputFieldV2, useForm, - RadioGroupField, - DateField, useWatch, } from '@ultraviolet/form' import { useState } from 'react' diff --git a/examples/next-simple/next.config.js b/examples/next-simple/next.config.js index 80683bf452..bdefa8a3c9 100644 --- a/examples/next-simple/next.config.js +++ b/examples/next-simple/next.config.js @@ -29,4 +29,4 @@ const nextConfig = () => { return config } -module.exports = nextConfig() +export default nextConfig() diff --git a/examples/next-simple/package.json b/examples/next-simple/package.json index ea117f28f2..9a88a5173b 100644 --- a/examples/next-simple/package.json +++ b/examples/next-simple/package.json @@ -8,6 +8,7 @@ "directory": "examples/next-simple" }, "private": true, + "type": "module", "scripts": { "dev": "next dev", "build": "next build", diff --git a/examples/next-simple/src/pages/_document.tsx b/examples/next-simple/src/pages/_document.tsx index ae15ef666c..9d110914c9 100644 --- a/examples/next-simple/src/pages/_document.tsx +++ b/examples/next-simple/src/pages/_document.tsx @@ -7,7 +7,7 @@ class MyDocument extends Document { return ( - + { + // oxlint-disable-next-line eslint/no-console console.log('data', data) }} errors={mockErrors} diff --git a/packages/form/src/components/Form/__stories__/Playground.stories.tsx b/packages/form/src/components/Form/__stories__/Playground.stories.tsx index b86cc53731..76add70e2b 100644 --- a/packages/form/src/components/Form/__stories__/Playground.stories.tsx +++ b/packages/form/src/components/Form/__stories__/Playground.stories.tsx @@ -238,6 +238,7 @@ export const Playground: StoryFn = () => { Playground.args = { onSubmit: values => { + // oxlint-disable-next-line eslint/no-console console.log('Submit', values) }, } diff --git a/packages/form/src/components/KeyValueField/index.tsx b/packages/form/src/components/KeyValueField/index.tsx index 3714e0d10f..c4baaceea1 100644 --- a/packages/form/src/components/KeyValueField/index.tsx +++ b/packages/form/src/components/KeyValueField/index.tsx @@ -63,7 +63,7 @@ export const KeyValueField = < return ( - {fields.length ? ( + {fields.length > 0 ? ( {fields.map((field, index) => ( & { + // oxlint-disable-next-line no-explicit-any parse?: (value: boolean) => any + // oxlint-disable-next-line no-explicit-any format?: (value: any) => boolean } diff --git a/packages/form/src/validators/__tests__/maxDate.test.ts b/packages/form/src/validators/__tests__/maxDate.test.ts index f7cf4c3f22..78dd1bc11a 100644 --- a/packages/form/src/validators/__tests__/maxDate.test.ts +++ b/packages/form/src/validators/__tests__/maxDate.test.ts @@ -10,15 +10,15 @@ const tomorrow = new Date(today.getTime() + 24 * hourInMs) describe('maxDate validator', () => { test('should success', () => { const validator = maxDateValidator(yesterday) - expect(validator(today)).toBe(false) - expect(validator(twoHoursLater)).toBe(false) - expect(validator(tomorrow)).toBe(false) + expect(validator(today)).toBeFalsy() + expect(validator(twoHoursLater)).toBeFalsy() + expect(validator(tomorrow)).toBeFalsy() }) test('should failed', () => { const validator = maxDateValidator(tomorrow) - expect(validator(yesterday)).toBe(true) - expect(validator(today)).toBe(true) - expect(validator(twoHoursLater)).toBe(true) + expect(validator(yesterday)).toBeTruthy() + expect(validator(today)).toBeTruthy() + expect(validator(twoHoursLater)).toBeTruthy() }) }) diff --git a/packages/form/src/validators/__tests__/minDate.test.ts b/packages/form/src/validators/__tests__/minDate.test.ts index be4f081a96..92f4414382 100644 --- a/packages/form/src/validators/__tests__/minDate.test.ts +++ b/packages/form/src/validators/__tests__/minDate.test.ts @@ -10,15 +10,15 @@ const tomorrow = new Date(today.getTime() + 24 * hourInMs) describe('minDate validator', () => { test('should invalidate the input', () => { const validator = minDateValidator(tomorrow) - expect(validator(today)).toBe(false) - expect(validator(twoHoursLater)).toBe(false) - expect(validator(yesterday)).toBe(false) + expect(validator(today)).toBeFalsy() + expect(validator(twoHoursLater)).toBeFalsy() + expect(validator(yesterday)).toBeFalsy() }) test('should validate the input', () => { const validator = minDateValidator(yesterday) - expect(validator(today)).toBe(true) - expect(validator(tomorrow)).toBe(true) - expect(validator(twoHoursLater)).toBe(true) + expect(validator(today)).toBeTruthy() + expect(validator(tomorrow)).toBeTruthy() + expect(validator(twoHoursLater)).toBeTruthy() }) }) diff --git a/packages/icons/src/components/CategoryIcon/__stories__/List.stories.tsx b/packages/icons/src/components/CategoryIcon/__stories__/List.stories.tsx index 767b86d7d7..cc3d709fe0 100644 --- a/packages/icons/src/components/CategoryIcon/__stories__/List.stories.tsx +++ b/packages/icons/src/components/CategoryIcon/__stories__/List.stories.tsx @@ -9,7 +9,7 @@ export const List: StoryFn = props => ( CategoryIcon[IconName as keyof typeof CategoryIcon] return ( - + diff --git a/packages/plus/src/components/ContentCard/__stories__/Click.stories.tsx b/packages/plus/src/components/ContentCard/__stories__/Click.stories.tsx index f71006f223..78edb7a858 100644 --- a/packages/plus/src/components/ContentCard/__stories__/Click.stories.tsx +++ b/packages/plus/src/components/ContentCard/__stories__/Click.stories.tsx @@ -21,6 +21,7 @@ Click.decorators = [ Click.args = { ...Template.args, onClick: () => { + // oxlint-disable-next-line eslint/no-alert alert('Clicked!') }, } diff --git a/packages/plus/src/components/ContentCard/__stories__/Disabled.stories.tsx b/packages/plus/src/components/ContentCard/__stories__/Disabled.stories.tsx index 60ca925215..c39f368ca7 100644 --- a/packages/plus/src/components/ContentCard/__stories__/Disabled.stories.tsx +++ b/packages/plus/src/components/ContentCard/__stories__/Disabled.stories.tsx @@ -49,7 +49,10 @@ export const Disabled: StoryFn = args => ( title="Create your first function" description="The Scaleway Serverless Functions platform makes your functions available, executes them on demand and manages resource allocation for you." disabled - onClick={() => console.log('ok')} + onClick={ + // oxlint-disable-next-line eslint/no-console + () => console.log('ok') + } /> diff --git a/packages/plus/src/components/EstimateCost/Components/UnitInput.tsx b/packages/plus/src/components/EstimateCost/Components/UnitInput.tsx index 8302f04527..8395af2d80 100644 --- a/packages/plus/src/components/EstimateCost/Components/UnitInput.tsx +++ b/packages/plus/src/components/EstimateCost/Components/UnitInput.tsx @@ -130,11 +130,11 @@ export const UnitInput = ({ value={value} placeholder={placeholder} onChange={input => { - const numericValue = parseInt(input, 10) + const numericValue = Number.parseInt(input, 10) onChange(numericValue) }} onBlur={(event: FocusEvent) => { - const numericValue = parseInt(event.target.value, 10) + const numericValue = Number.parseInt(event.target.value, 10) if (Number.isNaN(numericValue) || numericValue < minValue) { onChange(minValue) } diff --git a/packages/plus/src/components/EstimateCost/__stories__/NumberInput.stories.tsx b/packages/plus/src/components/EstimateCost/__stories__/NumberInput.stories.tsx index ce0981e2bd..f74b04a0bd 100644 --- a/packages/plus/src/components/EstimateCost/__stories__/NumberInput.stories.tsx +++ b/packages/plus/src/components/EstimateCost/__stories__/NumberInput.stories.tsx @@ -6,6 +6,7 @@ export const NumberInput = Template.bind({}) NumberInput.args = { children: [ , Powerful 100 Cores , - + 100 GB , ], diff --git a/packages/plus/src/components/EstimateCost/__tests__/helper.test.ts b/packages/plus/src/components/EstimateCost/__tests__/helper.test.ts index 594cd06ef2..1c0451c89c 100644 --- a/packages/plus/src/components/EstimateCost/__tests__/helper.test.ts +++ b/packages/plus/src/components/EstimateCost/__tests__/helper.test.ts @@ -43,7 +43,7 @@ describe('EstimateCost - helper', () => { amount: 5, amountFree: 2, timeUnit: 'months', - timeAmount: NaN, + timeAmount: Number.NaN, }), ).toEqual(0) }) diff --git a/packages/plus/src/components/Navigation/Footer.tsx b/packages/plus/src/components/Navigation/Footer.tsx index f5e666380b..1cebeb7462 100644 --- a/packages/plus/src/components/Navigation/Footer.tsx +++ b/packages/plus/src/components/Navigation/Footer.tsx @@ -39,6 +39,8 @@ export const Footer = ({ onToggleExpand, contentRef }: FooterProps) => { ) { return false } + + return true } return true diff --git a/packages/plus/src/components/Navigation/__stories__/Playground.stories.tsx b/packages/plus/src/components/Navigation/__stories__/Playground.stories.tsx index 4a2fc34e07..b2ffaa0fff 100644 --- a/packages/plus/src/components/Navigation/__stories__/Playground.stories.tsx +++ b/packages/plus/src/components/Navigation/__stories__/Playground.stories.tsx @@ -13,6 +13,12 @@ const Image = styled.img` animation: ${fadeOut} 250ms linear forwards; } ` +const onClickPinUnpin: ComponentProps< + typeof Navigation.Item +>['onClickPinUnpin'] = ({ totalPinned }) => { + // oxlint-disable-next-line eslint/no-console + console.log('total pinned items:', totalPinned) +} const PlaygroundContent = ({ ...props }: ComponentProps) => { const [active, setActive] = useState('Instance') @@ -24,6 +30,7 @@ const PlaygroundContent = ({ ...props }: ComponentProps) => { const saveExpandedInLocalStorage = useCallback((localExpanded: boolean) => { setExpanded(localExpanded) + // oxlint-disable-next-line eslint/no-console console.log( `expanded state with value ${localExpanded} saved in local storage`, ) @@ -31,21 +38,17 @@ const PlaygroundContent = ({ ...props }: ComponentProps) => { }, []) useEffect(() => { + // oxlint-disable-next-line eslint/no-console console.log('pinned items:', pinnedItems) localStorage.setItem('pinnedItems', pinnedItems.toString()) }, [pinnedItems]) const saveWidthInLocalStorage = useCallback((width: number) => { + // oxlint-disable-next-line eslint/no-console console.log(`width of ${width} saved in local storage`) localStorage.setItem('width', width.toString()) }, []) - const onClickPinUnpin: ComponentProps< - typeof Navigation.Item - >['onClickPinUnpin'] = ({ totalPinned }) => { - console.log('total pinned items:', totalPinned) - } - return ( ) => { + // eslint-disable-next-line no-param-reassign + event.currentTarget.style.opacity = '1' +} + export const Item = memo( ({ children, @@ -496,11 +501,6 @@ export const Item = memo( event.currentTarget.style.opacity = '0.5' } - const onDragStopTrigger = (event: DragEvent) => { - // eslint-disable-next-line no-param-reassign - event.currentTarget.style.opacity = '1' - } - const expandableAnimationDuration = useMemo(() => { if (!shouldAnimate || animationType === 'simple') return 0 diff --git a/packages/plus/src/components/SteppedListCard/Step.tsx b/packages/plus/src/components/SteppedListCard/Step.tsx index 31931271ca..08f8398129 100644 --- a/packages/plus/src/components/SteppedListCard/Step.tsx +++ b/packages/plus/src/components/SteppedListCard/Step.tsx @@ -44,33 +44,29 @@ export const SteppedList = ({ const containerData = useContext(Data) const active = containerData.currentStep === stepNumber - return ( - <> - {completed ? ( - containerData.setCurrentStep(stepNumber)} - data-testid={dataTestId} - > - - {stepTitle} - - - ) : ( - containerData.setCurrentStep(stepNumber)} - data-testid={dataTestId} - > - - {stepTitle} - - - )} - + return completed ? ( + containerData.setCurrentStep(stepNumber)} + data-testid={dataTestId} + > + + {stepTitle} + + + ) : ( + containerData.setCurrentStep(stepNumber)} + data-testid={dataTestId} + > + + {stepTitle} + + ) } diff --git a/packages/plus/src/components/SteppedListCard/SteppedListContainer.tsx b/packages/plus/src/components/SteppedListCard/SteppedListContainer.tsx index 157e3f11af..8ade0ec211 100644 --- a/packages/plus/src/components/SteppedListCard/SteppedListContainer.tsx +++ b/packages/plus/src/components/SteppedListCard/SteppedListContainer.tsx @@ -65,7 +65,7 @@ const SteppedListContainer = ({ const numberOfSteps = Children.count(children) const [currentStep, setCurrentStep] = useState(1) const [hidden, setHidden] = useState(false) - const [done, setDone] = useState(Array(steps.length).fill(false)) + const [done, setDone] = useState(new Array(steps.length).fill(false)) const values = useMemo( () => ({ diff --git a/packages/plus/src/components/SteppedListCard/SteppedListContent.tsx b/packages/plus/src/components/SteppedListCard/SteppedListContent.tsx index 80bbde4cd6..e1e64db675 100644 --- a/packages/plus/src/components/SteppedListCard/SteppedListContent.tsx +++ b/packages/plus/src/components/SteppedListCard/SteppedListContent.tsx @@ -31,36 +31,36 @@ export const SteppedListContent = ({ }: SteppedListContentProps) => { const containerData = useContext(Data) - return ( - <> - {stepNumber === containerData.currentStep ? ( - - - {typeof subHeader === 'string' ? ( - - {subHeader} - - ) : ( - subHeader - )} - - {typeof children === 'function' - ? children((completed: boolean) => - nextStep({ - completed, - setCompleted: containerData.setDone, - done: containerData.done, - stepNumber, - setCurrentStep: containerData.setCurrentStep, - numberOfSteps: containerData.numberOfSteps, - setHidden: containerData.setHidden, - onClickHide: containerData.onClickHide, - }), - ) - : children} - {image} - - ) : null} - - ) + if (stepNumber === containerData.currentStep) { + return ( + + + {typeof subHeader === 'string' ? ( + + {subHeader} + + ) : ( + subHeader + )} + + {typeof children === 'function' + ? children((completed: boolean) => + nextStep({ + completed, + setCompleted: containerData.setDone, + done: containerData.done, + stepNumber, + setCurrentStep: containerData.setCurrentStep, + numberOfSteps: containerData.numberOfSteps, + setHidden: containerData.setHidden, + onClickHide: containerData.onClickHide, + }), + ) + : children} + {image} + + ) + } + + return null } diff --git a/packages/plus/src/components/SteppedListCard/__stories__/Template.stories.tsx b/packages/plus/src/components/SteppedListCard/__stories__/Template.stories.tsx index 49cab45ed1..3b6e412064 100644 --- a/packages/plus/src/components/SteppedListCard/__stories__/Template.stories.tsx +++ b/packages/plus/src/components/SteppedListCard/__stories__/Template.stories.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled' import type { StoryFn } from '@storybook/react' import { blockStorageWire } from '@ultraviolet/illustrations/products/blockStorage' import { Button, Stack, Text } from '@ultraviolet/ui' -import { type ComponentProps } from 'react' +import type { ComponentProps } from 'react' import { SteppedListContainer } from '../SteppedListContainer' const StyledImage = styled.img` diff --git a/packages/plus/src/components/SteppedListCard/__tests__/index.test.tsx b/packages/plus/src/components/SteppedListCard/__tests__/index.test.tsx index daf8bde625..d017817c13 100644 --- a/packages/plus/src/components/SteppedListCard/__tests__/index.test.tsx +++ b/packages/plus/src/components/SteppedListCard/__tests__/index.test.tsx @@ -38,6 +38,7 @@ describe('SteppedListCard', () => { console.log('test')} > { console.log('test')} > { console.log('hide clicked')} > { }, {}) /* eslint-enable no-underscore-dangle */ - const propertiesList = Object.keys(componentNameAndProperties) - .map(key => Object.keys(componentNameAndProperties[key])) - .flat() + const propertiesList = Object.keys(componentNameAndProperties).flatMap(key => + Object.keys(componentNameAndProperties[key]), + ) const countPropertiesUsages = propertiesList.reduce>( (acc, property) => { @@ -172,12 +172,10 @@ const Properties = () => { } } - const reversedLocalProperty = lowerCaseLocalProperty - .split('') + const reversedLocalProperty = [...lowerCaseLocalProperty] .reverse() .join('') - const reversedLowerCaseProperty = lowerCaseProperty - .split('') + const reversedLowerCaseProperty = [...lowerCaseProperty] .reverse() .join('') @@ -209,35 +207,33 @@ const Properties = () => { ...new Set( sortedPropertiesUsagesCountAndComponentsName[ property - ]?.components - ?.map(component => { - const { name, value } = - ( - componentNameAndProperties as Record< - string, - Record - > - )[component]?.[property]?.type ?? {} + ]?.components?.flatMap(component => { + const { name, value } = + ( + componentNameAndProperties as Record< + string, + Record + > + )[component]?.[property]?.type ?? {} - if (name === 'boolean') { - return ['true', 'false'] - } + if (name === 'boolean') { + return ['true', 'false'] + } - if (name === 'string') { - return ['string'] - } + if (name === 'string') { + return ['string'] + } - if (name === 'enum') { - return ( - value?.map(localValue => - localValue.value.replaceAll('"', ''), - ) ?? [] - ) - } + if (name === 'enum') { + return ( + value?.map(localValue => + localValue.value.replaceAll('"', ''), + ) ?? [] + ) + } - return [] - }) - .flat(), + return [] + }), ), ] diff --git a/packages/ui/src/__stories__/Tools/ThemeGenerator/FormContent/index.tsx b/packages/ui/src/__stories__/Tools/ThemeGenerator/FormContent/index.tsx index 91cbf885b1..3aac4062aa 100644 --- a/packages/ui/src/__stories__/Tools/ThemeGenerator/FormContent/index.tsx +++ b/packages/ui/src/__stories__/Tools/ThemeGenerator/FormContent/index.tsx @@ -81,6 +81,8 @@ export const FormContent = () => { const countRequiredSentiments = fields.filter( ({ required }) => required, ).length + + // oxlint-disable-next-line eslint/no-console console.log({ errors }) return ( diff --git a/packages/ui/src/__stories__/Tools/ThemeGenerator/helpers.ts b/packages/ui/src/__stories__/Tools/ThemeGenerator/helpers.ts index 03f5057dec..1c8f672c4d 100644 --- a/packages/ui/src/__stories__/Tools/ThemeGenerator/helpers.ts +++ b/packages/ui/src/__stories__/Tools/ThemeGenerator/helpers.ts @@ -2,7 +2,7 @@ type ShadeHexColorType = (color: string, percent: number) => string export const shadeHexColor: ShadeHexColorType = (color, percent) => { - const f = parseInt(color.slice(1), 16) + const f = Number.parseInt(color.slice(1), 16) const t = percent < 0 ? 0 : 255 const p = percent < 0 ? percent * -1 : percent // eslint-disable-next-line no-bitwise diff --git a/packages/ui/src/__stories__/components/Colors.tsx b/packages/ui/src/__stories__/components/Colors.tsx index fefb1c8d97..6df491c4b4 100644 --- a/packages/ui/src/__stories__/components/Colors.tsx +++ b/packages/ui/src/__stories__/components/Colors.tsx @@ -63,7 +63,7 @@ const Colors = () => { ) as AvailableContexts[] return ( - + {sentiment} @@ -72,7 +72,7 @@ const Colors = () => { {colorContextKeys .filter(context => context.includes('background')) .map(context => ( - + { ].includes(background), ) .map(type => ( - + {type} @@ -249,7 +249,7 @@ const Colors = () => { {Object.keys(iconColors[type as keyof typeof iconColors]).map( sentiment => ( - + {sentiment} diff --git a/packages/ui/src/components/Alert/__stories__/Sentiments.stories.tsx b/packages/ui/src/components/Alert/__stories__/Sentiments.stories.tsx index 20743bb926..ae1002116f 100644 --- a/packages/ui/src/components/Alert/__stories__/Sentiments.stories.tsx +++ b/packages/ui/src/components/Alert/__stories__/Sentiments.stories.tsx @@ -6,8 +6,8 @@ import { Stack } from '../../Stack' export const Sentiments = (props: ComponentProps) => ALERT_SENTIMENTS.map(sentiment => ( = args => { - const onClick = () => {} +const onClick = () => {} - return ( - - - {([...SENTIMENTS, 'white', 'black'] as const).map(sentiment => ( - - - - {sentiment.toUpperCase()} - +export const Showcase: StoryFn = args => ( +
+ + {([...SENTIMENTS, 'white', 'black'] as const).map(sentiment => ( + + + + {sentiment.toUpperCase()} + + + {buttonVariants.map(variant => ( + + + + - {buttonVariants.map(variant => ( - - - - - - ))} - - ))} - -
- ) -} + ))} + + ))} + + +) Showcase.parameters = { docs: { diff --git a/packages/ui/src/components/Checkbox/__tests__/index.test.tsx b/packages/ui/src/components/Checkbox/__tests__/index.test.tsx index af9ddbadbf..639a2c7870 100644 --- a/packages/ui/src/components/Checkbox/__tests__/index.test.tsx +++ b/packages/ui/src/components/Checkbox/__tests__/index.test.tsx @@ -140,7 +140,7 @@ describe('Checkbox', () => { hidden: true, }) await userEvent.click(input) - expect(input.checked).toBe(true) + expect(input.checked).toBeTruthy() }) test('renders with click event with progress', async () => { @@ -154,6 +154,6 @@ describe('Checkbox', () => { hidden: true, }) await userEvent.click(input) - expect(input.checked).toBe(true) + expect(input.checked).toBeTruthy() }) }) diff --git a/packages/ui/src/components/DateInput/__stories__/Localized.stories.tsx b/packages/ui/src/components/DateInput/__stories__/Localized.stories.tsx index 24160a1e03..12c22aa1cd 100644 --- a/packages/ui/src/components/DateInput/__stories__/Localized.stories.tsx +++ b/packages/ui/src/components/DateInput/__stories__/Localized.stories.tsx @@ -13,8 +13,8 @@ const locales = [ export const Localized = (props: ComponentProps) => locales.map(({ label, locale }) => ( {}} label={label} locale={locale} diff --git a/packages/ui/src/components/DateInput/__tests__/helper.test.ts b/packages/ui/src/components/DateInput/__tests__/helper.test.ts index b842a41f15..aebf6c35d7 100644 --- a/packages/ui/src/components/DateInput/__tests__/helper.test.ts +++ b/packages/ui/src/components/DateInput/__tests__/helper.test.ts @@ -16,6 +16,8 @@ const rangeDate = { const date = new Date('20 November 2000') +const format = (value?: Date) => (value ? String(value.getFullYear()) : '1999') + describe('Helper functions dateInput', () => { test('getMonthFirstDay should work', () => { expect(getMonthFirstDay(1, 2000)).toBe(5) @@ -46,19 +48,19 @@ describe('Helper functions dateInput', () => { }) test('isSameMonth should work', () => { - expect(isSameMonth(new Date('23 Dec 2023'), new Date('22 Dec 2023'))).toBe( - true, - ) - expect(isSameMonth(new Date('23 Dec 2023'), new Date('23 Oct 2023'))).toBe( - false, - ) + expect( + isSameMonth(new Date('23 Dec 2023'), new Date('22 Dec 2023')), + ).toBeTruthy() + expect( + isSameMonth(new Date('23 Dec 2023'), new Date('23 Oct 2023')), + ).toBeFalsy() }) test('isSameDay should work', () => { - expect(isSameDay(new Date(), new Date('22 Dec 1999'))).toBe(false) - expect(isSameDay(new Date('23 Dec 2023'), new Date('23 Dec 2023'))).toBe( - true, - ) + expect(isSameDay(new Date(), new Date('22 Dec 1999'))).toBeFalsy() + expect( + isSameDay(new Date('23 Dec 2023'), new Date('23 Dec 2023')), + ).toBeTruthy() }) test('formatValue should work with default formatting', () => { @@ -74,9 +76,6 @@ describe('Helper functions dateInput', () => { }) test('formatValue should work with custom formatting', () => { - const format = (value?: Date) => - value ? String(value.getFullYear()) : '1999' - expect(formatValue(date, null, false, false, format)).toBe('2000') expect(formatValue(date, null, true, false, format)).toBe('2000') expect(formatValue(date, null, false, true, format)).toBe('2000') diff --git a/packages/ui/src/components/DateInput/index.tsx b/packages/ui/src/components/DateInput/index.tsx index 5363992076..a83bc7ef63 100644 --- a/packages/ui/src/components/DateInput/index.tsx +++ b/packages/ui/src/components/DateInput/index.tsx @@ -201,7 +201,7 @@ export const DateInput = ({ const [startDateInput, endDateInput] = newValue.split(' - ').map(val => { if (showMonthYearPicker) { // Force YYYY/MM (since MM/YYYY not recognised as a date in typescript) - const res = val.split(/\D+/).map(aa => parseInt(aa, 10)) + const res = val.split(/\D+/).map(aa => Number.parseInt(aa, 10)) return new Date(Math.max(...res), Math.min(...res) - 1) } diff --git a/packages/ui/src/components/LineChart/CustomLegend.tsx b/packages/ui/src/components/LineChart/CustomLegend.tsx index 9a64bcc8e5..089fe4dfbc 100644 --- a/packages/ui/src/components/LineChart/CustomLegend.tsx +++ b/packages/ui/src/components/LineChart/CustomLegend.tsx @@ -94,21 +94,31 @@ export const CustomLegend = ({ 'data-testid': dataTestId, }: CustomLegendProps) => ( -
+
Legend
-
+
{data?.map((row, index) => { const values = row.data.map(val => val.y as number) const labelIndexed = `${row.id}${index}` const id = row.id.toString() return ( -
+
{row?.['label']} -
+
diff --git a/packages/ui/src/components/LineChart/helpers.ts b/packages/ui/src/components/LineChart/helpers.ts index 41d07d01cd..72d8c218f9 100644 --- a/packages/ui/src/components/LineChart/helpers.ts +++ b/packages/ui/src/components/LineChart/helpers.ts @@ -2,20 +2,20 @@ import type { DatumValue, Serie } from '@nivo/line' const parse = (data?: DatumValue | null): number => { if (typeof data === 'number') return data || 0 - if (typeof data === 'string') return parseFloat(data) || 0 + if (typeof data === 'string') return Number.parseFloat(data) || 0 if (data instanceof Date) return data.getTime() return 0 } export const getMin = (values: DatumValue[] = []): number => - values.length ? Math.min(...values.map(parse)) : 0 + values.length > 0 ? Math.min(...values.map(parse)) : 0 export const getMax = (values: DatumValue[] = []): number => - values.length ? Math.max(...values.map(parse)) : 0 + values.length > 0 ? Math.max(...values.map(parse)) : 0 export const getAverage = (values: DatumValue[] = []): number => - values.length + values.length > 0 ? Math.round( (values.reduce((sum, curr) => sum + parse(curr), 0) / values.length) * @@ -44,7 +44,7 @@ export const getMinChartValue = (preppedData?: Serie[]): number => { } export const getCurrent = (values: number[] = []): number => - values.length ? values[values.length - 1] : 0 + values.length > 0 ? values[values.length - 1] : 0 export const getSelected = ( label: string, @@ -54,7 +54,7 @@ export const getSelected = ( const labelIndexed = label + index.toString() const found = selected.indexOf(labelIndexed) - if (found > -1) { + if (found !== -1) { selected.splice(found, 1) } else { selected.push(labelIndexed) diff --git a/packages/ui/src/components/Link/__stories__/Size.stories.tsx b/packages/ui/src/components/Link/__stories__/Size.stories.tsx index 1164f7ea94..c893281554 100644 --- a/packages/ui/src/components/Link/__stories__/Size.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/Size.stories.tsx @@ -11,7 +11,7 @@ const sizes: ComponentProps['size'][] = [ export const Size = (props: ComponentProps) => sizes.map(size => ( - + {size} )) diff --git a/packages/ui/src/components/List/__stories__/OnSelectedChange.stories.tsx b/packages/ui/src/components/List/__stories__/OnSelectedChange.stories.tsx index f9606c1af9..860e2435e2 100644 --- a/packages/ui/src/components/List/__stories__/OnSelectedChange.stories.tsx +++ b/packages/ui/src/components/List/__stories__/OnSelectedChange.stories.tsx @@ -9,6 +9,7 @@ import { columns, data } from './resources' export const OnSelectedChange: StoryFn = args => { const [selectedRows, onSelectedChange] = useState([]) + // oxlint-disable-next-line eslint/no-console console.log('selected', selectedRows) return ( @@ -18,7 +19,7 @@ export const OnSelectedChange: StoryFn = args => { {selectedRows.length > 0 ? (
    {selectedRows.map(item => ( -
  • +
  • {item} @@ -60,6 +61,7 @@ export const OnSelectedChange: StoryFn = args => { - ) +const CustomDisclosure = ({ visible }: DisclosureProps) => ( + +) - return ( - - Menu 1 - - ) -} +export const FunctionDisclosure: StoryFn = () => ( + + Menu 1 + +) FunctionDisclosure.parameters = { docs: { diff --git a/packages/ui/src/components/Menu/__stories__/WithModal.stories.tsx b/packages/ui/src/components/Menu/__stories__/WithModal.stories.tsx index 770ed4c3a2..349dbcbda5 100644 --- a/packages/ui/src/components/Menu/__stories__/WithModal.stories.tsx +++ b/packages/ui/src/components/Menu/__stories__/WithModal.stories.tsx @@ -3,28 +3,26 @@ import { Menu } from '..' import { Modal } from '../../Modal' import { DefaultDisclosure } from './Template.stories' -export const WithModal: StoryFn = () => { - const NestedModal = () => ( - MenuItem with Modal}> -
    - Content should be present in center of the modal -
    -
    - Content should be present in center of the modal -
    -
    - ) +const NestedModal = () => ( + MenuItem with Modal}> +
    + Content should be present in center of the modal +
    +
    + Content should be present in center of the modal +
    +
    +) - return ( - - Menu Item - - Menu Item Link - - - - ) -} +export const WithModal: StoryFn = () => ( + + Menu Item + + Menu Item Link + + + +) WithModal.parameters = { docs: { diff --git a/packages/ui/src/components/MenuV2/__stories__/FunctionDisclosure.stories.tsx b/packages/ui/src/components/MenuV2/__stories__/FunctionDisclosure.stories.tsx index f95d0b708a..bfe512166f 100644 --- a/packages/ui/src/components/MenuV2/__stories__/FunctionDisclosure.stories.tsx +++ b/packages/ui/src/components/MenuV2/__stories__/FunctionDisclosure.stories.tsx @@ -3,17 +3,15 @@ import type { DisclosureProps } from '..' import { MenuV2 } from '..' import { Button } from '../../Button' -export const FunctionDisclosure: StoryFn = () => { - const CustomDisclosure = ({ visible }: DisclosureProps) => ( - - ) +const CustomDisclosure = ({ visible }: DisclosureProps) => ( + +) - return ( - - MenuV2 1 - - ) -} +export const FunctionDisclosure: StoryFn = () => ( + + MenuV2 1 + +) FunctionDisclosure.parameters = { docs: { diff --git a/packages/ui/src/components/MenuV2/__stories__/WithModal.stories.tsx b/packages/ui/src/components/MenuV2/__stories__/WithModal.stories.tsx index ed34001a7d..6d09c10b31 100644 --- a/packages/ui/src/components/MenuV2/__stories__/WithModal.stories.tsx +++ b/packages/ui/src/components/MenuV2/__stories__/WithModal.stories.tsx @@ -4,29 +4,27 @@ import { Modal } from '../../Modal' import { TextInputV2 } from '../../TextInputV2' import { DefaultDisclosure } from './Template.stories' -export const WithModal: StoryFn = () => { - const NestedModal = () => ( - MenuItem with Modal}> -
    - Content should be present in center of the modal -
    -
    - Content should be present in center of the modal -
    - -
    - ) +const NestedModal = () => ( + MenuItem with Modal}> +
    + Content should be present in center of the modal +
    +
    + Content should be present in center of the modal +
    + +
    +) - return ( - - Menu Item - - Menu Item Link - - - - ) -} +export const WithModal: StoryFn = () => ( + + Menu Item + + Menu Item Link + + + +) WithModal.parameters = { docs: { diff --git a/packages/ui/src/components/Meter/__stories__/Playground.stories.tsx b/packages/ui/src/components/Meter/__stories__/Playground.stories.tsx index e32a29f71d..3163a2700c 100644 --- a/packages/ui/src/components/Meter/__stories__/Playground.stories.tsx +++ b/packages/ui/src/components/Meter/__stories__/Playground.stories.tsx @@ -18,7 +18,7 @@ export const Playground: StoryFn = args => { const [password, setPassword] = useState('') useEffect(() => { - if (password.length >= 1) { + if (password.length > 0) { setValue(zxcvbn(password).score) } }, [password, setValue]) diff --git a/packages/ui/src/components/Modal/components/Dialog.tsx b/packages/ui/src/components/Modal/components/Dialog.tsx index f2b9f9255b..ad90008d1d 100644 --- a/packages/ui/src/components/Modal/components/Dialog.tsx +++ b/packages/ui/src/components/Modal/components/Dialog.tsx @@ -95,6 +95,12 @@ export const StyledDialog = styled('dialog', { : undefined} ` +// Prevent default behaviour on Escape +const stopCancel: ReactEventHandler = event => { + event.preventDefault() + event.stopPropagation() +} + export const Dialog = ({ children, placement, @@ -250,12 +256,6 @@ export const Dialog = ({ } }, []) - // Prevent default behaviour on Escape - const stopCancel: ReactEventHandler = event => { - event.preventDefault() - event.stopPropagation() - } - // We need to reverse the array as the last opened modal should be the first to be with normal size // while the first opened modal should shrink const realPosition = [...openedModals].findIndex(object => object.id === id) @@ -314,7 +314,7 @@ export const Dialog = ({ ref={dialogRef} tabIndex={0} position={position} - top={top > 0 ? top : 0} + top={Math.max(top, 0)} data-animation={animation} size={size} > diff --git a/packages/ui/src/components/NumberInput/__stories__/Events.stories.tsx b/packages/ui/src/components/NumberInput/__stories__/Events.stories.tsx index 1ca4a8d501..f24f8b4eda 100644 --- a/packages/ui/src/components/NumberInput/__stories__/Events.stories.tsx +++ b/packages/ui/src/components/NumberInput/__stories__/Events.stories.tsx @@ -1,3 +1,4 @@ +// oxlint-disable eslint/no-console import { Template } from './Template.stories' export const Events = Template.bind({}) @@ -6,6 +7,7 @@ Events.args = { minValue: 50, maxValue: 100, defaultValue: 50, + onChange: () => console.log('onChange'), onFocus: () => console.log('onFocus'), onBlur: () => console.log('onBlur'), diff --git a/packages/ui/src/components/NumberInputV2/index.tsx b/packages/ui/src/components/NumberInputV2/index.tsx index 1076c2ed6e..7f967bd519 100644 --- a/packages/ui/src/components/NumberInputV2/index.tsx +++ b/packages/ui/src/components/NumberInputV2/index.tsx @@ -288,7 +288,7 @@ export const NumberInputV2 = forwardRef( } else if (direction === 'down') { localRef.current?.stepDown() } - onChange?.(parseFloat(localRef.current?.value ?? '') ?? min) + onChange?.(Number.parseFloat(localRef.current?.value ?? '') ?? min) }, [localRef, min, onChange], ) @@ -407,7 +407,9 @@ export const NumberInputV2 = forwardRef( onChange={ onChange ? event => { - const newNumber = parseFloat(event.target.value) + const newNumber = Number.parseFloat( + event.target.value, + ) onChange(Number.isNaN(newNumber) ? null : newNumber) } : undefined diff --git a/packages/ui/src/components/Popup/index.tsx b/packages/ui/src/components/Popup/index.tsx index eeea8d73c2..fbcadc9672 100644 --- a/packages/ui/src/components/Popup/index.tsx +++ b/packages/ui/src/components/Popup/index.tsx @@ -44,6 +44,13 @@ type StyledPopupProps = { isDialog: boolean } +/** + * This event handle allow us to not bubble the event to document.body like this react-select works fine + */ +const stopClickPropagation: MouseEventHandler = event => { + event.nativeEvent.stopImmediatePropagation() +} + const StyledPopup = styled('div', { shouldForwardProp: prop => ![ @@ -561,16 +568,11 @@ export const Popup = forwardRef( }, [dynamicDomRendering, visibleInDom]) if (!text) { - if (typeof children === 'function') return null - - return <>{children} - } + if (typeof children === 'function') { + return null + } - /** - * This event handle allow us to not bubble the event to document.body like this react-select works fine - */ - const stopClickPropagation: MouseEventHandler = event => { - event.nativeEvent.stopImmediatePropagation() + return children } return ( diff --git a/packages/ui/src/components/SelectInput/__stories__/IsClearable.stories.tsx b/packages/ui/src/components/SelectInput/__stories__/IsClearable.stories.tsx index b5a726d414..69f0e06299 100644 --- a/packages/ui/src/components/SelectInput/__stories__/IsClearable.stories.tsx +++ b/packages/ui/src/components/SelectInput/__stories__/IsClearable.stories.tsx @@ -7,8 +7,12 @@ IsClearable.args = { isClearable: true, value: { label: 'Option A', value: 'a' }, children: [ - Option A, - Option B, + + Option A + , + + Option B + , ], } IsClearable.parameters = { diff --git a/packages/ui/src/components/SelectInput/index.tsx b/packages/ui/src/components/SelectInput/index.tsx index 005292c812..9b69701ae8 100644 --- a/packages/ui/src/components/SelectInput/index.tsx +++ b/packages/ui/src/components/SelectInput/index.tsx @@ -160,7 +160,7 @@ const getSelectStyles = ({ : theme.colors.primary.borderHover, }, }), - ...(customStyle(state)?.['control'] || {}), + ...customStyle(state)?.['control'], animation: animation ? `${animationDuration}ms ${ (animations as Record>)[ @@ -177,7 +177,7 @@ const getSelectStyles = ({ ...provided, backgroundColor: theme.colors.neutral.border, display: 'none', - ...(customStyle(state)?.['indicatorSeparator'] || {}), + ...customStyle(state)?.['indicatorSeparator'], }), input: provided => ({ ...provided, @@ -187,19 +187,19 @@ const getSelectStyles = ({ }), menu: (provided, state) => ({ ...provided, - ...(customStyle(state)?.['menu'] || {}), + ...customStyle(state)?.['menu'], boxShadow: theme.shadows.menu, }), menuList: (provided, state) => ({ ...provided, backgroundColor: theme.colors.neutral.backgroundWeak, maxHeight: '225px', - ...(customStyle(state)?.['menuList'] || {}), + ...customStyle(state)?.['menuList'], }), menuPortal: (provided, state) => ({ ...provided, zIndex: 10000, - ...(customStyle(state)?.['menuPortal'] || {}), + ...customStyle(state)?.['menuPortal'], }), multiValue: (provided, state) => ({ ...provided, @@ -213,7 +213,7 @@ const getSelectStyles = ({ justifyContent: 'center', marginTop: theme.space[noTopLabel ? '0.5' : '2'], textOverflow: 'ellipsis', - ...(customStyle(state)?.['multiValue'] || {}), + ...customStyle(state)?.['multiValue'], }), multiValueLabel: (provided, state) => ({ ...provided, @@ -223,7 +223,7 @@ const getSelectStyles = ({ fontSize: '14px', fontWeight: 'normal', lineHeight: '20px', - ...(customStyle(state)?.['multiValueLabel'] || {}), + ...customStyle(state)?.['multiValueLabel'], }), multiValueRemove: (provided, state) => ({ ...provided, @@ -243,7 +243,7 @@ const getSelectStyles = ({ cursor: state.isDisabled ? 'none' : 'pointer', pointerEvents: state.isDisabled ? 'none' : 'fill', }, - ...(customStyle(state)?.['multiValueRemove'] || {}), + ...customStyle(state)?.['multiValueRemove'], }), option: (provided, state) => ({ ...provided, @@ -265,12 +265,12 @@ const getSelectStyles = ({ ? theme.colors.neutral.textDisabled : theme.colors.neutral.text, }, - ...(customStyle(state)?.['option'] || {}), + ...customStyle(state)?.['option'], }), placeholder: (provided, state) => ({ ...provided, color: getPlaceholderColor({ error, state, theme }), - ...(customStyle(state)?.['placeholder'] || {}), + ...customStyle(state)?.['placeholder'], }), singleValue: (provided, state) => ({ ...provided, @@ -281,11 +281,11 @@ const getSelectStyles = ({ marginRight: state.hasValue ? 0 : undefined, marginTop: !state.hasValue || noTopLabel ? 0 : '5px', paddingLeft: state.hasValue ? 0 : undefined, - ...(customStyle(state)?.['singleValue'] || {}), + ...customStyle(state)?.['singleValue'], }), valueContainer: (provided, state) => ({ ...provided, - ...(customStyle(state)?.['valueContainer'] || {}), + ...customStyle(state)?.['valueContainer'], cursor: state.isDisabled ? 'not-allowed' : undefined, height: '100%', label: { @@ -755,11 +755,10 @@ const FwdSelectInput = ({ value={value as SelectOption} maxMenuHeight={250} inputId={inputId} - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - ref={innerRef as any} + // @ts-expect-error innerRef type issue + ref={innerRef} name={name} id={idProp} - // @ts-expect-error time prop doesn't exist in react-select but is used time={time} isLoading={isLoading} required={required} diff --git a/packages/ui/src/components/SelectInputV2/Dropdown.tsx b/packages/ui/src/components/SelectInputV2/Dropdown.tsx index 8a254f35e1..60659b671b 100644 --- a/packages/ui/src/components/SelectInputV2/Dropdown.tsx +++ b/packages/ui/src/components/SelectInputV2/Dropdown.tsx @@ -697,7 +697,7 @@ export const Dropdown = ({ refSelect.current.getBoundingClientRect().bottom + DROPDOWN_MAX_HEIGHT + Number(theme.sizing[INPUT_SIZE_HEIGHT[size]].replace('rem', '')) * 16 + - parseInt(theme.space['5'], 10) + Number.parseInt(theme.space['5'], 10) const overflow = position - window.innerHeight if (overflow > 0) { const modalElement = document.getElementById('backdrop-modal') @@ -807,11 +807,11 @@ export const Dropdown = ({ const isEmpty = useMemo(() => { if (numberOfOptions === 0) return true if (Array.isArray(displayedOptions)) { - return !(displayedOptions.length > 0) + return displayedOptions.length === 0 } const groups = Object.keys(displayedOptions) for (const group of groups) { - if (displayedOptions[group].length !== 0) { + if (displayedOptions[group].length > 0) { return false } } diff --git a/packages/ui/src/components/SelectInputV2/SearchBarDropdown.tsx b/packages/ui/src/components/SelectInputV2/SearchBarDropdown.tsx index 347f1533e9..2839dd94dc 100644 --- a/packages/ui/src/components/SelectInputV2/SearchBarDropdown.tsx +++ b/packages/ui/src/components/SelectInputV2/SearchBarDropdown.tsx @@ -21,6 +21,7 @@ const StyledInput = styled(TextInputV2)` const matchRegex = (data: OptionType[], regex: RegExp) => data.filter( option => + // oxlint-disable-next-line eslint-plugin-unicorn(prefer-regexp-test) (option.searchText && !!option.searchText.match(regex)) || (typeof option.label === 'string' && option.label.match(regex)) || (typeof option.description === 'string' && @@ -82,7 +83,7 @@ export const SearchBarDropdown = ({ const handleChange = (search: string) => { if (search.length > 0) { // case insensitive search - const regex = RegExp(search, 'i') + const regex = new RegExp(search.toString(), 'i') if (!Array.isArray(options)) { const filteredOptions = { ...options } Object.keys(filteredOptions).map((group: string) => { diff --git a/packages/ui/src/components/SelectInputV2/SelectInputProvider.tsx b/packages/ui/src/components/SelectInputV2/SelectInputProvider.tsx index 17582c1d1d..fe659ead6b 100644 --- a/packages/ui/src/components/SelectInputV2/SelectInputProvider.tsx +++ b/packages/ui/src/components/SelectInputV2/SelectInputProvider.tsx @@ -143,7 +143,7 @@ export const SelectInputProvider = ({ action: ReducerAction, ): ReducerState => { switch (action.type) { - case 'selectAll': + case 'selectAll': { if (state.allSelected) { return { selectedValues: [], allSelected: false, selectedGroups: [] } } @@ -153,8 +153,9 @@ export const SelectInputProvider = ({ allSelected: true, selectedGroups: allGroups, } + } - case 'selectGroup': + case 'selectGroup': { if (!Array.isArray(options)) { if (state.selectedGroups.includes(action.selectedGroup)) { return { @@ -187,8 +188,9 @@ export const SelectInputProvider = ({ } return state + } - case 'selectOption': + case 'selectOption': { if (multiselect) { if (state.selectedValues.includes(action.clickedOption.value)) { return { @@ -231,11 +233,13 @@ export const SelectInputProvider = ({ allSelected: false, selectedGroups: state.selectedGroups, } + } - case 'clearAll': + case 'clearAll': { return { selectedGroups: [], selectedValues: [], allSelected: false } + } - case 'update': // update the selected values to only keep non-disabled one + case 'update': { return { selectedGroups: state.selectedGroups, allSelected: state.allSelected, @@ -253,14 +257,17 @@ export const SelectInputProvider = ({ ) }), } - case 'reset': + } + case 'reset': { return { selectedValues: action.selectedValues, allSelected: false, selectedGroups: action.selectedGroups, } - default: + } + default: { return state + } } } diff --git a/packages/ui/src/components/SelectInputV2/findOptionInOptions.ts b/packages/ui/src/components/SelectInputV2/findOptionInOptions.ts index 4d325f5b64..ebd6714d9c 100644 --- a/packages/ui/src/components/SelectInputV2/findOptionInOptions.ts +++ b/packages/ui/src/components/SelectInputV2/findOptionInOptions.ts @@ -3,9 +3,7 @@ import type { DataType, OptionType } from './types' export const findOptionInOptions = (options: DataType, optionValue: string) => { let flatOptions: OptionType[] = [] if (!Array.isArray(options)) { - flatOptions = Object.keys(options) - .map(group => options[group]) - .flat() + flatOptions = Object.keys(options).flatMap(group => options[group]) } else { flatOptions = options } diff --git a/packages/ui/src/components/SelectableCard/index.tsx b/packages/ui/src/components/SelectableCard/index.tsx index 3520647d52..d2158ab719 100644 --- a/packages/ui/src/components/SelectableCard/index.tsx +++ b/packages/ui/src/components/SelectableCard/index.tsx @@ -296,6 +296,7 @@ export const SelectableCard = forwardRef( {illustration.endsWith('.svg') && svgContent ? ( ) : ( diff --git a/packages/ui/src/components/SelectableCardGroup/__stories__/Examples.stories.tsx b/packages/ui/src/components/SelectableCardGroup/__stories__/Examples.stories.tsx index 5c117e1d98..2b43dc28e1 100644 --- a/packages/ui/src/components/SelectableCardGroup/__stories__/Examples.stories.tsx +++ b/packages/ui/src/components/SelectableCardGroup/__stories__/Examples.stories.tsx @@ -76,6 +76,7 @@ export const Examples: StoryFn = args => { data.push(event.currentTarget.value) } onChange3(data) + // oxlint-disable-next-line eslint/no-console console.log(data) }} > diff --git a/packages/ui/src/components/Slider/components/DoubleSlider.tsx b/packages/ui/src/components/Slider/components/DoubleSlider.tsx index ea1c0059c5..98efafb038 100644 --- a/packages/ui/src/components/Slider/components/DoubleSlider.tsx +++ b/packages/ui/src/components/Slider/components/DoubleSlider.tsx @@ -234,7 +234,7 @@ export const DoubleSlider = ({ { event.preventDefault() - handleMinChange(parseFloat(event.target.value)) + handleMinChange(Number.parseFloat(event.target.value)) }} themeSlider={theme} ref={refSlider} @@ -425,7 +425,7 @@ export const DoubleSlider = ({ step={step} onChange={event => { event.preventDefault() - handleMaxChange(parseFloat(event.target.value)) + handleMaxChange(Number.parseFloat(event.target.value)) }} themeSlider={theme} left={((selectedIndexes[1] - min) * 100) / (max - min)} diff --git a/packages/ui/src/components/Slider/components/SingleSlider.tsx b/packages/ui/src/components/Slider/components/SingleSlider.tsx index 1847775ebc..adf83562ba 100644 --- a/packages/ui/src/components/Slider/components/SingleSlider.tsx +++ b/packages/ui/src/components/Slider/components/SingleSlider.tsx @@ -166,7 +166,7 @@ export const SingleSlider = ({ { - internalOnChangeRef.current(parseFloat(event.target.value)) + internalOnChangeRef.current(Number.parseFloat(event.target.value)) }} min={min} max={max} diff --git a/packages/ui/src/components/Snippet/index.tsx b/packages/ui/src/components/Snippet/index.tsx index 2ed3ababd4..e9b582c4bc 100644 --- a/packages/ui/src/components/Snippet/index.tsx +++ b/packages/ui/src/components/Snippet/index.tsx @@ -177,7 +177,7 @@ const CodeContent = ({ > {multiline ? ( Children.map(lines, child => ( - + {child} )) @@ -235,8 +235,8 @@ export const Snippet = ({ const hasShowMoreButton = numberOfLines > rows && multiline && !noExpandable // Height of the expandable (when needed) = number of rows * height of a line (from rem to px) + padding (from rem to px) const minHeight = - rows * parseFloat(theme.typography.code.lineHeight) * 16 + - parseFloat(theme.space[4]) * 16 + rows * Number.parseFloat(theme.typography.code.lineHeight) * 16 + + Number.parseFloat(theme.space[4]) * 16 return ( { + // oxlint-disable-next-line eslint/no-alert alert('elements could be deleted') unselectAll() }} diff --git a/packages/ui/src/components/Table/__stories__/SelectableColumn.stories.tsx b/packages/ui/src/components/Table/__stories__/SelectableColumn.stories.tsx index 35b215a826..01b72a7cf6 100644 --- a/packages/ui/src/components/Table/__stories__/SelectableColumn.stories.tsx +++ b/packages/ui/src/components/Table/__stories__/SelectableColumn.stories.tsx @@ -7,6 +7,27 @@ import { Stack } from '../../Stack' const CHANNELS = ['email', 'app', 'sms'] +const selectableCell = ( + channel: string, + subject: string, + items: string[], + setter: Dispatch>, +) => ( + { + setter(current => { + const index = current.indexOf(channel) + + return index === -1 + ? [...current, channel] + : [...current.slice(0, index), ...current.slice(index + 1)] + }) + }} + /> +) + export const SelectableColumn: StoryFn = () => { const [incidentNotifications, setIndicentNotifications] = useState( [], @@ -31,27 +52,6 @@ export const SelectableColumn: StoryFn = () => { setState(current => (current.length === 0 ? [...CHANNELS] : [])) } - const selectableCell = ( - channel: string, - subject: string, - items: string[], - setter: Dispatch>, - ) => ( - { - setter(current => { - const index = current.indexOf(channel) - - return index === -1 - ? [...current, channel] - : [...current.slice(0, index), ...current.slice(index + 1)] - }) - }} - /> - ) - return ( {
    Incident notifications channels :{' '} - {incidentNotifications.length + {incidentNotifications.length > 0 ? incidentNotifications.join(',') : 'none'}
    Technical notifications channels :{' '} - {technicalNotifications.length + {technicalNotifications.length > 0 ? technicalNotifications.join(',') : 'none'}
    Billing notifications channels :{' '} - {billingNotifications.length ? billingNotifications.join(',') : 'none'} + {billingNotifications.length > 0 + ? billingNotifications.join(',') + : 'none'}
    ) diff --git a/packages/ui/src/components/Tabs/__stories__/Disabled.stories.tsx b/packages/ui/src/components/Tabs/__stories__/Disabled.stories.tsx index 620e6a29b2..a655b19ad7 100644 --- a/packages/ui/src/components/Tabs/__stories__/Disabled.stories.tsx +++ b/packages/ui/src/components/Tabs/__stories__/Disabled.stories.tsx @@ -5,11 +5,15 @@ export const Disabled = Template.bind({}) Disabled.args = { children: [ - Choice 1, - + + Choice 1 + , + Choice 2 , - Choice 3, + + Choice 3 + , ], selected: 'first', } diff --git a/packages/ui/src/components/Tabs/__stories__/Playground.stories.tsx b/packages/ui/src/components/Tabs/__stories__/Playground.stories.tsx index fbaa0bbf2a..f7a68fa200 100644 --- a/packages/ui/src/components/Tabs/__stories__/Playground.stories.tsx +++ b/packages/ui/src/components/Tabs/__stories__/Playground.stories.tsx @@ -5,8 +5,12 @@ export const Playground = Template.bind({}) Playground.args = { children: [ - Choice 1, - Choice 2, + + Choice 1 + , + + Choice 2 + , ], selected: 0, } diff --git a/packages/ui/src/components/Tabs/__stories__/Showcase.stories.tsx b/packages/ui/src/components/Tabs/__stories__/Showcase.stories.tsx index 81c1a3092d..d56151c049 100644 --- a/packages/ui/src/components/Tabs/__stories__/Showcase.stories.tsx +++ b/packages/ui/src/components/Tabs/__stories__/Showcase.stories.tsx @@ -5,30 +5,46 @@ export const Showcase = Template.bind({}) Showcase.args = { children: [ - Choice 1, - + + Choice 1 + , + Link , - + Disabled , - + Disabled too , - + Link no value , - + Choice 6 , - Choice 6 too but six value, - + + Choice 6 too but six value + , + With tooltip , - + Choice 8 , - + Choice 9 , ], diff --git a/packages/ui/src/components/TagInput/index.tsx b/packages/ui/src/components/TagInput/index.tsx index 8aa9c2cea0..22297d4a8b 100644 --- a/packages/ui/src/components/TagInput/index.tsx +++ b/packages/ui/src/components/TagInput/index.tsx @@ -244,7 +244,7 @@ export const TagInput = ({ if (newTagInput) { setStatus({ [newTagInput[newTagInput.length - 1].index]: STATUS.IDLE }) } - } catch (e) { + } catch { setTagInput(tagInputState) } } @@ -258,7 +258,7 @@ export const TagInput = ({ dispatchOnChange(newTagInput) setTagInput(newTagInput) setStatus({ [tagIndex]: STATUS.IDLE }) - } catch (e) { + } catch { setTagInput(tagInputState) } } @@ -271,7 +271,7 @@ export const TagInput = ({ if ( event.key === 'Backspace' && inputRef?.current?.selectionStart === 0 && - tagInputState.length + tagInputState.length > 0 ) { event.preventDefault() if (tagInputState) { @@ -291,7 +291,7 @@ export const TagInput = ({ try { dispatchOnChange(newTagInput) setStatus({ [newTagInput.length - 1]: STATUS.IDLE }) - } catch (err) { + } catch { setTagInput(tagInputState) } } @@ -314,7 +314,7 @@ export const TagInput = ({ return 'neutral' }, [error, success]) - const computedClearable = clearable && !!tagInputState.length + const computedClearable = clearable && tagInputState.length > 0 return ( @@ -374,7 +374,7 @@ export const TagInput = ({ name={name} aria-label={ariaLabel} type="text" - placeholder={!tagInputState.length ? placeholder : ''} + placeholder={tagInputState.length === 0 ? placeholder : ''} value={input} onBlur={addTag} onChange={onInputChange} diff --git a/packages/ui/src/components/TagList/__stories__/Multiline.stories.tsx b/packages/ui/src/components/TagList/__stories__/Multiline.stories.tsx index 6c4ff629c9..8cdebae264 100644 --- a/packages/ui/src/components/TagList/__stories__/Multiline.stories.tsx +++ b/packages/ui/src/components/TagList/__stories__/Multiline.stories.tsx @@ -14,7 +14,7 @@ Multiline.args = { multiline: true, tags: [ 'very', - ...Array(50).fill('item'), + ...new Array(50).fill('item'), 'tooltip', 'scaleway', 'paris', diff --git a/packages/ui/src/components/TagList/__stories__/Threshold.stories.tsx b/packages/ui/src/components/TagList/__stories__/Threshold.stories.tsx index 468b1f9c65..b439da98fd 100644 --- a/packages/ui/src/components/TagList/__stories__/Threshold.stories.tsx +++ b/packages/ui/src/components/TagList/__stories__/Threshold.stories.tsx @@ -14,7 +14,7 @@ Threshold.parameters = { Threshold.args = { tags: [ 'very', - ...Array(50).fill('large'), + ...new Array(50).fill('large'), 'tooltip', 'scaleway', 'paris', diff --git a/packages/ui/src/components/TagList/index.tsx b/packages/ui/src/components/TagList/index.tsx index 39aee87a1c..f5f4ddd0b0 100644 --- a/packages/ui/src/components/TagList/index.tsx +++ b/packages/ui/src/components/TagList/index.tsx @@ -153,7 +153,7 @@ export const TagList = ({ // Compute visible tags and hidden ones based on the container width and // what can fit into it from the potentially visible tags useEffect(() => { - if (!tags.length || !containerRef.current || !measureRef.current) { + if (tags.length === 0 || !containerRef.current || !measureRef.current) { return } @@ -190,7 +190,7 @@ export const TagList = ({ const newAccumulatedWidth = accumulator.accumulatedWidth + (currentValue as HTMLDivElement).offsetWidth + - parseInt(TAGS_GAP, 10) + Number.parseInt(TAGS_GAP, 10) return { measuredVisibleTags: [ @@ -208,16 +208,17 @@ export const TagList = ({ measuredVisibleTags: [tags[0]], // we need to always show one tag measuredHiddenTags: [], accumulatedWidth: - (firstTag as HTMLDivElement).offsetWidth + parseInt(TAGS_GAP, 10), + (firstTag as HTMLDivElement).offsetWidth + + Number.parseInt(TAGS_GAP, 10), }, ) - const finalHiddenTags = measuredHiddenTags.concat(surelyHiddenTags) + const finalHiddenTags = [...measuredHiddenTags, ...surelyHiddenTags] setVisibleTags(measuredVisibleTags) setHiddenTags(finalHiddenTags) - if (!finalHiddenTags.length) { + if (finalHiddenTags.length === 0) { setIsReady(true) } }, [ @@ -269,7 +270,7 @@ export const TagList = ({ } }, [isReady]) - if (!tags.length) { + if (tags.length === 0) { return null } diff --git a/packages/ui/src/components/TextArea/index.tsx b/packages/ui/src/components/TextArea/index.tsx index 6ca3b31474..6f004553a5 100644 --- a/packages/ui/src/components/TextArea/index.tsx +++ b/packages/ui/src/components/TextArea/index.tsx @@ -206,7 +206,9 @@ export const TextArea = forwardRef( textArea.style.resize = 'none' textArea.style.height = `${textArea.scrollHeight + 2}px` } else if (textArea && maxRows) { - const lineHeight = parseFloat(getComputedStyle(textArea).lineHeight) + const lineHeight = Number.parseFloat( + getComputedStyle(textArea).lineHeight, + ) textArea.style.height = 'auto' const maxHeight = maxRows * lineHeight diff --git a/packages/ui/src/components/VerificationCode/__stories__/OnComplete.stories.tsx b/packages/ui/src/components/VerificationCode/__stories__/OnComplete.stories.tsx index bbb590ea11..939876ab91 100644 --- a/packages/ui/src/components/VerificationCode/__stories__/OnComplete.stories.tsx +++ b/packages/ui/src/components/VerificationCode/__stories__/OnComplete.stories.tsx @@ -1,14 +1,15 @@ import type { StoryFn } from '@storybook/react' import { VerificationCode } from '..' -export const OnComplete: StoryFn = args => { - const onCompleteHandler = (value: unknown) => { - console.log('Code is fully typed', value) - } - - return +const onCompleteHandler = (value: unknown) => { + // oxlint-disable-next-line no-console + console.log('Code is fully typed', value) } +export const OnComplete: StoryFn = args => ( + +) + OnComplete.parameters = { docs: { description: { diff --git a/packages/ui/src/components/VerificationCode/index.tsx b/packages/ui/src/components/VerificationCode/index.tsx index f0b5d51fab..dbe46d1026 100644 --- a/packages/ui/src/components/VerificationCode/index.tsx +++ b/packages/ui/src/components/VerificationCode/index.tsx @@ -128,6 +128,9 @@ type VerificationCodeProps = { const DEFAULT_ON_FUNCTION = () => {} +const inputOnFocus: FocusEventHandler = event => + event.target.select() + /** * Verification code allows you to enter a code in multiple fields (4 by default). */ @@ -149,10 +152,9 @@ export const VerificationCode = ({ 'aria-label': ariaLabel = 'Verification code', }: VerificationCodeProps) => { const uniqueId = useId() - const valuesArray = Object.assign( - new Array(fields).fill(''), - initialValue.substring(0, fields).split(''), - ) + const valuesArray = Object.assign(new Array(fields).fill(''), [ + ...initialValue.substring(0, fields), + ]) const [values, setValues] = useState(valuesArray) const inputRefs = Array.from({ length: fields }, () => @@ -210,8 +212,9 @@ export const VerificationCode = ({ const vals = [...values] switch (event.key) { - case 'Backspace': + case 'Backspace': { event.preventDefault() + if (values[index]) { vals[index] = '' setValues(vals) @@ -223,39 +226,44 @@ export const VerificationCode = ({ triggerChange(vals) } break - case 'ArrowLeft': + } + + case 'ArrowLeft': { event.preventDefault() prev?.current?.focus() break - case 'ArrowRight': + } + case 'ArrowRight': { event.preventDefault() next?.current?.focus() break - case 'ArrowUp': + } + case 'ArrowUp': { event.preventDefault() first?.current?.focus() break - case 'ArrowDown': + } + case 'ArrowDown': { event.preventDefault() last?.current?.focus() + break - default: + } + + default: { break + } } } - const inputOnFocus: FocusEventHandler = event => - event.target.select() - const inputOnPaste = (currentIndex: number): ClipboardEventHandler => event => { event.preventDefault() - const pastedValue = [ - ...event.clipboardData.getData('Text').split(''), - ].map((copiedValue: string) => - // Replace non number char with empty char when type is number - type === 'number' ? copiedValue.replace(/[^\d]/gi, '') : copiedValue, + const pastedValue = [...event.clipboardData.getData('Text')].map( + (copiedValue: string) => + // Replace non number char with empty char when type is number + type === 'number' ? copiedValue.replace(/[^\d]/gi, '') : copiedValue, ) // Trim array to avoid array overflow @@ -266,7 +274,8 @@ export const VerificationCode = ({ ) setValues((vals: string[]) => { - const newArray = vals.slice() + const newArray = structuredClone(vals) + newArray.splice(currentIndex, pastedValue.length, ...pastedValue) return newArray diff --git a/packages/ui/src/helpers/__tests__/index.test.ts b/packages/ui/src/helpers/__tests__/index.test.ts index d704eebd48..487754d0b1 100644 --- a/packages/ui/src/helpers/__tests__/index.test.ts +++ b/packages/ui/src/helpers/__tests__/index.test.ts @@ -49,7 +49,7 @@ describe('recursivelyGetChildrenString', () => { describe('onKeyOnlyNumbers', () => { test('should only prevent numbers keyCodes', () => { - ;[...Array(100).keys()].forEach(keyCode => { + ;[...new Array(100).keys()].forEach(keyCode => { const preventDefault = vi.fn() onKeyOnlyNumbers({ diff --git a/packages/ui/src/helpers/isJSON.ts b/packages/ui/src/helpers/isJSON.ts index 10cae88d5f..aa41bc9dc7 100644 --- a/packages/ui/src/helpers/isJSON.ts +++ b/packages/ui/src/helpers/isJSON.ts @@ -1,7 +1,7 @@ const isJSONString = (str: string): boolean => { try { JSON.parse(str) - } catch (e) { + } catch { return false } diff --git a/packages/ui/src/hooks/__tests__/useIsOverflowing.test.tsx b/packages/ui/src/hooks/__tests__/useIsOverflowing.test.tsx index 078fe3a773..631d980c42 100644 --- a/packages/ui/src/hooks/__tests__/useIsOverflowing.test.tsx +++ b/packages/ui/src/hooks/__tests__/useIsOverflowing.test.tsx @@ -8,7 +8,7 @@ describe('useIsOverflowing', () => { useIsOverflowing({ current: document.createElement('div') }), ) await waitFor(() => { - expect(result.current).toBe(false) + expect(result.current).toBeFalsy() }) }) @@ -18,7 +18,7 @@ describe('useIsOverflowing', () => { useIsOverflowing({ current: document.createElement('div') }, callback), ) await waitFor(() => { - expect(result.current).toBe(false) + expect(result.current).toBeFalsy() }) expect(callback).toHaveBeenCalledTimes(1) @@ -38,7 +38,7 @@ describe('useIsOverflowing', () => { ) await waitFor(() => { - expect(result.current).toBe(true) + expect(result.current).toBeTruthy() }) }) @@ -60,7 +60,7 @@ describe('useIsOverflowing', () => { ) await waitFor(() => { - expect(result.current).toBe(true) + expect(result.current).toBeTruthy() }) expect(callback).toHaveBeenCalledWith(true) @@ -88,7 +88,7 @@ describe('useIsOverflowing', () => { ) await waitFor(() => { - expect(result.current).toBe(false) + expect(result.current).toBeFalsy() }) }) }) diff --git a/packages/ui/src/mocks/list.ts b/packages/ui/src/mocks/list.ts index 15dd6bc2bc..de0416000b 100644 --- a/packages/ui/src/mocks/list.ts +++ b/packages/ui/src/mocks/list.ts @@ -7,7 +7,7 @@ export type MockData = { } export const generateData = (count?: number, prefix?: string): MockData[] => - [...(Array(count) as unknown[])].map((_, index) => ({ + new Array(count).map((_, index) => ({ department: index % 3 === 0 ? `Front` : `Not Front`, description: `Fake message for row ${index}`, id: `${prefix ? `${prefix}-` : ''}${index}`, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index edf14ef86d..8d0b8f3626 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -212,6 +212,9 @@ importers: eslint: specifier: 9.15.0 version: 9.15.0(jiti@2.1.0) + eslint-plugin-oxlint: + specifier: 0.13.2 + version: 0.13.2 eslint-plugin-testing-library: specifier: 6.5.0 version: 6.5.0(eslint@9.15.0(jiti@2.1.0))(typescript@5.7.2) @@ -242,6 +245,9 @@ importers: lint-staged: specifier: 15.2.10 version: 15.2.10 + oxlint: + specifier: 0.13.2 + version: 0.13.2 postcss: specifier: 8.4.49 version: 8.4.49 @@ -2498,6 +2504,46 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oxlint/darwin-arm64@0.13.2': + resolution: {integrity: sha512-Jbi2UX1xTFTpH/D6n15RzDSG/44oJ0FwrHWl96TsdD3PAOGxmm8LgcrsCU4d22TSN7ShDFB8y34MYplduRzhfA==} + cpu: [arm64] + os: [darwin] + + '@oxlint/darwin-x64@0.13.2': + resolution: {integrity: sha512-ulA2vpNIM14VWJua6BjgX6l8nrjY8Un0uLqBLgELyDHMfpnC0HfrB2mR9PC9tRhb7+BEbgDLWTRs2aTV64eprw==} + cpu: [x64] + os: [darwin] + + '@oxlint/linux-arm64-gnu@0.13.2': + resolution: {integrity: sha512-iwguvPnIx/yIMc2IrwLeRflEFwyn+gcsNR8le5aX5DvQfHiB3/3x5JxOVl7Nd93lLsOg8RyVr2E11CxV10rKWw==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-arm64-musl@0.13.2': + resolution: {integrity: sha512-YJVvVyuvUobgCmiE47kBwN6dymLcQBG5/NsEaCbVyYw5HQJN7Ywp/x4suaF4c6M9ItPCnLxEZ4Lwu5TkrkU3vQ==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-x64-gnu@0.13.2': + resolution: {integrity: sha512-w78gjd3ZJ+ahByS5gmN0jxFn/+G3W/oBJbyGFOuteXYcJwLj6JIyS0VhpMKxuAdFNCnsjJeDHSbdWaCV6VkA3A==} + cpu: [x64] + os: [linux] + + '@oxlint/linux-x64-musl@0.13.2': + resolution: {integrity: sha512-yedk6KQT3jtBzpzExo3Ib7NwfuugaaFKvk/EzXEojMQ5aYh6CNhmVmm+adzwBp4YyOeXQZxSPHQ1LcNwDCUJIA==} + cpu: [x64] + os: [linux] + + '@oxlint/win32-arm64@0.13.2': + resolution: {integrity: sha512-KZvJ3uMWSCwkZtvOcO+K4mLmYU/jIcHUb00s8aPasG1taflbjINsq9ZUUj5LNigcDwJRZGUwNcvY5KuQ+E9WtQ==} + cpu: [arm64] + os: [win32] + + '@oxlint/win32-x64@0.13.2': + resolution: {integrity: sha512-G8FM8RTJRMt2dPRJ/TXhqIEu9k00vRAoh3DSV2r3o+jD8FZDBZXOgoDn2D0F9HqgFLEOVTxpfalmWntyk2hmyw==} + cpu: [x64] + os: [win32] + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -4566,6 +4612,9 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-oxlint@0.13.2: + resolution: {integrity: sha512-/i/zrkpU7lsDqM/WPBa4QWv9s8iSFVdfHzbUyNqYejFgRqpfd9toqHiGPFLJ7qNaFbH3GVhS5refo3CHdU2nwg==} + eslint-plugin-react-hooks@5.0.0: resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} engines: {node: '>=10'} @@ -5500,6 +5549,9 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -6082,6 +6134,11 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + oxlint@0.13.2: + resolution: {integrity: sha512-R7wXUspM75TeEXaAFLmuPH0W3h5B5r6BNtOvkcnVddD3BWbBiEOWe9bCqt4+te0l8wQ4a0/XLCGrJroxFFeajg==} + engines: {node: '>=14.*'} + hasBin: true + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -10070,6 +10127,30 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@oxlint/darwin-arm64@0.13.2': + optional: true + + '@oxlint/darwin-x64@0.13.2': + optional: true + + '@oxlint/linux-arm64-gnu@0.13.2': + optional: true + + '@oxlint/linux-arm64-musl@0.13.2': + optional: true + + '@oxlint/linux-x64-gnu@0.13.2': + optional: true + + '@oxlint/linux-x64-musl@0.13.2': + optional: true + + '@oxlint/win32-arm64@0.13.2': + optional: true + + '@oxlint/win32-x64@0.13.2': + optional: true + '@pkgjs/parseargs@0.11.0': optional: true @@ -12594,6 +12675,10 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.1 + eslint-plugin-oxlint@0.13.2: + dependencies: + jsonc-parser: 3.3.1 + eslint-plugin-react-hooks@5.0.0(eslint@9.15.0(jiti@2.1.0)): dependencies: eslint: 9.15.0(jiti@2.1.0) @@ -13635,6 +13720,8 @@ snapshots: json5@2.2.3: {} + jsonc-parser@3.3.1: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -14371,6 +14458,17 @@ snapshots: outdent@0.5.0: {} + oxlint@0.13.2: + optionalDependencies: + '@oxlint/darwin-arm64': 0.13.2 + '@oxlint/darwin-x64': 0.13.2 + '@oxlint/linux-arm64-gnu': 0.13.2 + '@oxlint/linux-arm64-musl': 0.13.2 + '@oxlint/linux-x64-gnu': 0.13.2 + '@oxlint/linux-x64-musl': 0.13.2 + '@oxlint/win32-arm64': 0.13.2 + '@oxlint/win32-x64': 0.13.2 + p-filter@2.1.0: dependencies: p-map: 2.1.0 diff --git a/scripts/figma-synchronise-tokens.mjs b/scripts/figma-synchronise-tokens.mjs index d29acb00c1..b3f3f81fbd 100755 --- a/scripts/figma-synchronise-tokens.mjs +++ b/scripts/figma-synchronise-tokens.mjs @@ -1,3 +1,5 @@ +// oxlint-disable eslint/no-console + import fs from 'node:fs' import { generateThemeCss } from './create-css-variables.mjs' @@ -26,7 +28,7 @@ const header = ` const createCSSFile = (theme, content) => { const cssContent = generateThemeCss(content) const filePath = `packages/themes/public/style/${theme}.css` - fs.writeFileSync(filePath, cssContent, 'utf-8') + fs.writeFileSync(filePath, cssContent, 'utf8') } function alphaOrder(obj) { @@ -74,7 +76,7 @@ function evalValue(value, variables) { return base }) - // eslint-disable-next-line no-eval + // oxlint-disable-next-line no-eval returnedValue = temp.includes('*') ? eval(temp) : temp } @@ -101,7 +103,7 @@ function getValues(data, { typeFilter, variables }) { return values }, {}) - return Object.keys(res).length ? res : null + return Object.keys(res).length > 0 ? res : null } return null diff --git a/scripts/generate-icons-file.ts b/scripts/generate-icons-file.ts index 72ce67e051..6fa767979b 100644 --- a/scripts/generate-icons-file.ts +++ b/scripts/generate-icons-file.ts @@ -1,3 +1,5 @@ +// oxlint-disable eslint/no-console + import { promises as fs } from 'node:fs' import path from 'node:path' @@ -70,7 +72,7 @@ const readDirectoryRecursive = async (dir: string) => { if (file.isDirectory()) { const subDirResults = await readDirectoryRecursive(filePath) - results = results.concat(subDirResults) + results = [...results, ...subDirResults] } else if (path.extname(file.name).toLowerCase() === '.svg') { results.push(filePath) } @@ -80,7 +82,7 @@ const readDirectoryRecursive = async (dir: string) => { } const readSvg = async (filePath: string) => { - const svgContent = await fs.readFile(filePath, 'utf-8') + const svgContent = await fs.readFile(filePath, 'utf8') const innerSvgContent = svgContent.replace(/]*>|<\/svg>/g, '') // Remove and tags // Replace class with className diff --git a/svgo.config.cjs b/svgo.config.cjs index 1d03413780..7848bbdb90 100644 --- a/svgo.config.cjs +++ b/svgo.config.cjs @@ -1,3 +1,4 @@ +// oxlint-disable plugin-import/no-commonjs module.exports = { plugins: [ { diff --git a/utils/test/src/vitest/mockFormErrors.ts b/utils/test/src/vitest/mockFormErrors.ts index fef09fc8d9..578782d673 100644 --- a/utils/test/src/vitest/mockFormErrors.ts +++ b/utils/test/src/vitest/mockFormErrors.ts @@ -1,4 +1,4 @@ -import { type FormErrors } from '../../../../packages/form/src' +import type { FormErrors } from '../../../../packages/form/src' export const mockFormErrors: FormErrors = { maxLength: ({ maxLength }) =>