Skip to content
Merged
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
4 changes: 2 additions & 2 deletions packages/react-aria-components/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ export interface ButtonRenderProps {
}

export interface ButtonProps extends Omit<AriaButtonProps, 'children' | 'href' | 'target' | 'rel' | 'elementType'>, SlotProps, RenderProps<ButtonRenderProps> {
/**
/**
* The <form> element to associate the button with.
* The value of this attribute must be the id of a <form> in the same document.
*/
form?: string,
/**
/**
* The URL that processes the information submitted by the button.
* Overrides the action attribute of the button's form owner.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react-aria-components/src/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ function CalendarCell({date, ...otherProps}: CalendarCellProps, ref: ForwardedRe

return (
<td {...cellProps}>
<div {...mergeProps(buttonProps, focusProps, hoverProps, dataAttrs, renderProps)} ref={objectRef} />
<div {...mergeProps(filterDOMProps(otherProps as any), buttonProps, focusProps, hoverProps, dataAttrs, renderProps)} ref={objectRef} />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The as any bothers me, but I couldn't come up with a good way to mark the props as only accepting data attributes since we've been relying on how TS doesn't consider a data-* attribute to be an error if it isn't found in the element attributes type. Also I've forgone the delete DOMProps.id when the props didn't share any values in common with the filterDOMProps type since we can reasonably expect only data-* attributes then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do cells not support an id at all? I guess I'm a little confused as to what the issue with id is in this whole situation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, we don't accept ids on the CalendarCell at all

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if they are only data-* and that is allowed on CalendarCellProps, you could create a type that would encompass it. maybe this would work?

type OnlyDataAttributes = { 
  [key: `data-${string}`]: unknown;
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saving progress in update_types_for_data_attributes branch, the type changes are a bit extensive for this

</td>
);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/react-aria-components/src/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import {AriaCheckboxGroupProps, AriaCheckboxProps, mergeProps, useCheckbox, useCheckboxGroup, useCheckboxGroupItem, useFocusRing, useHover, usePress, VisuallyHidden} from 'react-aria';
import {CheckboxGroupState, useCheckboxGroupState, useToggleState, ValidationState} from 'react-stately';
import {ContextValue, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
import {filterDOMProps} from '@react-aria/utils';
import {LabelContext} from './Label';
import React, {createContext, ForwardedRef, forwardRef, useContext, useState} from 'react';
import {TextContext} from './Text';
Expand Down Expand Up @@ -204,9 +205,12 @@ function Checkbox(props: CheckboxProps, ref: ForwardedRef<HTMLInputElement>) {
}
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

return (
<label
{...mergeProps(pressProps, hoverProps, renderProps)}
{...mergeProps(DOMProps, pressProps, hoverProps, renderProps)}
slot={props.slot}
data-selected={isSelected || undefined}
data-indeterminate={props.isIndeterminate || undefined}
Expand Down
16 changes: 11 additions & 5 deletions packages/react-aria-components/src/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import {AriaComboBoxProps, useComboBox, useFilter} from 'react-aria';
import {ButtonContext} from './Button';
import {ComboBoxState, useComboBoxState} from 'react-stately';
import {ContextValue, forwardRefType, Provider, RenderProps, slotCallbackSymbol, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
import {filterDOMProps, useResizeObserver} from '@react-aria/utils';
import {InputContext} from './Input';
import {LabelContext} from './Label';
import {ListBoxContext, ListBoxProps} from './ListBox';
import {PopoverContext} from './Popover';
import React, {createContext, ForwardedRef, forwardRef, useCallback, useRef, useState} from 'react';
import React, {createContext, ForwardedRef, forwardRef, useCallback, useMemo, useRef, useState} from 'react';
import {TextContext} from './Text';
import {useCollection} from './Collection';
import {useResizeObserver} from '@react-aria/utils';

export interface ComboBoxProps<T extends object> extends Omit<AriaComboBoxProps<T>, 'children' | 'placeholder' | 'name' | 'label' | 'description' | 'errorMessage'>, RenderProps<ComboBoxState<T>>, SlotProps {
/** The filter function used to determine if a option should be included in the combo box list. */
Expand All @@ -41,11 +41,14 @@ function ComboBox<T extends object>(props: ComboBoxProps<T>, ref: ForwardedRef<H
let state = useComboBoxState({
defaultFilter: props.defaultFilter || contains,
...props,
items: propsFromListBox ? (props.items ?? propsFromListBox.items) : [],
// If props.items isn't provided, rely on collection filtering (aka listbox.items is provided or defaultItems provided to Combobox)
items: props.items,
Comment on lines +44 to +45
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For discussion: should we make users perform filtering on their ComboBox's ListBox themselves if they provide items to the ListBox? Or is that a case where we perform default filtering for them?

See ComboBoxRenderPropsListBoxDynamic story for an example setup

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets discuss these things after grooming tomorrow

children: undefined,
collection
});

// Only expose a subset of state to renderProps function to avoid infinite render loop
let renderPropsState = useMemo(() => ({isOpen: state.isOpen, isFocused: state.isFocused}), [state.isOpen, state.isFocused]);
let buttonRef = useRef<HTMLButtonElement>(null);
let inputRef = useRef<HTMLInputElement>(null);
let listBoxRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -87,10 +90,13 @@ function ComboBox<T extends object>(props: ComboBoxProps<T>, ref: ForwardedRef<H

let renderProps = useRenderProps({
...props,
values: state,
values: renderPropsState as ComboBoxState<T>,
defaultClassName: 'react-aria-ComboBox'
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

return (
<Provider
values={[
Expand All @@ -114,7 +120,7 @@ function ComboBox<T extends object>(props: ComboBoxProps<T>, ref: ForwardedRef<H
}
}]
]}>
<div {...renderProps} ref={ref} slot={props.slot} />
<div {...DOMProps} {...renderProps} ref={ref} slot={props.slot} />
{portal}
</Provider>
);
Expand Down
16 changes: 11 additions & 5 deletions packages/react-aria-components/src/DateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import {AriaDateFieldProps, AriaTimeFieldProps, DateValue, mergeProps, TimeValue
import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps, useSlot} from './utils';
import {createCalendar} from '@internationalized/date';
import {DateFieldState, DateSegmentType, DateSegment as IDateSegment, useDateFieldState, useTimeFieldState} from 'react-stately';
import {filterDOMProps, useObjectRef} from '@react-aria/utils';
import {LabelContext} from './Label';
import React, {cloneElement, createContext, ForwardedRef, forwardRef, HTMLAttributes, ReactElement, useContext, useRef} from 'react';
import {TextContext} from './Text';
import {useObjectRef} from '@react-aria/utils';

export interface DateFieldProps<T extends DateValue> extends Omit<AriaDateFieldProps<T>, 'label' | 'description' | 'errorMessage'>, RenderProps<DateFieldState>, SlotProps {}
export interface TimeFieldProps<T extends TimeValue> extends Omit<AriaTimeFieldProps<T>, 'label' | 'description' | 'errorMessage'>, RenderProps<DateFieldState>, SlotProps {}
Expand Down Expand Up @@ -49,6 +49,9 @@ function DateField<T extends DateValue>(props: DateFieldProps<T>, ref: Forwarded
defaultClassName: 'react-aria-DateField'
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

return (
<Provider
values={[
Expand All @@ -61,7 +64,7 @@ function DateField<T extends DateValue>(props: DateFieldProps<T>, ref: Forwarded
}
}]
]}>
<div {...renderProps} ref={ref} slot={props.slot} />
<div {...DOMProps} {...renderProps} ref={ref} slot={props.slot} />
</Provider>
);
}
Expand Down Expand Up @@ -91,6 +94,9 @@ function TimeField<T extends TimeValue>(props: TimeFieldProps<T>, ref: Forwarded
defaultClassName: 'react-aria-TimeField'
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

return (
<Provider
values={[
Expand All @@ -103,7 +109,7 @@ function TimeField<T extends TimeValue>(props: TimeFieldProps<T>, ref: Forwarded
}
}]
]}>
<div {...renderProps} ref={ref} slot={props.slot} />
<div {...DOMProps} {...renderProps} ref={ref} slot={props.slot} />
</Provider>
);
}
Expand Down Expand Up @@ -162,7 +168,7 @@ function DateInput({children, slot, ...otherProps}: DateInputProps, ref: Forward
return (
<InternalDateInputContext.Provider value={state}>
<div
{...mergeProps(fieldProps, focusProps, hoverProps)}
{...mergeProps(filterDOMProps(otherProps as any), fieldProps, focusProps, hoverProps)}
{...renderProps}
ref={fieldRef}
slot={slot}
Expand Down Expand Up @@ -225,7 +231,7 @@ function DateSegment({segment, ...otherProps}: DateSegmentProps, ref: ForwardedR

return (
<div
{...segmentProps}
{...mergeProps(filterDOMProps(otherProps as any), segmentProps)}
{...renderProps}
ref={domRef}
data-type={segment.type} />
Expand Down
11 changes: 9 additions & 2 deletions packages/react-aria-components/src/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {createCalendar} from '@internationalized/date';
import {DateInputContext} from './DateField';
import {DatePickerState, DateRangePickerState, useDateFieldState, useDatePickerState, useDateRangePickerState} from 'react-stately';
import {DialogContext} from './Dialog';
import {filterDOMProps} from '@react-aria/utils';
import {GroupContext} from './Group';
import {LabelContext} from './Label';
import {PopoverContext} from './Popover';
Expand Down Expand Up @@ -61,6 +62,9 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
defaultClassName: 'react-aria-DatePicker'
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

return (
<Provider
values={[
Expand All @@ -78,7 +82,7 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
}
}]
]}>
<div {...renderProps} ref={ref} slot={props.slot} />
<div {...DOMProps} {...renderProps} ref={ref} slot={props.slot} />
</Provider>
);
}
Expand Down Expand Up @@ -131,6 +135,9 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
defaultClassName: 'react-aria-DateRangePicker'
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

return (
<Provider
values={[
Expand Down Expand Up @@ -161,7 +168,7 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
}
}]
]}>
<div {...renderProps} ref={ref} slot={props.slot} />
<div {...DOMProps} {...renderProps} ref={ref} slot={props.slot} />
</Provider>
);
}
Expand Down
33 changes: 26 additions & 7 deletions packages/react-aria-components/src/GridList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ function GridList<T extends object>(props: GridListProps<T>, ref: ForwardedRef<H
<Provider
values={[
[InternalGridListContext, {state, dragAndDropHooks, dragState, dropState}],
[DropIndicatorContext, {render: GridListDropIndicator}]
[DropIndicatorContext, {render: GridListDropIndicatorWrapper}]
]}>
{isListDroppable && <RootDropIndicator />}
{children}
Expand Down Expand Up @@ -308,7 +308,7 @@ function GridListItem({item}) {
</div>
}
<div
{...mergeProps(rowProps, focusProps, hoverProps, draggableItem?.dragProps)}
{...mergeProps(filterDOMProps(props as any), rowProps, focusProps, hoverProps, draggableItem?.dragProps)}
{...renderProps}
ref={ref}
data-hovered={isHovered || undefined}
Expand Down Expand Up @@ -350,7 +350,7 @@ function GridListItem({item}) {
);
}

function GridListDropIndicator(props: DropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
function GridListDropIndicatorWrapper(props: DropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
ref = useObjectRef(ref);
let {dragAndDropHooks, dropState} = useContext(InternalGridListContext)!;
let buttonRef = useRef<HTMLDivElement>(null);
Expand All @@ -360,15 +360,32 @@ function GridListDropIndicator(props: DropIndicatorProps, ref: ForwardedRef<HTML
buttonRef
);

let {visuallyHiddenProps} = useVisuallyHidden();

if (isHidden) {
return null;
}

// eslint-disable-next-line react-hooks/rules-of-hooks
return (
<GridListDropIndicatorForwardRef {...props} dropIndicatorProps={dropIndicatorProps} isDropTarget={isDropTarget} buttonRef={buttonRef} ref={ref} />
);
}

interface GridListDropIndicatorProps extends DropIndicatorProps {
dropIndicatorProps: React.HTMLAttributes<HTMLElement>,
isDropTarget: boolean,
buttonRef: RefObject<HTMLDivElement>
}

function GridListDropIndicator(props: GridListDropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
let {
dropIndicatorProps,
isDropTarget,
buttonRef,
...otherProps
} = props;

let {visuallyHiddenProps} = useVisuallyHidden();
let renderProps = useRenderProps({
...props,
...otherProps,
defaultClassName: 'react-aria-DropIndicator',
values: {
isDropTarget
Expand All @@ -388,6 +405,8 @@ function GridListDropIndicator(props: DropIndicatorProps, ref: ForwardedRef<HTML
);
}

const GridListDropIndicatorForwardRef = forwardRef(GridListDropIndicator);

function RootDropIndicator() {
let {dragAndDropHooks, dropState} = useContext(InternalGridListContext)!;
let ref = useRef<HTMLDivElement>(null);
Expand Down
7 changes: 5 additions & 2 deletions packages/react-aria-components/src/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import {AriaLinkOptions, mergeProps, useFocusRing, useHover, useLink} from 'react-aria';
import {ContextValue, RenderProps, SlotProps, useContextProps, useRenderProps} from './utils';
import {mergeRefs} from '@react-aria/utils';
import {filterDOMProps, mergeRefs} from '@react-aria/utils';
import React, {createContext, ForwardedRef, forwardRef, useMemo} from 'react';

export interface LinkProps extends Omit<AriaLinkOptions, 'elementType'>, RenderProps<LinkRenderProps>, SlotProps {}
Expand Down Expand Up @@ -74,14 +74,17 @@ function Link(props: LinkProps, ref: ForwardedRef<HTMLAnchorElement>) {
}
});

let DOMProps = filterDOMProps(props);
delete DOMProps.id;

let element: any = typeof renderProps.children === 'string'
? <span>{renderProps.children}</span>
: React.Children.only(renderProps.children);

return React.cloneElement(element, {
ref: useMemo(() => element.ref ? mergeRefs(element.ref, ref) : ref, [element.ref, ref]),
slot: props.slot,
...mergeProps(renderProps, linkProps, hoverProps, focusProps, {
...mergeProps(DOMProps, renderProps, linkProps, hoverProps, focusProps, {
children: element.props.children,
'data-hovered': isHovered || undefined,
'data-pressed': isPressed || undefined,
Expand Down
28 changes: 23 additions & 5 deletions packages/react-aria-components/src/ListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ function ListBoxInner<T>({state, props, listBoxRef}: ListBoxInnerProps<T>) {
values={[
[InternalListBoxContext, {state, shouldFocusOnHover: props.shouldFocusOnHover, dragAndDropHooks, dragState, dropState}],
[SeparatorContext, {elementType: 'li'}],
[DropIndicatorContext, {render: ListBoxDropIndicator}]
[DropIndicatorContext, {render: ListBoxDropIndicatorWrapper}]
]}>
{children}
</Provider>
Expand Down Expand Up @@ -344,7 +344,7 @@ function Option<T>({item}: OptionProps<T>) {
renderDropIndicator({type: 'item', key: item.key, dropPosition: 'before'})
}
<div
{...mergeProps(optionProps, hoverProps, draggableItem?.dragProps, droppableItem?.dropProps)}
{...mergeProps(filterDOMProps(props as any), optionProps, hoverProps, draggableItem?.dragProps, droppableItem?.dropProps)}
{...renderProps}
ref={ref}
data-hovered={isHovered || undefined}
Expand Down Expand Up @@ -372,7 +372,7 @@ function Option<T>({item}: OptionProps<T>) {
);
}

function ListBoxDropIndicator(props: DropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
function ListBoxDropIndicatorWrapper(props: DropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
ref = useObjectRef(ref);
let {dragAndDropHooks, dropState} = useContext(InternalListBoxContext)!;
let {dropIndicatorProps, isHidden, isDropTarget} = dragAndDropHooks!.useDropIndicator!(
Expand All @@ -385,9 +385,25 @@ function ListBoxDropIndicator(props: DropIndicatorProps, ref: ForwardedRef<HTMLE
return null;
}

// eslint-disable-next-line react-hooks/rules-of-hooks
return (
<ListBoxtDropIndicatorForwardRef {...props} dropIndicatorProps={dropIndicatorProps} isDropTarget={isDropTarget} ref={ref} />
);
}

interface ListBoxDropIndicatorProps extends DropIndicatorProps {
dropIndicatorProps: React.HTMLAttributes<HTMLElement>,
isDropTarget: boolean
}

function ListBoxtDropIndicator(props: ListBoxDropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
let {
dropIndicatorProps,
isDropTarget,
...otherProps
} = props;

let renderProps = useRenderProps({
...props,
...otherProps,
defaultClassName: 'react-aria-DropIndicator',
values: {
isDropTarget
Expand All @@ -404,3 +420,5 @@ function ListBoxDropIndicator(props: DropIndicatorProps, ref: ForwardedRef<HTMLE
data-drop-target={isDropTarget || undefined} />
);
}

const ListBoxtDropIndicatorForwardRef = forwardRef(ListBoxtDropIndicator);
5 changes: 4 additions & 1 deletion packages/react-aria-components/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,12 @@ function MenuItem<T>({item}: MenuItemProps<T>) {
}
});

let DOMProps = filterDOMProps(props as any);
delete DOMProps.id;

return (
<div
{...mergeProps(menuItemProps, focusProps)}
{...mergeProps(DOMProps, menuItemProps, focusProps)}
{...renderProps}
ref={ref}
data-hovered={states.isFocused || undefined}
Expand Down
Loading