Skip to content

Commit 684e596

Browse files
authored
Add data-trigger to Popover to style based on trigger component (#5429)
1 parent 714c8f6 commit 684e596

17 files changed

+70
-31
lines changed

packages/react-aria-components/docs/ComboBox.mdx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,9 @@ import {ComboBox, Label, Input, Button, Popover, ListBox, ListBoxItem} from 'rea
112112
}
113113
}
114114

115-
@layer popover {
116-
.react-aria-Popover {
115+
.react-aria-Popover[data-trigger=ComboBox] {
117116
width: var(--trigger-width);
118-
}
119-
}
120117

121-
@layer listbox {
122118
.react-aria-ListBox {
123119
display: block;
124120
width: unset;
@@ -1230,12 +1226,14 @@ A [Button](Button.html) can be targeted with the `.react-aria-Button` CSS select
12301226

12311227
### Popover
12321228

1233-
The [Popover](Popover.html) component can be targeted with the `.react-aria-Popover` CSS selector, or by overriding with a custom `className`. Note that it renders in a [React Portal](https://reactjs.org/docs/portals.html), so it will not appear as a descendant of the ComboBox in the DOM.
1229+
The [Popover](Popover.html) component can be targeted with the `.react-aria-Popover` CSS selector, or by overriding with a custom `className`. Note that it renders in a [React Portal](https://reactjs.org/docs/portals.html), so it will not appear as a descendant of the ComboBox in the DOM. It supports the following states and render props:
1230+
1231+
<StateTable properties={docs.exports.PopoverRenderProps.properties} />
12341232

1235-
The `--trigger-width` CSS custom property will be set on the popover, which you can use to make the popover match the width of the combobox.
1233+
Within a ComboBox, the popover will have the `data-trigger="ComboBox"` attribute, which can be used to define combobox-specific styles. In addition, the `--trigger-width` CSS custom property will be set on the popover, which you can use to make the popover match the width of the combobox.
12361234

12371235
```css render=false
1238-
.react-aria-Popover {
1236+
.react-aria-Popover[data-trigger=ComboBox] {
12391237
width: var(--trigger-width);
12401238
}
12411239
```

packages/react-aria-components/docs/DatePicker.mdx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,9 @@ import {DatePicker, Label, Group, Popover, Dialog, Calendar, CalendarGrid, Calen
121121
}
122122
}
123123

124-
@layer popover {
125-
.react-aria-Popover {
126-
max-width: unset;
127-
padding: 1.25rem;
128-
}
124+
.react-aria-Popover[data-trigger=DatePicker] {
125+
max-width: unset;
126+
padding: 1.25rem;
129127
}
130128
```
131129

@@ -944,6 +942,14 @@ The [Popover](Popover.html) component can be targeted with the `.react-aria-Popo
944942

945943
<StateTable properties={docs.exports.PopoverRenderProps.properties} />
946944

945+
Within a DatePicker, the popover will have the `data-trigger="DatePicker"` attribute, which can be used to define date picker-specific styles.
946+
947+
```css render=false
948+
.react-aria-Popover[data-trigger=DatePicker] {
949+
/* ... */
950+
}
951+
```
952+
947953
### Dialog
948954

949955
A [Dialog](Dialog.html) can be targeted with the `.react-aria-Dialog` CSS selector, or by overriding with a custom `className`.

packages/react-aria-components/docs/DateRangePicker.mdx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,9 @@ import {DateRangePicker, Label, Group, Popover, Dialog, RangeCalendar, CalendarG
157157
}
158158
}
159159

160-
@layer popover {
161-
.react-aria-Popover {
162-
max-width: unset;
163-
padding: 1.25rem;
164-
}
160+
.react-aria-Popover[data-trigger=DateRangePicker] {
161+
max-width: unset;
162+
padding: 1.25rem;
165163
}
166164
```
167165

@@ -1035,6 +1033,14 @@ The [Popover](Popover.html) component can be targeted with the `.react-aria-Popo
10351033

10361034
<StateTable properties={docs.exports.PopoverRenderProps.properties} />
10371035

1036+
Within a DateRangePicker, the popover will have the `data-trigger="DateRangePicker"` attribute, which can be used to define date range picker-specific styles.
1037+
1038+
```css render=false
1039+
.react-aria-Popover[data-trigger=DateRangePicker] {
1040+
/* ... */
1041+
}
1042+
```
1043+
10381044
### Dialog
10391045

10401046
A [Dialog](Dialog.html) can be targeted with the `.react-aria-Dialog` CSS selector, or by overriding with a custom `className`.

packages/react-aria-components/docs/Menu.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,14 @@ The [Popover](Popover.html) component can be targeted with the `.react-aria-Popo
800800

801801
<StateTable properties={docs.exports.PopoverRenderProps.properties} />
802802

803+
Within a MenuTrigger, the popover will have the `data-trigger="MenuTrigger"` attribute, which can be used to define menu-specific styles.
804+
805+
```css render=false
806+
.react-aria-Popover[data-trigger=MenuTrigger] {
807+
/* ... */
808+
}
809+
```
810+
803811
### Menu
804812

805813
A `Menu` can be targeted with the `.react-aria-Menu` CSS selector, or by overriding with a custom `className`.

packages/react-aria-components/docs/Select.mdx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,9 @@ import {Select, SelectValue, Label, Button, Popover, ListBox, ListBoxItem} from
117117
}
118118
}
119119

120-
@layer popover {
121-
.react-aria-Popover {
120+
.react-aria-Popover[data-trigger=Select] {
122121
min-width: var(--trigger-width);
123-
}
124-
}
125122

126-
@layer listbox {
127123
.react-aria-ListBox {
128124
display: block;
129125
width: unset;
@@ -994,12 +990,14 @@ A `SelectValue` can be targed with the `.react-aria-SelectValue` CSS selector, o
994990

995991
### Popover
996992

997-
The [Popover](Popover.html) component can be targeted with the `.react-aria-Popover` CSS selector, or by overriding with a custom `className`. Note that it renders in a [React Portal](https://reactjs.org/docs/portals.html), so it will not appear as a descendant of the Select in the DOM.
993+
The [Popover](Popover.html) component can be targeted with the `.react-aria-Popover` CSS selector, or by overriding with a custom `className`. Note that it renders in a [React Portal](https://reactjs.org/docs/portals.html), so it will not appear as a descendant of the Select in the DOM. It supports the following states and render props:
994+
995+
<StateTable properties={docs.exports.PopoverRenderProps.properties} />
998996

999-
The `--trigger-width` CSS custom property will be set on the popover, which you can use to make the popover match the width of the select button.
997+
Within a Select, the popover will have the `data-trigger="Select"` attribute, which can be used to define select-specific styles. In addition, the `--trigger-width` CSS custom property will be set on the popover, which you can use to make the popover match the width of the select button.
1000998

1001999
```css render=false
1002-
.react-aria-Popover {
1000+
.react-aria-Popover[data-trigger=Select] {
10031001
width: var(--trigger-width);
10041002
}
10051003
```

packages/react-aria-components/src/ComboBox.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ function ComboBoxInner<T extends object>({props, collection, comboBoxRef: ref}:
192192
triggerRef: inputRef,
193193
placement: 'bottom start',
194194
isNonModal: true,
195+
trigger: 'ComboBox',
195196
style: {'--trigger-width': menuWidth} as React.CSSProperties
196197
}],
197198
[ListBoxContext, {...listBoxProps, ref: listBoxRef}],

packages/react-aria-components/src/DatePicker.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
122122
[LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
123123
[CalendarContext, calendarProps],
124124
[OverlayTriggerStateContext, state],
125-
[PopoverContext, {triggerRef: groupRef, placement: 'bottom start'}],
125+
[PopoverContext, {trigger: 'DatePicker', triggerRef: groupRef, placement: 'bottom start'}],
126126
[DialogContext, dialogProps],
127127
[TextContext, {
128128
slots: {
@@ -205,7 +205,7 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
205205
[LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
206206
[RangeCalendarContext, calendarProps],
207207
[OverlayTriggerStateContext, state],
208-
[PopoverContext, {triggerRef: groupRef, placement: 'bottom start'}],
208+
[PopoverContext, {trigger: 'DateRangePicker', triggerRef: groupRef, placement: 'bottom start'}],
209209
[DialogContext, dialogProps],
210210
[DateFieldContext, {
211211
slots: {

packages/react-aria-components/src/Dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export function DialogTrigger(props: DialogTriggerProps) {
5555
values={[
5656
[OverlayTriggerStateContext, state],
5757
[DialogContext, overlayProps],
58-
[PopoverContext, {triggerRef: buttonRef}]
58+
[PopoverContext, {trigger: 'DialogTrigger', triggerRef: buttonRef}]
5959
]}>
6060
<PressResponder {...triggerProps} ref={buttonRef} isPressed={state.isOpen}>
6161
{props.children}

packages/react-aria-components/src/Menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function MenuTrigger(props: MenuTriggerProps) {
4747
values={[
4848
[MenuContext, menuProps],
4949
[OverlayTriggerStateContext, state],
50-
[PopoverContext, {triggerRef: ref, placement: 'bottom start'}]
50+
[PopoverContext, {trigger: 'MenuTrigger', triggerRef: ref, placement: 'bottom start'}]
5151
]}>
5252
<PressResponder {...menuTriggerProps} ref={ref} isPressed={state.isOpen}>
5353
{props.children}

packages/react-aria-components/src/Popover.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ import {OverlayTriggerStateContext} from './Dialog';
1919
import React, {createContext, ForwardedRef, forwardRef, RefObject, useContext} from 'react';
2020

2121
export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPopoverProps, 'popoverRef' | 'triggerRef'>, OverlayTriggerProps, RenderProps<PopoverRenderProps>, SlotProps {
22+
/**
23+
* The name of the component that triggered the popover. This is reflected on the element
24+
* as the `data-trigger` attribute, and can be used to provide specific
25+
* styles for the popover depending on which element triggered it.
26+
*/
27+
trigger?: string,
2228
/**
2329
* The ref for the element which the popover positions itself with respect to.
2430
*
@@ -37,6 +43,11 @@ export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPo
3743
}
3844

3945
export interface PopoverRenderProps {
46+
/**
47+
* The name of the component that triggered the popover, e.g. "DialogTrigger" or "ComboBox".
48+
* @selector [data-trigger="..."]
49+
*/
50+
trigger: string | null,
4051
/**
4152
* The placement of the popover relative to the trigger.
4253
* @selector [data-placement="left | right | top | bottom"]
@@ -69,6 +80,7 @@ function Popover(props: PopoverProps, ref: ForwardedRef<HTMLElement>) {
6980
let children = props.children;
7081
if (typeof children === 'function') {
7182
children = children({
83+
trigger: props.trigger || null,
7284
placement: 'bottom',
7385
isEntering: false,
7486
isExiting: false
@@ -101,7 +113,8 @@ export {_Popover as Popover};
101113
interface PopoverInnerProps extends AriaPopoverProps, RenderProps<PopoverRenderProps>, SlotProps {
102114
state: OverlayTriggerState,
103115
isEntering?: boolean,
104-
isExiting: boolean
116+
isExiting: boolean,
117+
trigger?: string
105118
}
106119

107120
function PopoverInner({state, isExiting, ...props}: PopoverInnerProps) {
@@ -116,6 +129,7 @@ function PopoverInner({state, isExiting, ...props}: PopoverInnerProps) {
116129
...props,
117130
defaultClassName: 'react-aria-Popover',
118131
values: {
132+
trigger: props.trigger || null,
119133
placement,
120134
isEntering,
121135
isExiting
@@ -133,6 +147,7 @@ function PopoverInner({state, isExiting, ...props}: PopoverInnerProps) {
133147
ref={ref}
134148
slot={props.slot || undefined}
135149
style={style}
150+
data-trigger={props.trigger}
136151
data-placement={placement}
137152
data-entering={isEntering || undefined}
138153
data-exiting={isExiting || undefined}>

packages/react-aria-components/src/Select.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ function Select<T extends object>(props: SelectProps<T>, ref: ForwardedRef<HTMLD
148148
[ButtonContext, {...triggerProps, ref: buttonRef, isPressed: state.isOpen}],
149149
[OverlayTriggerStateContext, state],
150150
[PopoverContext, {
151+
trigger: 'Select',
151152
triggerRef: buttonRef,
152153
placement: 'bottom start',
153154
style: {'--trigger-width': buttonWidth} as React.CSSProperties

packages/react-aria-components/test/ComboBox.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ describe('ComboBox', () => {
6161
let listbox = getByRole('listbox');
6262
expect(listbox).toHaveAttribute('class', 'react-aria-ListBox');
6363
expect(listbox.closest('.react-aria-Popover')).toBeInTheDocument();
64+
expect(listbox.closest('.react-aria-Popover')).toHaveAttribute('data-trigger', 'ComboBox');
6465

6566
let options = within(listbox).getAllByRole('option');
6667
expect(options).toHaveLength(3);

packages/react-aria-components/test/DatePicker.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ describe('DatePicker', () => {
8181
expect(dialog).toHaveAttribute('aria-labelledby');
8282
expect(dialog.getAttribute('aria-labelledby')).toContain(label.id);
8383
expect(dialog.closest('.react-aria-Popover')).toBeInTheDocument();
84+
expect(dialog.closest('.react-aria-Popover')).toHaveAttribute('data-trigger', 'DatePicker');
8485

8586
expect(getByRole('grid')).toHaveClass('react-aria-CalendarGrid');
8687
});

packages/react-aria-components/test/DateRangePicker.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ describe('DateRangePicker', () => {
8787
expect(dialog).toHaveAttribute('aria-labelledby');
8888
expect(dialog.getAttribute('aria-labelledby')).toContain(label.id);
8989
expect(dialog.closest('.react-aria-Popover')).toBeInTheDocument();
90+
expect(dialog.closest('.react-aria-Popover')).toHaveAttribute('data-trigger', 'DateRangePicker');
9091

9192
expect(getByRole('grid')).toHaveClass('react-aria-CalendarGrid');
9293
});

packages/react-aria-components/test/Menu.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ describe('Menu', () => {
262262

263263
let popover = menu.closest('.react-aria-Popover');
264264
expect(popover).toBeInTheDocument();
265+
expect(popover).toHaveAttribute('data-trigger', 'MenuTrigger');
265266

266267
await user.click(getAllByRole('menuitem')[1]);
267268
expect(onAction).toHaveBeenLastCalledWith('rename');

packages/react-aria-components/test/Popover.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ describe('Popover', () => {
4949

5050
let dialog = getByRole('dialog');
5151
expect(dialog).toBeInTheDocument();
52+
expect(dialog.closest('.react-aria-Popover')).toHaveAttribute('data-trigger', 'DialogTrigger');
5253

5354
await user.click(document.body);
5455

packages/react-aria-components/test/Select.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ describe('Select', () => {
6868
let listbox = getByRole('listbox');
6969
expect(listbox).toHaveAttribute('class', 'react-aria-ListBox');
7070
expect(listbox.closest('.react-aria-Popover')).toBeInTheDocument();
71+
expect(listbox.closest('.react-aria-Popover')).toHaveAttribute('data-trigger', 'Select');
7172

7273
let options = within(listbox).getAllByRole('option');
7374
expect(options).toHaveLength(3);

0 commit comments

Comments
 (0)