refactor(web): update deprecated HeadlessUI v2 components (#1580)

* refactor(web): move away from old headless UI dot notation

* refactor(web): refactor `Disclosure` component

* refactor(web): rename formik's `Field` to `FormikField` and keep original HeadlessUI component names
This commit is contained in:
martylukyy 2024-08-12 20:36:45 +02:00 committed by GitHub
parent 21a8e27260
commit 7d7bf9ed4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 185 additions and 176 deletions

View file

@ -3,7 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import { Switch } from "@headlessui/react";
import { Switch, Field, Label, Description } from "@headlessui/react";
import { classNames } from "@utils";
interface CheckboxProps {
@ -23,7 +23,7 @@ export const Checkbox = ({
setValue,
disabled
}: CheckboxProps) => (
<Switch.Group
<Field
as="div"
className={classNames(className ?? "py-2", "flex items-center justify-between")}
onClick={(e) => {
@ -34,14 +34,14 @@ export const Checkbox = ({
{(label || description) ? (
<div className="flex flex-col mr-4">
{label ? (
<Switch.Label as="p" className="text-sm font-medium whitespace-nowrap text-gray-900 dark:text-white" passive>
<Label as="p" className="text-sm font-medium whitespace-nowrap text-gray-900 dark:text-white" passive>
{label}
</Switch.Label>
</Label>
) : null}
{description ? (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-400">
<Description className="text-sm text-gray-500 dark:text-gray-400">
{description}
</Switch.Description>
</Description>
) : null}
</div>
) : null}
@ -81,5 +81,5 @@ export const Checkbox = ({
)}
</span>
</Switch>
</Switch.Group>
</Field>
);

View file

@ -6,7 +6,7 @@
import toast from "react-hot-toast";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useRouter } from "@tanstack/react-router";
import { Disclosure } from "@headlessui/react";
import { Disclosure, DisclosureButton } from "@headlessui/react";
import { Bars3Icon, XMarkIcon, MegaphoneIcon } from "@heroicons/react/24/outline";
import { APIClient } from "@api/APIClient";
@ -60,7 +60,7 @@ export const Header = () => {
<RightNav logoutMutation={logoutMutation.mutate} />
<div className="-mr-2 flex sm:hidden">
{/* Mobile menu button */}
<Disclosure.Button className="bg-gray-200 dark:bg-gray-800 inline-flex items-center justify-center p-2 rounded-md text-gray-600 dark:text-gray-400 hover:text-white hover:bg-gray-700">
<DisclosureButton className="bg-gray-200 dark:bg-gray-800 inline-flex items-center justify-center p-2 rounded-md text-gray-600 dark:text-gray-400 hover:text-white hover:bg-gray-700">
<span className="sr-only">Open main menu</span>
{open ? (
<XMarkIcon
@ -73,7 +73,7 @@ export const Header = () => {
aria-hidden="true"
/>
)}
</Disclosure.Button>
</DisclosureButton>
</div>
</div>
</div>

View file

@ -3,8 +3,8 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import {Link} from "@tanstack/react-router";
import { Disclosure } from "@headlessui/react";
import { Link } from "@tanstack/react-router";
import { DisclosurePanel } from "@headlessui/react";
import { classNames } from "@utils";
@ -12,7 +12,7 @@ import { NAV_ROUTES } from "./_shared";
import type { RightNavProps } from "./_shared";
export const MobileNav = (props: RightNavProps) => (
<Disclosure.Panel className="border-b border-gray-300 dark:border-gray-700 md:hidden">
<DisclosurePanel className="border-b border-gray-300 dark:border-gray-700 md:hidden">
<div className="px-2 py-3 space-y-1 sm:px-3">
{NAV_ROUTES.map((item) => (
<Link
@ -48,5 +48,5 @@ export const MobileNav = (props: RightNavProps) => (
Logout
</button>
</div>
</Disclosure.Panel>
</DisclosurePanel>
);

View file

@ -5,7 +5,7 @@
import { Fragment } from "react";
import { UserIcon } from "@heroicons/react/24/solid";
import { Menu, Transition } from "@headlessui/react";
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from "@headlessui/react";
import { classNames } from "@utils";
@ -44,7 +44,7 @@ export const RightNav = (props: RightNavProps) => {
<Menu as="div" className="ml-2 relative">
{({ open }) => (
<>
<Menu.Button
<MenuButton
className={classNames(
open ? "bg-gray-200 dark:bg-gray-800 text-gray-900 dark:text-white" : "hover:text-gray-900 dark:hover:text-white",
"text-gray-600 dark:text-gray-500 hover:bg-gray-200 dark:hover:bg-gray-800 px-3 py-2 rounded-2xl text-sm font-medium",
@ -62,7 +62,7 @@ export const RightNav = (props: RightNavProps) => {
className="inline ml-1 h-5 w-5"
aria-hidden="true"
/>
</Menu.Button>
</MenuButton>
<Transition
show={open}
as={Fragment}
@ -73,11 +73,11 @@ export const RightNav = (props: RightNavProps) => {
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
<MenuItems
static
className="origin-top-right absolute right-0 mt-2 w-48 z-10 divide-y divide-gray-100 dark:divide-gray-750 rounded-md shadow-lg bg-white dark:bg-gray-800 border border-gray-250 dark:border-gray-775 focus:outline-none"
>
<Menu.Item>
<MenuItem>
{({ active }) => (
<Link
to="/settings/account"
@ -95,8 +95,8 @@ export const RightNav = (props: RightNavProps) => {
Account
</Link>
)}
</Menu.Item>
<Menu.Item>
</MenuItem>
<MenuItem>
{({ active }) => (
<Link
to="/settings"
@ -114,8 +114,8 @@ export const RightNav = (props: RightNavProps) => {
Settings
</Link>
)}
</Menu.Item>
<Menu.Item>
</MenuItem>
<MenuItem>
{({ active }) => (
<button
onClick={(e) => {
@ -136,8 +136,8 @@ export const RightNav = (props: RightNavProps) => {
Log out
</button>
)}
</Menu.Item>
</Menu.Items>
</MenuItem>
</MenuItems>
</Transition>
</>
)}

View file

@ -3,9 +3,9 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import { Field } from "formik";
import { Field as FormikField } from "formik";
import Select from "react-select";
import { Switch } from "@headlessui/react";
import { Field, Label, Description } from "@headlessui/react";
import type { FieldProps, FieldValidator } from "formik";
import { classNames } from "@utils";
@ -55,7 +55,7 @@ export const TextFieldWide = ({
</label>
</div>
<div className="sm:col-span-2">
<Field
<FormikField
name={name}
value={defaultValue}
required={required}
@ -81,7 +81,7 @@ export const TextFieldWide = ({
data-1p-ignore
/>
)}
</Field>
</FormikField>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
@ -130,7 +130,7 @@ export const PasswordFieldWide = ({
</label>
</div>
<div className="sm:col-span-2">
<Field
<FormikField
name={name}
defaultValue={defaultValue}
validate={validate}
@ -159,7 +159,7 @@ export const PasswordFieldWide = ({
</div>
</div>
)}
</Field>
</FormikField>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
@ -203,7 +203,7 @@ export const NumberFieldWide = ({
</label>
</div>
<div className="sm:col-span-2">
<Field
<FormikField
name={name}
defaultValue={defaultValue ?? 0}
>
@ -229,7 +229,7 @@ export const NumberFieldWide = ({
placeholder={placeholder}
/>
)}
</Field>
</FormikField>
{help && (
<p className="mt-2 text-sm text-gray-500 dark:text-gray-500" id={`${name}-description`}>{help}</p>
)}
@ -255,23 +255,23 @@ export const SwitchGroupWide = ({
defaultValue
}: SwitchGroupWideProps) => (
<ul className="px-4 divide-y divide-gray-200 dark:divide-gray-700">
<Switch.Group as="li" className="py-4 flex items-center justify-between">
<Field as="li" className="py-4 flex items-center justify-between">
<div className="flex flex-col">
<Switch.Label as="div" passive className="text-sm font-medium text-gray-900 dark:text-white">
<Label as="div" passive className="text-sm font-medium text-gray-900 dark:text-white">
<div className="flex">
{tooltip ? (
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
) : label}
</div>
</Switch.Label>
</Label>
{description && (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-700">
<Description className="text-sm text-gray-500 dark:text-gray-700">
{description}
</Switch.Description>
</Description>
)}
</div>
<Field
<FormikField
name={name}
defaultValue={defaultValue as boolean}
type="checkbox"
@ -288,8 +288,8 @@ export const SwitchGroupWide = ({
}}
/>
)}
</Field>
</Switch.Group>
</FormikField>
</Field>
</ul>
);
@ -314,7 +314,7 @@ export const SelectFieldWide = ({
</label>
</div>
<div className="sm:col-span-2">
<Field name={name} type="select">
<FormikField name={name} type="select">
{({
field,
form: { setFieldValue }
@ -352,7 +352,7 @@ export const SelectFieldWide = ({
options={options}
/>
)}
</Field>
</FormikField>
</div>
</div>
);

View file

@ -4,7 +4,7 @@
*/
import { Field, useFormikContext } from "formik";
import { RadioGroup } from "@headlessui/react";
import { RadioGroup, Description, Label, Radio } from "@headlessui/react";
import { classNames } from "@utils";
export interface radioFieldsetOption {
@ -47,12 +47,12 @@ function RadioFieldsetWide({ name, legend, options }: props) {
<Field name={name} type="radio">
{() => (
<RadioGroup value={values[name]} onChange={onChange}>
<RadioGroup.Label className="sr-only">
<Label className="sr-only">
{legend}
</RadioGroup.Label>
</Label>
<div className="bg-white dark:bg-gray-800 rounded-md -space-y-px">
{options.map((setting, settingIdx) => (
<RadioGroup.Option
<Radio
key={setting.value}
value={setting.value}
className={({ checked }) =>
@ -82,7 +82,7 @@ function RadioFieldsetWide({ name, legend, options }: props) {
aria-hidden="true"
/>
<div className="ml-3 flex flex-col w-full">
<RadioGroup.Label
<Label
as="span"
className={classNames(
"block text-md text-gray-900 dark:text-gray-300",
@ -93,17 +93,17 @@ function RadioFieldsetWide({ name, legend, options }: props) {
{setting.label}
{setting.type && <span className="rounded bg-orange-500 text-orange-900 px-1 ml-2 text-sm">{setting.type}</span>}
</div>
</RadioGroup.Label>
<RadioGroup.Description
</Label>
<Description
as="span"
className="block text-sm text-gray-700 dark:text-gray-400"
>
{setting.description}
</RadioGroup.Description>
</Description>
</div>
</>
)}
</RadioGroup.Option>
</Radio>
))}
</div>
</RadioGroup>

View file

@ -5,7 +5,7 @@
import { Fragment } from "react";
import { Field, FieldProps } from "formik";
import { Listbox, Transition } from "@headlessui/react";
import { Listbox, ListboxButton, Label, ListboxOption, ListboxOptions, Transition } from "@headlessui/react";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/solid";
import { MultiSelect as RMSC } from "react-multi-select-component";
@ -166,11 +166,11 @@ export function DownloadClientSelect({
>
{({ open }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
<Label className="block text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
Client
</Listbox.Label>
</Label>
<div className="mt-1 relative">
<Listbox.Button className="block w-full shadow-sm sm:text-sm rounded-md border py-2 pl-3 pr-10 text-left focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100">
<ListboxButton className="block w-full shadow-sm sm:text-sm rounded-md border py-2 pl-3 pr-10 text-left focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100">
<span className="block truncate">
{field.value
? clients.find((c) => c.id === field.value)?.name
@ -181,7 +181,7 @@ export function DownloadClientSelect({
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true" />
</span>
</Listbox.Button>
</ListboxButton>
<Transition
show={open}
@ -190,14 +190,14 @@ export function DownloadClientSelect({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
<ListboxOptions
static
className="absolute z-10 mt-1 w-full border border-gray-400 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-lg max-h-60 rounded-md py-1 text-base overflow-auto focus:outline-none sm:text-sm"
>
{clients
.filter((c) => c.type === action.type)
.map((client) => (
<Listbox.Option
<ListboxOption
key={client.id}
className={({ active }) => classNames(
active
@ -232,9 +232,9 @@ export function DownloadClientSelect({
) : null}
</>
)}
</Listbox.Option>
</ListboxOption>
))}
</Listbox.Options>
</ListboxOptions>
</Transition>
{meta.touched && meta.error && (
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
@ -294,13 +294,13 @@ export const Select = ({
>
{({ open }) => (
<>
<Listbox.Label className="flex text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
<Label className="flex text-xs font-bold text-gray-800 dark:text-gray-100 uppercase tracking-wide">
{tooltip ? (
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
) : label}
</Listbox.Label>
</Label>
<div className="mt-1 relative">
<Listbox.Button className="block w-full relative shadow-sm sm:text-sm text-left rounded-md border pl-3 pr-10 py-2.5 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100">
<ListboxButton className="block w-full relative shadow-sm sm:text-sm text-left rounded-md border pl-3 pr-10 py-2.5 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100">
<span className="block truncate">
{field.value
? options.find((c) => c.value === field.value)?.label
@ -313,7 +313,7 @@ export const Select = ({
aria-hidden="true"
/>
</span>
</Listbox.Button>
</ListboxButton>
<Transition
show={open}
@ -322,12 +322,12 @@ export const Select = ({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
<ListboxOptions
static
className="absolute z-10 mt-1 w-full shadow-lg max-h-60 rounded-md py-1 text-base overflow-auto border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-100 focus:outline-none sm:text-sm"
>
{options.map((opt) => (
<Listbox.Option
<ListboxOption
key={opt.value}
className={({ active: hovered, selected }) =>
classNames(
@ -359,9 +359,9 @@ export const Select = ({
</span>
</>
)}
</Listbox.Option>
</ListboxOption>
))}
</Listbox.Options>
</ListboxOptions>
</Transition>
</div>
</>
@ -395,11 +395,11 @@ export const SelectWide = ({
{({ open }) => (
<div className="py-4 flex items-center justify-between">
<Listbox.Label className="block text-sm font-medium text-gray-900 dark:text-white">
<Label className="block text-sm font-medium text-gray-900 dark:text-white">
{label}
</Listbox.Label>
</Label>
<div className="w-full">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<ListboxButton className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? options.find((c) => c.value === field.value)?.label
@ -412,7 +412,7 @@ export const SelectWide = ({
aria-hidden="true"
/>
</span>
</Listbox.Button>
</ListboxButton>
<Transition
show={open}
@ -421,12 +421,12 @@ export const SelectWide = ({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
<ListboxOptions
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{options.map((opt) => (
<Listbox.Option
<ListboxOption
key={opt.value}
className={({ active }) =>
classNames(
@ -464,9 +464,9 @@ export const SelectWide = ({
) : null}
</>
)}
</Listbox.Option>
</ListboxOption>
))}
</Listbox.Options>
</ListboxOptions>
</Transition>
</div>
</div>
@ -512,14 +512,14 @@ export const AgeSelect = ({
{({ open }) => (
<>
<div className="mt-0 relative">
<Listbox.Button className="block w-full relative shadow-sm text-sm text-left rounded-md border pl-3 pr-10 py-2.5 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-400">
<ListboxButton className="block w-full relative shadow-sm text-sm text-left rounded-md border pl-3 pr-10 py-2.5 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-gray-400">
<span className="block truncate text-gray-500 dark:text-white">
{duration ? options.find(opt => opt.value === duration)?.label : 'Select...'}
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<ChevronUpDownIcon className="h-5 w-5 text-gray-700 dark:text-gray-500" aria-hidden="true" />
</span>
</Listbox.Button>
</ListboxButton>
<Transition
show={open}
as={Fragment}
@ -527,9 +527,9 @@ export const AgeSelect = ({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-10 mt-1 w-full shadow-lg max-h-60 rounded-md py-1 overflow-auto border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-white focus:outline-none text-sm">
<ListboxOptions className="absolute z-10 mt-1 w-full shadow-lg max-h-60 rounded-md py-1 overflow-auto border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-815 dark:text-white focus:outline-none text-sm">
{options.map((option) => (
<Listbox.Option
<ListboxOption
key={option.value}
className={({ active, selected }) =>
`relative cursor-default select-none py-2 pl-3 pr-9 ${selected ? "font-bold text-black dark:text-white bg-gray-300 dark:bg-gray-950" : active ? "text-black dark:text-gray-100 font-normal bg-gray-200 dark:bg-gray-800" : "text-gray-700 dark:text-gray-300 font-normal"
@ -547,9 +547,9 @@ export const AgeSelect = ({
)}
</>
)}
</Listbox.Option>
</ListboxOption>
))}
</Listbox.Options>
</ListboxOptions>
</Transition>
</div>
</>

View file

@ -4,8 +4,8 @@
*/
import type { FieldProps } from "formik";
import { Field } from "formik";
import { Switch as HeadlessSwitch } from "@headlessui/react";
import { Field as FormikField } from "formik";
import { Field, Label, Description } from "@headlessui/react";
import { classNames } from "@utils";
import { DocsTooltip } from "@components/tooltips/DocsTooltip";
@ -30,7 +30,7 @@ const SwitchGroup = ({
disabled,
className
}: SwitchGroupProps) => (
<HeadlessSwitch.Group
<Field
as="div"
className={classNames(
className ?? "py-2",
@ -38,7 +38,7 @@ const SwitchGroup = ({
)}
>
{label && <div className="flex flex-col">
<HeadlessSwitch.Label
<Label
passive
as={heading ? "h2" : "span"}
className={classNames(
@ -51,16 +51,16 @@ const SwitchGroup = ({
<DocsTooltip label={label}>{tooltip}</DocsTooltip>
) : label}
</div>
</HeadlessSwitch.Label>
</Label>
{description && (
<HeadlessSwitch.Description as="span" className="text-sm mt-1 pr-4 text-gray-500 dark:text-gray-400">
<Description as="span" className="text-sm mt-1 pr-4 text-gray-500 dark:text-gray-400">
{description}
</HeadlessSwitch.Description>
</Description>
)}
</div>
}
<Field name={name} type="checkbox">
<FormikField name={name} type="checkbox">
{({
field,
form: { setFieldValue }
@ -75,8 +75,8 @@ const SwitchGroup = ({
disabled={disabled}
/>
)}
</Field>
</HeadlessSwitch.Group>
</FormikField>
</Field>
);
export { SwitchGroup };