Skip to content

Commit

Permalink
Expose missing data-disabled and data-focus attributes on the `Ta…
Browse files Browse the repository at this point in the history
…bsPanel`, `MenuButton`, `PopoverButton` and `DisclosureButton` components (#3061)

* expose `data-focus` on the `TabsPanel` component

* expose `data-disabled` on the `MenuButton` component

* expose `data-disabled` on the `PopoverButton` component

* expose `data-disabled` on the `DisclosureButton` component

* cleanup repetition

* update changelog

* add `satisfies` statements to ensure all data is present

* update changelog entry
  • Loading branch information
RobinMalfait committed Mar 27, 2024
1 parent bf4dc77 commit 6d44a8d
Show file tree
Hide file tree
Showing 20 changed files with 349 additions and 334 deletions.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Prefer incoming `data-*` attributes, over the ones set by Headless UI ([#3035](https://github.com/tailwindlabs/headlessui/pull/3035))
- Respect `selectedIndex` for controlled `<Tab/>` components ([#3037](https://github.com/tailwindlabs/headlessui/pull/3037))
- Prevent unnecessary execution of the `displayValue` callback in the `ComboboxInput` component ([#3048](https://github.com/tailwindlabs/headlessui/pull/3048))
- Expose missing `data-disabled` and `data-focus` attributes on the `TabsPanel`, `MenuButton`, `PopoverButton` and `DisclosureButton` components ([#3061](https://github.com/tailwindlabs/headlessui/pull/3061))

### Changed

Expand Down
21 changes: 7 additions & 14 deletions packages/@headlessui-react/src/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,34 +41,27 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
ref: Ref<HTMLElement>
) {
let providedDisabled = useDisabled()
let { disabled = providedDisabled || false, ...theirProps } = props
let { disabled = providedDisabled || false, autoFocus = false, ...theirProps } = props

let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus: props.autoFocus ?? false })
let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })
let { pressed: active, pressProps } = useActivePress({ disabled })

let ourProps = mergeProps(
{
ref,
disabled: disabled || undefined,
type: theirProps.type ?? 'button',
disabled: disabled || undefined,
autoFocus,
},
focusProps,
hoverProps,
pressProps
)

let slot = useMemo(
() =>
({
disabled,
hover,
focus,
active,
autofocus: props.autoFocus ?? false,
}) satisfies ButtonRenderPropArg,
[disabled, hover, focus, active, props.autoFocus]
)
let slot = useMemo(() => {
return { disabled, hover, focus, active, autofocus: autoFocus } satisfies ButtonRenderPropArg
}, [disabled, hover, focus, active, autoFocus])

return render({
ourProps,
Expand Down
33 changes: 16 additions & 17 deletions packages/@headlessui-react/src/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
let {
id = providedId || `headlessui-checkbox-${internalId}`,
disabled = providedDisabled || false,
autoFocus = false,
checked: controlledChecked,
defaultChecked = false,
onChange: controlledOnChange,
Expand Down Expand Up @@ -127,9 +128,9 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
// This is needed so that we can "cancel" the click event when we use the `Enter` key on a button.
let handleKeyPress = useEvent((event: ReactKeyboardEvent<HTMLElement>) => event.preventDefault())

let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus: props.autoFocus ?? false })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled ?? false })
let { pressed: active, pressProps } = useActivePress({ disabled: disabled ?? false })
let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })
let { pressed: active, pressProps } = useActivePress({ disabled })

let ourProps = mergeProps(
{
Expand All @@ -151,20 +152,18 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
pressProps
)

let slot = useMemo(
() =>
({
checked,
disabled,
hover,
focus,
active,
indeterminate,
changing,
autofocus: props.autoFocus ?? false,
}) satisfies CheckboxRenderPropArg,
[checked, indeterminate, disabled, hover, focus, active, changing, props.autoFocus]
)
let slot = useMemo(() => {
return {
checked,
disabled,
hover,
focus,
active,
indeterminate,
changing,
autofocus: autoFocus,
} satisfies CheckboxRenderPropArg
}, [checked, indeterminate, disabled, hover, focus, active, changing, autoFocus])

let reset = useCallback(() => {
return onChange?.(defaultChecked)
Expand Down
118 changes: 61 additions & 57 deletions packages/@headlessui-react/src/components/combobox/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -770,22 +770,20 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
data.comboboxState === ComboboxState.Open
)

let slot = useMemo(
() =>
({
open: data.comboboxState === ComboboxState.Open,
disabled,
activeIndex: data.activeOptionIndex,
activeOption:
data.activeOptionIndex === null
? null
: data.virtual
? data.virtual.options[data.activeOptionIndex ?? 0]
: (data.options[data.activeOptionIndex]?.dataRef.current.value as TValue) ?? null,
value,
}) satisfies ComboboxRenderPropArg<unknown>,
[data, disabled, value]
)
let slot = useMemo(() => {
return {
open: data.comboboxState === ComboboxState.Open,
disabled,
activeIndex: data.activeOptionIndex,
activeOption:
data.activeOptionIndex === null
? null
: data.virtual
? data.virtual.options[data.activeOptionIndex ?? 0]
: (data.options[data.activeOptionIndex]?.dataRef.current.value as TValue) ?? null,
value,
} satisfies ComboboxRenderPropArg<unknown>
}, [data, disabled, value])

let selectActiveOption = useEvent(() => {
if (data.activeOptionIndex === null) return
Expand Down Expand Up @@ -958,6 +956,7 @@ export type ComboboxInputProps<
InputPropsWeControl,
{
defaultValue?: TType
disabled?: boolean
displayValue?(item: TType): string
onChange?(event: React.ChangeEvent<HTMLInputElement>): void
autoFocus?: boolean
Expand All @@ -970,18 +969,21 @@ function InputFn<
// But today is not that day..
TType = Parameters<typeof ComboboxRoot>[0]['value'],
>(props: ComboboxInputProps<TTag, TType>, ref: Ref<HTMLInputElement>) {
let data = useData('Combobox.Input')
let actions = useActions('Combobox.Input')

let internalId = useId()
let providedId = useProvidedId()
let {
id = providedId || `headlessui-combobox-input-${internalId}`,
onChange,
displayValue,
disabled = data.disabled || false,
autoFocus = false,
// @ts-ignore: We know this MAY NOT exist for a given tag but we only care when it _does_ exist.
type = 'text',
...theirProps
} = props
let data = useData('Combobox.Input')
let actions = useActions('Combobox.Input')

let inputRef = useSyncRefs(data.inputRef, ref, useFloatingReference())
let ownerDocument = useOwnerDocument(data.inputRef)
Expand Down Expand Up @@ -1320,20 +1322,18 @@ function InputFn<
let labelledBy = useLabelledBy()
let describedBy = useDescribedBy()

let { isFocused: focus, focusProps } = useFocusRing({ autoFocus: props.autoFocus ?? false })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: data.disabled ?? false })
let { isFocused: focus, focusProps } = useFocusRing({ autoFocus })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })

let slot = useMemo(
() =>
({
open: data.comboboxState === ComboboxState.Open,
disabled: data.disabled,
hover,
focus,
autofocus: props.autoFocus ?? false,
}) satisfies InputRenderPropArg,
[data, hover, focus, props.autoFocus]
)
let slot = useMemo(() => {
return {
open: data.comboboxState === ComboboxState.Open,
disabled,
hover,
focus,
autofocus: autoFocus,
} satisfies InputRenderPropArg
}, [data, hover, focus, autoFocus, disabled])

let ourProps = mergeProps(
{
Expand Down Expand Up @@ -1365,7 +1365,8 @@ function InputFn<
? displayValue?.(data.defaultValue as unknown as TType)
: null) ??
data.defaultValue,
disabled: data.disabled,
disabled: disabled || undefined,
autoFocus,
onCompositionStart: handleCompositionStart,
onCompositionEnd: handleCompositionEnd,
onKeyDown: handleKeyDown,
Expand Down Expand Up @@ -1411,6 +1412,7 @@ export type ComboboxButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON
ButtonPropsWeControl,
{
autoFocus?: boolean
disabled?: boolean
}
>

Expand All @@ -1422,7 +1424,12 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
let actions = useActions('Combobox.Button')
let buttonRef = useSyncRefs(data.buttonRef, ref)
let internalId = useId()
let { id = `headlessui-combobox-button-${internalId}`, ...theirProps } = props
let {
id = `headlessui-combobox-button-${internalId}`,
disabled = data.disabled || false,
autoFocus = false,
...theirProps
} = props
let d = useDisposables()

let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLUListElement>) => {
Expand Down Expand Up @@ -1479,22 +1486,20 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(

let labelledBy = useLabelledBy([id])

let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus: props.autoFocus ?? false })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: data.disabled ?? false })
let { pressed: active, pressProps } = useActivePress({ disabled: data.disabled ?? false })
let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })
let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })
let { pressed: active, pressProps } = useActivePress({ disabled })

let slot = useMemo(
() =>
({
open: data.comboboxState === ComboboxState.Open,
active: active || data.comboboxState === ComboboxState.Open,
disabled: data.disabled,
value: data.value,
hover,
focus,
}) satisfies ButtonRenderPropArg,
[data, hover, focus, active]
)
let slot = useMemo(() => {
return {
open: data.comboboxState === ComboboxState.Open,
active: active || data.comboboxState === ComboboxState.Open,
disabled,
value: data.value,
hover,
focus,
} satisfies ButtonRenderPropArg
}, [data, hover, focus, active, disabled])
let ourProps = mergeProps(
{
ref: buttonRef,
Expand All @@ -1505,7 +1510,8 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
'aria-controls': data.optionsRef.current?.id,
'aria-expanded': data.comboboxState === ComboboxState.Open,
'aria-labelledby': labelledBy,
disabled: data.disabled,
disabled: disabled || undefined,
autoFocus,
onClick: handleClick,
onKeyDown: handleKeyDown,
},
Expand Down Expand Up @@ -1592,14 +1598,12 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(

let labelledBy = useLabelledBy([data.buttonRef.current?.id])

let slot = useMemo(
() =>
({
open: data.comboboxState === ComboboxState.Open,
option: undefined,
}) satisfies OptionsRenderPropArg,
[data]
)
let slot = useMemo(() => {
return {
open: data.comboboxState === ComboboxState.Open,
option: undefined,
} satisfies OptionsRenderPropArg
}, [data])
let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {
'aria-labelledby': labelledBy,
role: 'listbox',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ describe('Rendering', () => {
open: false,
hover: false,
active: false,
disabled: false,
focus: false,
autofocus: false,
}),
Expand All @@ -283,6 +284,7 @@ describe('Rendering', () => {
open: true,
hover: false,
active: false,
disabled: false,
focus: false,
autofocus: false,
}),
Expand Down Expand Up @@ -310,6 +312,7 @@ describe('Rendering', () => {
open: false,
hover: false,
active: false,
disabled: false,
focus: false,
autofocus: false,
}),
Expand All @@ -325,6 +328,7 @@ describe('Rendering', () => {
open: true,
hover: false,
active: false,
disabled: false,
focus: false,
autofocus: false,
}),
Expand Down
Loading

0 comments on commit 6d44a8d

Please sign in to comment.