Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion packages/@react-aria/calendar/src/useCalendarCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
: state.value && isSameDay(state.value, date)
);

if (isInvalid) {
if (isInvalid && !isUnavailable) {
isSelected = true;
}

Expand Down
72 changes: 68 additions & 4 deletions packages/@react-spectrum/s2/chromatic/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,34 @@ let states = [

let combinations = generatePowerset(states);

const Template = (args: ButtonProps): ReactNode => {
let {children, ...otherArgs} = args;
let premiumStates = [
{isDisabled: true},
{size: ['S', 'M', 'L', 'XL']}
];

let premiumCombinations = generatePowerset(premiumStates);

let genaiStates = [
{isDisabled: true},
{size: ['S', 'M', 'L', 'XL']}
];

let genaiCombinations = generatePowerset(genaiStates);

const Template = (args: ButtonProps & {combos?: any[]}): ReactNode => {
let {children, combos = combinations, variant, ...otherArgs} = args;
return (
<div className={style({display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 250px))', gridAutoFlow: 'row', alignItems: 'center', justifyItems: 'start', gap: 24, width: '100vw'})}>
{combinations.map(c => {
{combos.map(c => {
let fullComboName = Object.keys(c).map(k => `${k}: ${c[k]}`).join(' ');
let key = Object.keys(c).map(k => shortName(k, c[k])).join(' ');
if (!key) {
key = 'default';
}

let button = <Button data-testid={fullComboName} key={key} {...otherArgs} {...c}>{children ? children : key}</Button>;
let finalVariant = c.variant ?? variant;
let buttonProps = {...otherArgs, ...c, ...(finalVariant && {variant: finalVariant})};
let button = <Button data-testid={fullComboName} key={key} {...buttonProps}>{children ? children : key}</Button>;
if (c.staticColor != null) {
return (
<StaticColorProvider staticColor={c.staticColor}>
Expand Down Expand Up @@ -84,4 +100,52 @@ export const IconOnly: StoryObj<typeof Button> = {
}
};

export const Premium: StoryObj<typeof Button> = {
render: (args) => <Template {...args} combos={premiumCombinations} />,
args: {
children: 'Press me',
variant: 'premium'
}
};

export const PremiumWithIcon: StoryObj<typeof Button> = {
render: (args) => <Template {...args} combos={premiumCombinations} />,
args: {
children: <><NewIcon /><Text>Press me</Text></>,
variant: 'premium'
}
};

export const PremiumIconOnly: StoryObj<typeof Button> = {
render: (args) => <Template {...args} combos={premiumCombinations} />,
args: {
children: <NewIcon />,
variant: 'premium'
}
};

export const GenAI: StoryObj<typeof Button> = {
render: (args) => <Template {...args} combos={genaiCombinations} />,
args: {
children: 'Press me',
variant: 'genai'
}
};

export const GenAIWithIcon: StoryObj<typeof Button> = {
render: (args) => <Template {...args} combos={genaiCombinations} />,
args: {
children: <><NewIcon /><Text>Press me</Text></>,
variant: 'genai'
}
};

export const GenAIIconOnly: StoryObj<typeof Button> = {
render: (args) => <Template {...args} combos={genaiCombinations} />,
args: {
children: <NewIcon />,
variant: 'genai'
}
};

export {WithWrapping};
16 changes: 16 additions & 0 deletions packages/@react-spectrum/s2/chromatic/LinkButton.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,19 @@ export const Example: Story = {
target: '_blank'
}
};

export const Premium: Story = {
...Example,
args: {
...Example.args,
variant: 'premium'
}
};

export const GenAI: Story = {
...Example,
args: {
...Example.args,
variant: 'genai'
}
};
56 changes: 54 additions & 2 deletions packages/@react-spectrum/s2/chromatic/SelectBoxGroup.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,30 @@ export const VerticalOrientation: Story = {
<div style={{width: 600}}>
<SelectBoxGroup
aria-label="Vertical"
orientation="vertical"
orientation="vertical"
onSelectionChange={action('onSelectionChange')}>
<SelectBox id="text-only" textValue="V: Text Only">
<Text slot="label">V: Text Only</Text>
</SelectBox>
<SelectBox id="illustration-text" textValue="V: Illustration + Text">
<Server />
<Text slot="label">V: Illustration + Text</Text>
</SelectBox>
<SelectBox id="illustration-desc" textValue="Send">
<PaperAirplane />
</SelectBox>
</SelectBoxGroup>
</div>
)
};

export const VerticalOrientationMultiSelect: Story = {
render: () => (
<div style={{width: 600}}>
<SelectBoxGroup
selectionMode="multiple"
aria-label="Vertical"
orientation="vertical"
onSelectionChange={action('onSelectionChange')}>
<SelectBox id="text-only" textValue="V: Text Only">
<Text slot="label">V: Text Only</Text>
Expand All @@ -55,7 +78,36 @@ export const HorizontalOrientation: Story = {
<div style={{width: 800}}>
<SelectBoxGroup
aria-label="Horizontal"
orientation="horizontal"
orientation="horizontal"
onSelectionChange={action('onSelectionChange')}>
<SelectBox id="text-only" textValue="Title Only">
<Text slot="label">Title Only</Text>
</SelectBox>
<SelectBox id="illustration-text" textValue="Illustration + Title">
<Server />
<Text slot="label">Illustration + Title</Text>
</SelectBox>
<SelectBox id="text-desc" textValue="Title + Description">
<Text slot="label">Title + Description</Text>
<Text slot="description">Additional description</Text>
</SelectBox>
<SelectBox id="h-all" textValue="Illustration + Title + Description">
<Server />
<Text slot="label">Illustration + Title + Description</Text>
<Text slot="description">Full horizontal layout with all elements</Text>
</SelectBox>
</SelectBoxGroup>
</div>
)
};

export const HorizontalOrientationMultSelect: Story = {
render: () => (
<div style={{width: 800}}>
<SelectBoxGroup
selectionMode="multiple"
aria-label="Horizontal"
orientation="horizontal"
onSelectionChange={action('onSelectionChange')}>
<SelectBox id="text-only" textValue="Title Only">
<Text slot="label">Title Only</Text>
Expand Down
63 changes: 42 additions & 21 deletions packages/@react-spectrum/s2/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,14 @@ export const LinkButton = forwardRef(function LinkButton(props: LinkButtonProps,
props = useFormProps(props);
let domRef = useFocusableRef(ref);
let overlayTriggerState = useContext(OverlayTriggerStateContext);
let {
fillStyle = 'fill',
size = 'M',
variant = 'primary',
staticColor,
styles,
children
} = props;

return (
<Link
Expand All @@ -440,28 +448,41 @@ export const LinkButton = forwardRef(function LinkButton(props: LinkButtonProps,
...renderProps,
// Retain hover styles when an overlay is open.
isHovered: renderProps.isHovered || overlayTriggerState?.isOpen || false,
variant: props.variant || 'primary',
fillStyle: props.fillStyle || 'fill',
size: props.size || 'M',
staticColor: props.staticColor,
isStaticColor: !!props.staticColor,
variant,
fillStyle,
size,
staticColor,
isStaticColor: !!staticColor,
isPending: false
}, props.styles)}>
<Provider
values={[
[SkeletonContext, null],
[TextContext, {
styles: style({paddingY: '--labelPadding', order: 1}),
// @ts-ignore data-attributes allowed on all JSX elements, but adding to DOMProps has been problematic in the past
'data-rsp-slot': 'text'
}],
[IconContext, {
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
}]
]}>
{typeof props.children === 'string' ? <Text>{props.children}</Text> : props.children}
</Provider>
}, styles)}>
{(renderProps) => (<>
{variant === 'genai' || variant === 'premium'
? (
<span
className={gradient({
...renderProps,
// Retain hover styles when an overlay is open.
isHovered: renderProps.isHovered || overlayTriggerState?.isOpen || false,
variant
})} />
)
: null}
<Provider
values={[
[SkeletonContext, null],
[TextContext, {
styles: style({paddingY: '--labelPadding', order: 1}),
// @ts-ignore data-attributes allowed on all JSX elements, but adding to DOMProps has been problematic in the past
'data-rsp-slot': 'text'
}],
[IconContext, {
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
}]
]}>
{typeof children === 'string' ? <Text>{children}</Text> : children}
</Provider>
</>)}
</Link>
);
});
3 changes: 2 additions & 1 deletion packages/@react-spectrum/s2/src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ export const FieldGroup = forwardRef(function FieldGroup(props: FieldGroupProps,
}
}}
onTouchEnd={e => {
if (!(e.target as Element).closest('button,input,textarea')) {
let target = e.target as HTMLElement;
Copy link
Member

Choose a reason for hiding this comment

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

test for this change?

Copy link
Member Author

@LFDanLu LFDanLu Dec 3, 2025

Choose a reason for hiding this comment

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

I added a test for textarea, but couldn't get the datepicker test to quite simulate the proper touch end event on the content editable for some reason... Using user event doesn't trigger the touchEnd properly and firing events on the spinbuttons directly doesn't seem to work either

if (!target.isContentEditable && !target.closest('button,input,textarea')) {
e.preventDefault();
(e.currentTarget.querySelector('input, textarea') as HTMLElement)?.focus();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-spectrum/s2/src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export interface PickerStyleProps {

type SelectionMode = 'single' | 'multiple';
export interface PickerProps<T extends object, M extends SelectionMode = 'single'> extends
Omit<AriaSelectProps<T, M>, 'children' | 'style' | 'className' | keyof GlobalDOMAttributes>,
Omit<AriaSelectProps<T, M>, 'children' | 'style' | 'className' | 'allowsEmptyCollection' | keyof GlobalDOMAttributes>,
PickerStyleProps,
StyleProps,
SpectrumLabelableProps,
Expand Down
31 changes: 22 additions & 9 deletions packages/@react-spectrum/s2/src/SelectBoxGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,16 @@ const selectBoxStyles = style({
horizontal: 188
}
},
maxWidth: {
default: 170,
orientation: {
horizontal: 480
'--select-box-max-width': {
type: 'width',
value: {
default: 170,
orientation: {
horizontal: 480
}
}
},
maxWidth: 'min(100%, var(--select-box-max-width))',
minHeight: {
default: 144,
orientation: {
Expand Down Expand Up @@ -272,7 +276,7 @@ const gridStyles = style<{orientation?: Orientation}>({
gridAutoRows: '1fr',
gap: 24,
justifyContent: 'center',
'--size': {
'--select-box-group-width': {
type: 'width',
value: {
orientation: {
Expand All @@ -281,10 +285,19 @@ const gridStyles = style<{orientation?: Orientation}>({
}
}
},
'--select-box-group-min-width': {
type: 'width',
value: {
orientation: {
horizontal: 188,
vertical: 144
}
}
},
gridTemplateColumns: {
orientation: {
horizontal: 'repeat(auto-fit, var(--size))',
vertical: 'repeat(auto-fit, var(--size))'
horizontal: 'repeat(auto-fit, minmax(var(--select-box-group-min-width), min(var(--select-box-group-width), 100%)))',
vertical: 'repeat(auto-fit, minmax(var(--select-box-group-min-width), min(var(--select-box-group-width), 100%)))'
}
}
}, getAllowedOverrides());
Expand Down Expand Up @@ -315,7 +328,7 @@ export function SelectBox(props: SelectBoxProps): ReactNode {
}, styles)}
style={pressScale(ref, UNSAFE_style)}
{...otherProps}>
{({isSelected, isDisabled, isHovered}) => {
{({isSelected, isDisabled, isHovered, selectionMode}) => {
return (
<>
<div
Expand All @@ -326,7 +339,7 @@ export function SelectBox(props: SelectBoxProps): ReactNode {
pointerEvents: 'none'
})}
aria-hidden="true">
{!isDisabled && (
{!isDisabled && selectionMode === 'multiple' && (
<div
className={box({
isSelected,
Expand Down
9 changes: 4 additions & 5 deletions packages/@react-spectrum/s2/src/TagGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ function TagGroupInner<T>({
<Provider
values={[
[RACTextContext, undefined],
[RACButtonContext, undefined],
[TagGroupContext, {size, isEmphasized}]
]}>
{/* invisible collection for measuring */}
Expand Down Expand Up @@ -525,11 +526,9 @@ export const Tag = /*#__PURE__*/ (forwardRef as forwardRefType)(function Tag({ch

function TagWrapper({children, isDisabled, allowsRemoving, isInRealDOM, isEmphasized, isSelected}) {
let {size = 'M'} = useSlottedContext(TagGroupContext) ?? {};

return (
<Provider
values={[
[RACButtonContext, null]
]}>
<>
{isInRealDOM && (
<div
className={style({
Expand Down Expand Up @@ -574,6 +573,6 @@ function TagWrapper({children, isDisabled, allowsRemoving, isInRealDOM, isEmphas
isStaticColor={isEmphasized && isSelected}
isDisabled={isDisabled} />
)}
</Provider>
</>
);
}
Loading