From 0c6837e87a42653a9c03ea24269297a71dc0618f Mon Sep 17 00:00:00 2001 From: Roman Dmytrenko Date: Tue, 31 Dec 2024 14:56:44 +0200 Subject: [PATCH 1/2] refactor(ui): replace headlessui Switch with radix-ui Signed-off-by: Roman Dmytrenko --- ui/package-lock.json | 177 ++++++++++++++++++++++++ ui/package.json | 1 + ui/src/app/preferences/Preferences.tsx | 39 ++---- ui/src/components/Switch.tsx | 29 ++++ ui/src/components/forms/Toggle.tsx | 38 ++--- ui/src/components/ui/table-skeleton.tsx | 2 +- 6 files changed, 225 insertions(+), 61 deletions(-) create mode 100644 ui/src/components/Switch.tsx diff --git a/ui/package-lock.json b/ui/package-lock.json index f401f22ccf..78302b7bc9 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -26,6 +26,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-select": "^2.1.4", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.2", "@reduxjs/toolkit": "^2.5.0", "@tanstack/react-table": "^8.20.5", "@uiw/codemirror-theme-tokyo-night": "^4.23.6", @@ -5225,6 +5226,127 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.2.tgz", + "integrity": "sha512-zGukiWHjEdBCRyXvKR6iXAQG6qXm2esuAD6kDOi9Cn+1X6ev3ASo4+CsYaD6Fov9r/AQFekqnD/7+V0Cs6/98g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", @@ -19505,6 +19627,61 @@ } } }, + "@radix-ui/react-switch": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.2.tgz", + "integrity": "sha512-zGukiWHjEdBCRyXvKR6iXAQG6qXm2esuAD6kDOi9Cn+1X6ev3ASo4+CsYaD6Fov9r/AQFekqnD/7+V0Cs6/98g==", + "requires": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "dependencies": { + "@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + }, + "@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "requires": {} + }, + "@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "requires": {} + }, + "@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "requires": { + "@radix-ui/react-slot": "1.1.1" + } + }, + "@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "requires": {} + }, + "@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "requires": { + "@radix-ui/react-use-callback-ref": "1.1.0" + } + } + } + }, "@radix-ui/react-use-callback-ref": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", diff --git a/ui/package.json b/ui/package.json index 7a27ba0305..c11308dea4 100644 --- a/ui/package.json +++ b/ui/package.json @@ -32,6 +32,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-select": "^2.1.4", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.2", "@reduxjs/toolkit": "^2.5.0", "@tanstack/react-table": "^8.20.5", "@uiw/codemirror-theme-tokyo-night": "^4.23.6", diff --git a/ui/src/app/preferences/Preferences.tsx b/ui/src/app/preferences/Preferences.tsx index dcd6490c80..2b7964b527 100644 --- a/ui/src/app/preferences/Preferences.tsx +++ b/ui/src/app/preferences/Preferences.tsx @@ -1,11 +1,10 @@ -import { Switch } from '@headlessui/react'; +import { Switch } from '~/components/Switch'; import { Formik } from 'formik'; import { useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import Select from '~/components/forms/Select'; import { useTimezone } from '~/data/hooks/timezone'; import { Theme, Timezone } from '~/types/Preferences'; -import { cls } from '~/utils/helpers'; import { selectTheme, selectTimezone, @@ -60,15 +59,8 @@ export default function Preferences() { }} /> - - +
+ UTC Timezone

Display dates and times in UTC timezone @@ -76,32 +68,19 @@ export default function Preferences() {

{inTimezone(new Date().toISOString())}

- -
+ +
{ + aria-readonly + onCheckedChange={() => { dispatch( timezoneChanged(isUTC ? Timezone.LOCAL : Timezone.UTC) ); }} - className={cls( - 'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent bg-gray-200 transition-colors duration-200 ease-in-out focus:outline-none sm:ml-auto', - { 'bg-violet-400': isUTC } - )} - > - + />
- +
diff --git a/ui/src/components/Switch.tsx b/ui/src/components/Switch.tsx new file mode 100644 index 0000000000..807de94817 --- /dev/null +++ b/ui/src/components/Switch.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import * as SwitchPrimitives from '@radix-ui/react-switch'; + +import { cls } from '~/utils/helpers'; + +const Switch = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + className?: string; + } +>(({ className, ...props }, ref) => ( + + + +)); +Switch.displayName = SwitchPrimitives.Root.displayName; + +export { Switch }; diff --git a/ui/src/components/forms/Toggle.tsx b/ui/src/components/forms/Toggle.tsx index 9fcf641640..64b26c905b 100644 --- a/ui/src/components/forms/Toggle.tsx +++ b/ui/src/components/forms/Toggle.tsx @@ -1,6 +1,5 @@ -import { Switch } from '@headlessui/react'; +import { Switch } from '~/components/Switch'; import { useField } from 'formik'; -import { cls } from '~/utils/helpers'; type ToggleProps = { id: string; @@ -17,47 +16,26 @@ export default function Toggle(props: ToggleProps) { const [field] = useField(props); return ( - +
- - {label} - + {label} {description && ( - - {description} - + {description} )} { + onCheckedChange={(e: boolean) => { onChange && onChange(e); }} - className={cls( - 'relative inline-flex h-6 w-11 items-center rounded-full bg-violet-200 hover:cursor-pointer focus:ring-0', - { - 'bg-green-400': checked, - 'hover:cursor-not-allowed': disabled - } - )} + className="data-[state=checked]:bg-green-400 data-[state=unchecked]:bg-violet-200" > Enable - - +
); } diff --git a/ui/src/components/ui/table-skeleton.tsx b/ui/src/components/ui/table-skeleton.tsx index 39acc64097..8bdfc660db 100644 --- a/ui/src/components/ui/table-skeleton.tsx +++ b/ui/src/components/ui/table-skeleton.tsx @@ -5,7 +5,7 @@ const TableSkeleton = () => { <>
- +
From 700ac0bd4b6a174859259da55aaaf2b5c8ca1708 Mon Sep 17 00:00:00 2001 From: Roman Dmytrenko Date: Tue, 31 Dec 2024 18:52:33 +0200 Subject: [PATCH 2/2] added missing things so playwright is happy Signed-off-by: Roman Dmytrenko --- ui/src/app/preferences/Preferences.tsx | 7 +++++-- ui/src/components/forms/Toggle.tsx | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/src/app/preferences/Preferences.tsx b/ui/src/app/preferences/Preferences.tsx index 2b7964b527..ff7ac6d5a5 100644 --- a/ui/src/app/preferences/Preferences.tsx +++ b/ui/src/app/preferences/Preferences.tsx @@ -60,7 +60,10 @@ export default function Preferences() { />
- + UTC Timezone

Display dates and times in UTC timezone @@ -72,7 +75,7 @@ export default function Preferences() {

{ dispatch( timezoneChanged(isUTC ? Timezone.LOCAL : Timezone.UTC) diff --git a/ui/src/components/forms/Toggle.tsx b/ui/src/components/forms/Toggle.tsx index 64b26c905b..9ee5fa8a0e 100644 --- a/ui/src/components/forms/Toggle.tsx +++ b/ui/src/components/forms/Toggle.tsx @@ -18,24 +18,27 @@ export default function Toggle(props: ToggleProps) { return (
- {label} + + {label} + {description && ( {description} )} { onChange && onChange(e); }} className="data-[state=checked]:bg-green-400 data-[state=unchecked]:bg-violet-200" - > - Enable - + />
); }