feat(WEB-163): edit profile field validation
This commit is contained in:
@@ -19,6 +19,13 @@
|
||||
padding: var(--Spacing-x1) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.comboBoxContainer:has(
|
||||
.input[data-invalid="true"],
|
||||
.input[aria-invalid="true"]
|
||||
) {
|
||||
border-color: var(--Scandic-Red-60);
|
||||
}
|
||||
|
||||
.label {
|
||||
grid-area: label;
|
||||
}
|
||||
@@ -66,6 +73,8 @@
|
||||
var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.listBoxItem[data-focused="true"],
|
||||
.listBoxItem[data-focus-visible="true"],
|
||||
.listBoxItem[data-selected="true"],
|
||||
.listBoxItem:hover {
|
||||
background-color: var(--Scandic-Blue-00);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
"use client"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
import { useState } from "react"
|
||||
import {
|
||||
Button,
|
||||
ComboBox,
|
||||
FieldError,
|
||||
Input,
|
||||
type Key,
|
||||
ListBox,
|
||||
@@ -18,6 +16,7 @@ import Label from "@/components/TempDesignSystem/Form/Label"
|
||||
import SelectChevron from "@/components/TempDesignSystem/Form/SelectChevron"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
|
||||
import ErrorMessage from "../ErrorMessage"
|
||||
import { countries } from "./countries"
|
||||
|
||||
import styles from "./country.module.css"
|
||||
@@ -42,7 +41,7 @@ export default function CountrySelect({
|
||||
}
|
||||
}
|
||||
const { control, setValue } = useFormContext()
|
||||
const { field } = useController({
|
||||
const { field, fieldState, formState } = useController({
|
||||
control,
|
||||
name,
|
||||
rules: registerOptions,
|
||||
@@ -60,6 +59,7 @@ export default function CountrySelect({
|
||||
aria-label={formatMessage({ id: "Select country of residence" })}
|
||||
className={styles.select}
|
||||
isRequired={!!registerOptions?.required}
|
||||
isInvalid={fieldState.invalid}
|
||||
name={field.name}
|
||||
onBlur={field.onBlur}
|
||||
onSelectionChange={handleChange}
|
||||
@@ -85,9 +85,7 @@ export default function CountrySelect({
|
||||
<SelectChevron />
|
||||
</Button>
|
||||
</div>
|
||||
<FieldError>
|
||||
<ErrorMessage name={name} />
|
||||
</FieldError>
|
||||
<ErrorMessage errors={formState.errors} name={name} />
|
||||
<Popover
|
||||
className={styles.popover}
|
||||
placement="bottom"
|
||||
|
||||
@@ -8,5 +8,5 @@ export const enum DateName {
|
||||
export interface DateProps
|
||||
extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
name: string
|
||||
registerOptions: RegisterOptions
|
||||
registerOptions?: RegisterOptions
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
"use client"
|
||||
import { parseDate } from "@internationalized/date"
|
||||
import {
|
||||
DateInput,
|
||||
DatePicker,
|
||||
DateSegment,
|
||||
Group,
|
||||
} from "react-aria-components"
|
||||
import { DateInput, DatePicker, Group } from "react-aria-components"
|
||||
import { useController, useFormContext, useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
@@ -23,7 +18,7 @@ import type { Key } from "react-aria-components"
|
||||
import type { DateProps } from "./date"
|
||||
|
||||
/** TODO: Get selecting with Enter-key to work */
|
||||
export default function DateSelect({ name, registerOptions }: DateProps) {
|
||||
export default function DateSelect({ name, registerOptions = {} }: DateProps) {
|
||||
const { formatMessage } = useIntl()
|
||||
const d = useWatch({ name })
|
||||
const { control, setValue } = useFormContext()
|
||||
@@ -84,7 +79,7 @@ export default function DateSelect({ name, registerOptions }: DateProps) {
|
||||
)
|
||||
}
|
||||
return (
|
||||
<DateSegment className={styles.day} segment={segment}>
|
||||
<div className={styles.day}>
|
||||
<Select
|
||||
aria-label={dayLabel}
|
||||
items={days}
|
||||
@@ -92,13 +87,15 @@ export default function DateSelect({ name, registerOptions }: DateProps) {
|
||||
name={DateName.date}
|
||||
onSelect={createOnSelect(DateName.date)}
|
||||
placeholder="DD"
|
||||
required
|
||||
tabIndex={3}
|
||||
value={segment.value}
|
||||
/>
|
||||
</DateSegment>
|
||||
</div>
|
||||
)
|
||||
case "month":
|
||||
return (
|
||||
<DateSegment className={styles.month} segment={segment}>
|
||||
<div className={styles.month}>
|
||||
<Select
|
||||
aria-label={monthLabel}
|
||||
items={months}
|
||||
@@ -106,13 +103,15 @@ export default function DateSelect({ name, registerOptions }: DateProps) {
|
||||
name={DateName.month}
|
||||
onSelect={createOnSelect(DateName.month)}
|
||||
placeholder="MM"
|
||||
required
|
||||
tabIndex={2}
|
||||
value={segment.value}
|
||||
/>
|
||||
</DateSegment>
|
||||
</div>
|
||||
)
|
||||
case "year":
|
||||
return (
|
||||
<DateSegment className={styles.year} segment={segment}>
|
||||
<div className={styles.year}>
|
||||
<Select
|
||||
aria-label={yearLabel}
|
||||
items={years}
|
||||
@@ -120,9 +119,11 @@ export default function DateSelect({ name, registerOptions }: DateProps) {
|
||||
name={DateName.year}
|
||||
onSelect={createOnSelect(DateName.year)}
|
||||
placeholder="YYYY"
|
||||
required
|
||||
tabIndex={1}
|
||||
value={segment.value}
|
||||
/>
|
||||
</DateSegment>
|
||||
</div>
|
||||
)
|
||||
default:
|
||||
/** DateInput forces return of ReactElement */
|
||||
|
||||
13
components/TempDesignSystem/Form/ErrorMessage/Error.tsx
Normal file
13
components/TempDesignSystem/Form/ErrorMessage/Error.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { InfoCircleIcon } from "@/components/Icons"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./error.module.css"
|
||||
|
||||
export default function Error({ children }: React.PropsWithChildren) {
|
||||
return (
|
||||
<Caption className={styles.message} fontOnly>
|
||||
<InfoCircleIcon color="red" />
|
||||
{children}
|
||||
</Caption>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ErrorMessage as RHFErrorMessage } from "@hookform/error-message"
|
||||
|
||||
import { InfoCircleIcon } from "@/components/Icons"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./error.module.css"
|
||||
import Error from "./Error"
|
||||
|
||||
import type { ErrorMessageProps } from "./errorMessage"
|
||||
|
||||
@@ -15,12 +12,7 @@ export default function ErrorMessage<T>({
|
||||
<RHFErrorMessage
|
||||
errors={errors}
|
||||
name={name}
|
||||
render={({ message }) => (
|
||||
<Caption className={styles.message} fontOnly>
|
||||
<InfoCircleIcon color="red" />
|
||||
{message}
|
||||
</Caption>
|
||||
)}
|
||||
render={({ message }) => <Error>{message}</Error>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,66 +2,86 @@
|
||||
import {
|
||||
Input as AriaInput,
|
||||
Label as AriaLabel,
|
||||
Text,
|
||||
TextField,
|
||||
} from "react-aria-components"
|
||||
import { useController, useFormContext } from "react-hook-form"
|
||||
import { Controller, useFormContext } from "react-hook-form"
|
||||
|
||||
import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage"
|
||||
import { CheckIcon, InfoCircleIcon } from "@/components/Icons"
|
||||
import Label from "@/components/TempDesignSystem/Form/Label"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./input.module.css"
|
||||
|
||||
import type { HTMLAttributes, WheelEvent } from "react"
|
||||
|
||||
import type { InputProps } from "./input"
|
||||
|
||||
export default function Input({
|
||||
"aria-label": ariaLabel,
|
||||
disabled,
|
||||
disabled = false,
|
||||
helpText = "",
|
||||
label,
|
||||
name,
|
||||
placeholder = "",
|
||||
registerOptions = {},
|
||||
required = false,
|
||||
type = "text",
|
||||
}: InputProps) {
|
||||
const { control } = useFormContext()
|
||||
const rules = {
|
||||
...registerOptions,
|
||||
required:
|
||||
"required" in registerOptions ? !!registerOptions.required : required,
|
||||
let numberAttributes: HTMLAttributes<HTMLInputElement> = {}
|
||||
if (type === "number") {
|
||||
numberAttributes.onWheel = function (evt: WheelEvent<HTMLInputElement>) {
|
||||
evt.currentTarget.blur()
|
||||
}
|
||||
}
|
||||
const { field, fieldState, formState } = useController({
|
||||
control,
|
||||
name,
|
||||
rules,
|
||||
})
|
||||
|
||||
return (
|
||||
<TextField
|
||||
aria-label={ariaLabel}
|
||||
defaultValue={field.value}
|
||||
isDisabled={disabled ?? field.disabled}
|
||||
isInvalid={fieldState.invalid}
|
||||
isRequired={!!registerOptions?.required}
|
||||
name={field.name}
|
||||
type={type}
|
||||
>
|
||||
<AriaLabel className={styles.container} htmlFor={field.name}>
|
||||
<Body asChild fontOnly>
|
||||
<AriaInput
|
||||
className={styles.input}
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
onBlur={field.onBlur}
|
||||
onChange={field.onChange}
|
||||
placeholder={placeholder}
|
||||
ref={field.ref}
|
||||
required={rules.required}
|
||||
/>
|
||||
</Body>
|
||||
<Label required={rules.required}>{label}</Label>
|
||||
</AriaLabel>
|
||||
<ErrorMessage errors={formState.errors} name={field.name} />
|
||||
</TextField>
|
||||
<Controller
|
||||
disabled={disabled}
|
||||
control={control}
|
||||
name={name}
|
||||
rules={registerOptions}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
aria-label={ariaLabel}
|
||||
isDisabled={field.disabled}
|
||||
isInvalid={fieldState.invalid}
|
||||
isRequired={!!registerOptions.required}
|
||||
name={field.name}
|
||||
onBlur={field.onBlur}
|
||||
onChange={field.onChange}
|
||||
validationBehavior="aria"
|
||||
value={field.value}
|
||||
>
|
||||
<AriaLabel className={styles.container} htmlFor={field.name}>
|
||||
<Body asChild fontOnly>
|
||||
<AriaInput
|
||||
{...numberAttributes}
|
||||
aria-labelledby={field.name}
|
||||
className={styles.input}
|
||||
id={field.name}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
/>
|
||||
</Body>
|
||||
<Label required={!!registerOptions.required}>{label}</Label>
|
||||
</AriaLabel>
|
||||
{helpText && !fieldState.error ? (
|
||||
<Caption asChild color="black">
|
||||
<Text className={styles.helpText} slot="description">
|
||||
<CheckIcon height={20} width={30} />
|
||||
{helpText}
|
||||
</Text>
|
||||
</Caption>
|
||||
) : null}
|
||||
{fieldState.error ? (
|
||||
<Caption className={styles.error} fontOnly>
|
||||
<InfoCircleIcon color="red" />
|
||||
{fieldState.error.message}
|
||||
</Caption>
|
||||
) : null}
|
||||
</TextField>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@
|
||||
border-color: var(--Scandic-Blue-90);
|
||||
}
|
||||
|
||||
.container:has(.input:disabled) {
|
||||
background-color: var(--Main-Grey-10);
|
||||
border: none;
|
||||
color: var(--Main-Grey-40);
|
||||
}
|
||||
|
||||
.container:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
|
||||
border-color: var(--Scandic-Red-60);
|
||||
}
|
||||
|
||||
.input {
|
||||
background: none;
|
||||
border: none;
|
||||
@@ -42,3 +52,21 @@
|
||||
height: 18px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:disabled {
|
||||
color: var(--Main-Grey-40);
|
||||
}
|
||||
|
||||
.helpText {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.error {
|
||||
align-items: center;
|
||||
color: var(--Scandic-Red-60);
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
margin: var(--Spacing-x1) 0 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { RegisterOptions } from "react-hook-form"
|
||||
import type { RegisterOptions, UseFormRegister } from "react-hook-form"
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
helpText?: string
|
||||
label: string
|
||||
name: string
|
||||
registerOptions?: RegisterOptions
|
||||
|
||||
@@ -28,3 +28,7 @@ input:placeholder-shown ~ .label {
|
||||
align-self: center;
|
||||
grid-row: 1/-1;
|
||||
}
|
||||
|
||||
input:disabled ~ .label {
|
||||
color: var(--Main-Grey-40);
|
||||
}
|
||||
|
||||
113
components/TempDesignSystem/Form/NewPassword/index.tsx
Normal file
113
components/TempDesignSystem/Form/NewPassword/index.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
"use client"
|
||||
import {
|
||||
Input as AriaInput,
|
||||
Label as AriaLabel,
|
||||
Text,
|
||||
TextField,
|
||||
} from "react-aria-components"
|
||||
import { Controller, useFormContext } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { CheckIcon, CloseIcon } from "@/components/Icons"
|
||||
import Error from "@/components/TempDesignSystem/Form/ErrorMessage/Error"
|
||||
import Label from "@/components/TempDesignSystem/Form/Label"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import { type IconProps, Key, type NewPasswordProps } from "./newPassword"
|
||||
|
||||
import styles from "./newPassword.module.css"
|
||||
|
||||
export default function NewPassword({
|
||||
"aria-label": ariaLabel,
|
||||
disabled = false,
|
||||
placeholder = "",
|
||||
registerOptions = {},
|
||||
}: NewPasswordProps) {
|
||||
const { control } = useFormContext()
|
||||
const { formatMessage } = useIntl()
|
||||
return (
|
||||
<Controller
|
||||
disabled={disabled}
|
||||
control={control}
|
||||
name="newPassword"
|
||||
rules={registerOptions}
|
||||
render={({ field, fieldState }) => {
|
||||
const messages = (fieldState.error?.message?.split(",") ?? []) as Key[]
|
||||
return (
|
||||
<TextField
|
||||
aria-label={ariaLabel}
|
||||
isDisabled={field.disabled}
|
||||
isInvalid={fieldState.invalid}
|
||||
isRequired={!!registerOptions.required}
|
||||
name={field.name}
|
||||
onBlur={field.onBlur}
|
||||
onChange={field.onChange}
|
||||
validationBehavior="aria"
|
||||
value={field.value}
|
||||
type="password"
|
||||
>
|
||||
<AriaLabel className={styles.container} htmlFor={field.name}>
|
||||
<Body asChild fontOnly>
|
||||
<AriaInput
|
||||
aria-labelledby={field.name}
|
||||
className={styles.input}
|
||||
id={field.name}
|
||||
placeholder={placeholder}
|
||||
type="password"
|
||||
/>
|
||||
</Body>
|
||||
<Label required={!!registerOptions.required}>
|
||||
{formatMessage({ id: "New password" })}
|
||||
</Label>
|
||||
</AriaLabel>
|
||||
{field.value ? (
|
||||
<div className={styles.errors}>
|
||||
<Caption asChild color="black">
|
||||
<Text className={styles.helpText} slot="description">
|
||||
<Icon matcher={Key.CHAR_LENGTH} messages={messages} />
|
||||
10 {formatMessage({ id: "to" })} 40{" "}
|
||||
{formatMessage({ id: "characters" })}
|
||||
</Text>
|
||||
</Caption>
|
||||
<Caption asChild color="black">
|
||||
<Text className={styles.helpText} slot="description">
|
||||
<Icon matcher={Key.UPPERCASE} messages={messages} />1{" "}
|
||||
{formatMessage({ id: "uppercase letter" })}
|
||||
</Text>
|
||||
</Caption>
|
||||
<Caption asChild color="black">
|
||||
<Text className={styles.helpText} slot="description">
|
||||
<Icon matcher={Key.NUM} messages={messages} />1{" "}
|
||||
{formatMessage({ id: "number" })}
|
||||
</Text>
|
||||
</Caption>
|
||||
<Caption asChild color="black">
|
||||
<Text className={styles.helpText} slot="description">
|
||||
<Icon matcher={Key.SPECIAL_CHAR} messages={messages} />1{" "}
|
||||
{formatMessage({ id: "special character" })}
|
||||
</Text>
|
||||
</Caption>
|
||||
</div>
|
||||
) : null}
|
||||
{!field.value && fieldState.error ? (
|
||||
<Error>
|
||||
<Text className={styles.helpText} slot="description">
|
||||
{fieldState.error.message}
|
||||
</Text>
|
||||
</Error>
|
||||
) : null}
|
||||
</TextField>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function Icon({ matcher, messages }: IconProps) {
|
||||
return messages.includes(matcher) ? (
|
||||
<CloseIcon color="red" height={20} width={20} />
|
||||
) : (
|
||||
<CheckIcon color="green" height={20} width={20} />
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
.container {
|
||||
align-content: center;
|
||||
background-color: var(--Main-Grey-White);
|
||||
border-color: var(--Scandic-Beige-40);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x-half);
|
||||
grid-template-rows: auto auto;
|
||||
height: 60px;
|
||||
padding: var(--Spacing-x1) var(--Spacing-x2);
|
||||
transition: border-color 200ms ease;
|
||||
}
|
||||
|
||||
.container:has(.input:not(:focus):placeholder-shown) {
|
||||
gap: 0;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
.container:has(.input:active, .input:focus) {
|
||||
border-color: var(--Scandic-Blue-90);
|
||||
}
|
||||
|
||||
.container:has(.input:disabled) {
|
||||
background-color: var(--Main-Grey-10);
|
||||
border: none;
|
||||
color: var(--Main-Grey-40);
|
||||
}
|
||||
|
||||
.container:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
|
||||
border-color: var(--Scandic-Red-60);
|
||||
}
|
||||
|
||||
.input {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--Main-Grey-100);
|
||||
height: 18px;
|
||||
margin: 0;
|
||||
order: 2;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.input:not(:active, :focus):placeholder-shown {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.input:focus,
|
||||
.input:focus:placeholder-shown {
|
||||
height: 18px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:disabled {
|
||||
color: var(--Main-Grey-40);
|
||||
}
|
||||
|
||||
.errors {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--Spacing-x-one-and-half) var(--Spacing-x1);
|
||||
padding-top: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.helpText {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
19
components/TempDesignSystem/Form/NewPassword/newPassword.ts
Normal file
19
components/TempDesignSystem/Form/NewPassword/newPassword.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { RegisterOptions } from "react-hook-form"
|
||||
|
||||
export enum Key {
|
||||
CHAR_LENGTH = "CHAR_LENGTH",
|
||||
NUM = "NUM",
|
||||
SPECIAL_CHAR = "SPECIAL_CHAR",
|
||||
UPPERCASE = "UPPERCASE",
|
||||
}
|
||||
|
||||
export interface NewPasswordProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string
|
||||
registerOptions?: RegisterOptions
|
||||
}
|
||||
|
||||
export interface IconProps {
|
||||
matcher: Key
|
||||
messages: Key[]
|
||||
}
|
||||
@@ -132,7 +132,6 @@ export default function Phone({
|
||||
</AriaLabel>
|
||||
<ErrorMessage errors={formState.errors} name={field.name} />
|
||||
</TextField>
|
||||
<ErrorMessage errors={formState.errors} name={name} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
border-color: var(--Scandic-Blue-90);
|
||||
}
|
||||
|
||||
.inputContainer:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
|
||||
border-color: var(--Scandic-Red-60);
|
||||
}
|
||||
|
||||
.input {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
@@ -31,6 +31,8 @@ export default function Select({
|
||||
name,
|
||||
onSelect,
|
||||
placeholder,
|
||||
required = false,
|
||||
tabIndex,
|
||||
value,
|
||||
}: SelectProps) {
|
||||
const [rootDiv, setRootDiv] = useState<SelectPortalContainer>(undefined)
|
||||
@@ -58,8 +60,10 @@ export default function Select({
|
||||
>
|
||||
<Body asChild fontOnly>
|
||||
<Button className={styles.input}>
|
||||
<div className={styles.inputContentWrapper}>
|
||||
<Label size="small">{label}</Label>
|
||||
<div className={styles.inputContentWrapper} tabIndex={tabIndex}>
|
||||
<Label required={required} size="small">
|
||||
{label}
|
||||
</Label>
|
||||
<SelectValue />
|
||||
</div>
|
||||
<SelectChevron />
|
||||
|
||||
@@ -58,7 +58,8 @@
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.listBoxItem[data-focused="true"] {
|
||||
.listBoxItem[data-focused="true"],
|
||||
.listBoxItem[data-selected="true"] {
|
||||
background: var(--UI-Input-Controls-Surface-Hover, var(--Scandic-Blue-00));
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Key } from "react-aria-components"
|
||||
import type { Key, SelectProps as AriaSelectProps } from "react-aria-components"
|
||||
|
||||
export interface SelectProps
|
||||
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "onSelect"> {
|
||||
|
||||
Reference in New Issue
Block a user