Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/free-mugs-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ultraviolet/ui": patch
---

Fix accesbility issue inside Pagination PerPage label.
5 changes: 5 additions & 0 deletions .changeset/small-coins-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ultraviolet/form": patch
---

use new useField hook to TimeInputField, TextAreaField which allow usage of the rhf register api"
Comment thread
philibea marked this conversation as resolved.
2 changes: 1 addition & 1 deletion packages/form/src/components/KeyValueField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { Control, FieldArrayPath, FieldValues } from 'react-hook-form'
import { TextInputField } from '../TextInputField'

type InputKeyProps = {
label: ComponentProps<typeof TextInputField>['label']
label: NonNullable<ComponentProps<typeof TextInputField>['label']>
required?: ComponentProps<typeof TextInputField>['required']
regex?: ComponentProps<typeof TextInputField>['regex']
}
Expand Down
76 changes: 7 additions & 69 deletions packages/form/src/components/TextAreaField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
'use client'

import { TextArea } from '@ultraviolet/ui'
import type { LabelProps } from '@ultraviolet/ui'
import type { ComponentProps, KeyboardEvent } from 'react'
import { useController } from 'react-hook-form'
import type { FieldPath, FieldValues, Path, PathValue } from 'react-hook-form'
import { useErrors } from '../../providers'
import type { FieldPath, FieldValues } from 'react-hook-form'
import { useField } from '../../hooks/useField'
import type { BaseFieldProps } from '../../types'
import { validateRegex } from '../../utils/validateRegex'

export type TextAreaFieldProps<
TFieldValues extends FieldValues,
Expand All @@ -15,7 +14,7 @@ export type TextAreaFieldProps<
Omit<ComponentProps<typeof TextArea>, 'value' | 'error' | 'name' | 'onChange'> & {
regex?: (RegExp | RegExp[])[]
submitOnEnter?: boolean
}
} & LabelProps
Comment thread
philibea marked this conversation as resolved.

/**
* This component offers a form field based on Ultraviolet UI TextArea component
Expand All @@ -25,43 +24,11 @@ export const TextAreaField = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
control,
label,
onChange,
minLength,
maxLength,
name,
onBlur,
onKeyDown,
required,
regex: regexes,
submitOnEnter,
validate,
errorLabel,
'aria-label': ariaLabel,
onKeyDown,
...props
}: TextAreaFieldProps<TFieldValues, TFieldName>) => {
const { getError } = useErrors()

const {
field,
fieldState: { error },
} = useController<TFieldValues, TFieldName>({
control,
name,
rules: {
maxLength,
minLength,
required,
validate: {
...(regexes
? {
pattern: value => validateRegex(value, regexes),
}
: {}),
...validate,
},
},
})
const { fieldProps } = useField(props)

const onKeyDownHandler = (event: KeyboardEvent<HTMLTextAreaElement>) => {
if (submitOnEnter && event.key === 'Enter' && !event.shiftKey) {
Expand All @@ -80,36 +47,7 @@ export const TextAreaField = <
}
}

return (
<TextArea
{...props}
error={getError(
{
label: errorLabel ?? label ?? ariaLabel ?? name,
maxLength,
minLength,
regex: regexes,
value: field.value,
},
error,
)}
maxLength={maxLength}
minLength={minLength}
name={name}
onBlur={event => {
onBlur?.(event)
field.onBlur()
}}
onChange={event => {
field.onChange(event)
onChange?.(event as PathValue<TFieldValues, Path<TFieldValues>>)
}}
onKeyDown={onKeyDownHandler}
required={required}
value={field.value}
{...(label ? { label } : { 'aria-label': ariaLabel! })}
/>
)
return <TextArea {...props} {...fieldProps} onKeyDown={onKeyDownHandler} />
}

TextAreaField.displayName = 'TextAreaField'
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ exports[`textInputField > should render correctly 1`] = `
Test
</label>
<div
aria-required="false"
class="uv_yl1780 uv_yl1781 uv_yl1785 uv_toi52u0 uv_toi52ud uv_toi52u2j uv_toi52u77"
>
<div
Expand Down Expand Up @@ -110,7 +109,6 @@ exports[`textInputField > should render correctly disabled 1`] = `
Test
</label>
<div
aria-required="false"
class="uv_yl1780 uv_yl1781 uv_yl1785 uv_toi52u0 uv_toi52ud uv_toi52u2j uv_toi52u77"
>
<div
Expand Down
51 changes: 4 additions & 47 deletions packages/form/src/components/TimeInputField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import { TimeInput } from '@ultraviolet/ui'
import type { ComponentProps } from 'react'
import { useController } from 'react-hook-form'
import type { FieldPath, FieldValues, Path, PathValue } from 'react-hook-form'
import { useErrors } from '../../providers'
import type { FieldPath, FieldValues } from 'react-hook-form'
import { useField } from '../../hooks/useField'
import type { BaseFieldProps } from '../../types'

type TimeInputFieldProps<TFieldValues extends FieldValues, TFieldName extends FieldPath<TFieldValues>> = BaseFieldProps<
Expand All @@ -21,54 +20,12 @@ export const TimeInputField = <
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
onChange,
label,
required = false,
name,
onBlur,
'aria-label': ariaLabel,
shouldUnregister,
control,
errorLabel,
...props
}: TimeInputFieldProps<TFieldValues, TFieldName>) => {
const { getError } = useErrors()
const { fieldProps } = useField(props)

const {
field,
fieldState: { error },
} = useController<TFieldValues, TFieldName>({
control,
name,
rules: {
required,
},
shouldUnregister,
})

return (
<TimeInput
{...props}
error={getError(
{
label: errorLabel ?? label ?? ariaLabel ?? name,
value: field.value,
},
error,
)}
label={label}
onBlur={event => {
onBlur?.(event)
field.onBlur()
}}
onChange={value => {
field.onChange(value)
onChange?.(value as PathValue<TFieldValues, Path<TFieldValues>>)
}}
required={required}
value={field.value}
/>
)
return <TimeInput {...props} {...fieldProps} />
}

TimeInputField.displayName = 'TimeInputField'
2 changes: 1 addition & 1 deletion packages/form/src/hooks/useField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useRegisterField } from './useRegisterField'
export type UseFieldProps<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = BaseFieldProps<TFieldValues, TFieldName> & Omit<MetaField, 'label'> & { 'aria-label'?: string }
> = BaseFieldProps<TFieldValues, TFieldName> & MetaField

export type FieldProps<
TFieldValues extends FieldValues,
Expand Down
23 changes: 4 additions & 19 deletions packages/form/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FocusEvent, ReactNode } from 'react'
import type { BaseFormComponentProps } from '@ultraviolet/ui'
import type { FocusEvent } from 'react'
import type {
Control,
FieldError,
Expand All @@ -23,7 +24,7 @@ export type MetaField = {
regex?: (RegExp | RegExp[])[]
minDate?: Date
maxDate?: Date
label: string
label?: string
value?: string | number
}

Expand All @@ -48,12 +49,10 @@ export type FormErrors = {
export type BaseFieldProps<
TFieldValues extends FieldValues = FieldValues,
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
> = BaseFormComponentProps & {
name: TFieldName
required?: boolean
validate?: Record<string, Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>>
defaultValue?: PathValue<TFieldValues, Path<TFieldValues>>
label?: string
value?: PathValue<TFieldValues, Path<TFieldValues>>
onChange?: (value?: PathValue<TFieldValues, TFieldName>) => void
onBlur?: (event?: FocusEvent) => void
Expand All @@ -62,20 +61,6 @@ export type BaseFieldProps<
errorLabel?: string
}

/**
* Classic prop type where label is a ReactNode and aria-label is a string.
* One or another is required.
*/
export type LabelProp =
| {
label: ReactNode
'aria-label'?: never
}
| {
label?: never
'aria-label': string
}

export type PascalToCamelCase<S extends string> = S extends `${infer P1}${infer P2}` ? `${Lowercase<P1>}${P2}` : S

export type RemoveSuffix<S extends string, Suffix extends string> = S extends `${infer Prefix}${Suffix}` ? Prefix : S
Expand Down
63 changes: 26 additions & 37 deletions packages/ui/src/components/Checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,39 @@ import { cn } from '@ultraviolet/utils'
import { forwardRef, useId } from 'react'
import type { InputHTMLAttributes, ReactNode } from 'react'
import { hasHelperText } from '../../helpers/hasHelperText'
import type { BaseFormComponentProps, CheckboxLabelProp } from '../../types'
import { Description } from '../Description'
import { Stack } from '../Stack'
import { Text } from '../Text'
import { Tooltip } from '../Tooltip'
import { CheckboxIconContainer } from './CheckboxIconContainer'
import { checkboxStyle } from './styles.css'

type LabelProp =
| {
children: ReactNode
'aria-label'?: never
}
| {
children?: never
'aria-label': string
}

type CheckboxProps = {
error?: ReactNode
helper?: ReactNode
disabled?: boolean
checked?: boolean | 'indeterminate'
className?: string
['data-visibility']?: string
required?: boolean
'data-testid'?: string
tooltip?: string
size?: 'default' | 'small'
} & Pick<
InputHTMLAttributes<HTMLInputElement>,
| 'autoFocus'
| 'id'
| 'name'
| 'onBlur'
| 'onChange'
| 'onKeyDown'
| 'onClick'
| 'onFocus'
| 'tabIndex'
| 'value'
| 'style'
| 'aria-describedby'
> &
LabelProp
type CheckboxProps = BaseFormComponentProps &
CheckboxLabelProp & {
error?: ReactNode
helper?: ReactNode
checked?: boolean | 'indeterminate'
className?: string
['data-visibility']?: string
'data-testid'?: string
tooltip?: string
size?: 'default' | 'small'
} & Pick<
InputHTMLAttributes<HTMLInputElement>,
| 'autoFocus'
| 'id'
| 'name'
| 'onBlur'
| 'onChange'
| 'onKeyDown'
| 'onClick'
| 'onFocus'
| 'tabIndex'
| 'value'
| 'style'
| 'aria-describedby'
>

/**
* Checkbox is an input component used to select or deselect an option.
Expand Down
22 changes: 5 additions & 17 deletions packages/ui/src/components/NumberInput/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { RefObject, InputHTMLAttributes, ReactNode } from 'react'
import type { SIZES } from './constant'
type Sizes = keyof typeof SIZES
import type { BaseFormComponentProps } from '../../types'

export type NumberInputProps = {
export type NumberInputProps = BaseFormComponentProps & {
size?: Sizes
/**
* Text displayed into component at the right of number value.
Expand All @@ -11,7 +12,6 @@ export type NumberInputProps = {
tooltip?: string
className?: string
'data-testid'?: string
label?: string
/**
* Label description displayed right next to the label. It allows you to customize the label content.
*/
Expand All @@ -28,21 +28,9 @@ export type NumberInputProps = {
min?: number
max?: number
} & Pick<
InputHTMLAttributes<HTMLInputElement>,
| 'onFocus'
| 'onBlur'
| 'name'
| 'id'
| 'placeholder'
| 'aria-label'
| 'disabled'
| 'step'
| 'readOnly'
| 'required'
| 'autoFocus'
| 'style'
| 'aria-describedby'
>
InputHTMLAttributes<HTMLInputElement>,
'onFocus' | 'onBlur' | 'placeholder' | 'disabled' | 'step' | 'readOnly' | 'autoFocus' | 'style' | 'aria-describedby'
>

export type ControlsProps = {
controls?: NumberInputProps['controls']
Expand Down
Loading
Loading