Skip to content

Commit 6bdca86

Browse files
authored
fix: picker overflow bug (#8419)
* fix: Select and Picker S2 overflow bug * build a story for s2 as well * remove extra story * fix all examples and add docs
1 parent ef071a5 commit 6bdca86

File tree

9 files changed

+46
-19
lines changed

9 files changed

+46
-19
lines changed

packages/@react-spectrum/s2/src/Picker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ export const Picker = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pick
340340
aria-describedby={spinnerId}
341341
placeholder={placeholder}
342342
style={UNSAFE_style}
343-
className={UNSAFE_className + style(field(), getAllowedOverrides())({
343+
className={UNSAFE_className + style({...field(), position: 'relative'}, getAllowedOverrides())({
344344
isInForm: !!formContext,
345345
labelPosition,
346346
size

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import {Select, SelectValue, Label, Button, Popover, ListBox, ListBoxItem} from
8585

8686
.react-aria-Select {
8787
color: var(--text-color);
88+
position: relative;
8889
max-width: 250px;
8990
width: fit-content;
9091

@@ -1273,3 +1274,27 @@ it('Select can select an option via keyboard', async function () {
12731274
```
12741275

12751276
<ClassAPI links={selectUtil.links} class={selectUtil.exports.SelectTester} />
1277+
1278+
## Gotchas
1279+
1280+
`Select` requires a `position: relative` either on itself or on a close parent. This is because
1281+
the `Popover` component uses a hidden input to manage native form functionality. The hidden input
1282+
is absolutely positioned and can spill out unexpectedly if the parent is not positioned relative
1283+
causing unintended scroll bars to appear.
1284+
1285+
Note: you do not need to set the style inline, you can use any CSS method to do it.
1286+
1287+
```tsx render=false
1288+
<Select style={{position: 'relative'}}>
1289+
<Label>Favorite Animal</Label>
1290+
<Button>
1291+
<SelectValue />
1292+
<span aria-hidden="true">▼</span>
1293+
</Button>
1294+
<Popover>
1295+
<ListBox>
1296+
...
1297+
</ListBox>
1298+
</Popover>
1299+
</Select>
1300+
```

packages/react-aria-components/docs/examples/searchable-select.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function SelectExample() {
9595

9696
return (
9797
<div className="bg-linear-to-br from-cyan-200 to-blue-400 p-8 sm:h-[350px] rounded-lg flex justify-center">
98-
<Select className="flex flex-col gap-1 w-[200px]">
98+
<Select className="flex flex-col gap-1 w-[200px] relative">
9999
<Label className="text-black cursor-default">Language</Label>
100100
<Button className="flex items-center cursor-default rounded-lg border-0 bg-white/90 pressed:bg-white transition py-2 pl-5 pr-2 text-base text-left leading-normal ring-1 ring-black/5 shadow-md text-gray-700 focus:outline-hidden focus-visible:outline-2 outline-black outline-offset-3 focus-visible:ring-black/25">
101101
<SelectValue className="flex-1 truncate" />

packages/react-aria-components/docs/examples/status-select.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import CheckIcon from '@spectrum-icons/workflow/Checkmark';
4646
function SelectExample() {
4747
return (
4848
<div className="bg-linear-to-tl from-amber-500 to-rose-700 p-8 sm:h-[250px] rounded-lg flex justify-center">
49-
<Select className="flex flex-col gap-1 w-[200px]">
49+
<Select className="flex flex-col gap-1 w-[200px] relative">
5050
<Label className="text-white cursor-default">Status</Label>
5151
<Button className="flex items-center cursor-default rounded-lg border-0 bg-white/90 pressed:bg-white transition py-2 pl-5 pr-2 text-base text-left leading-normal shadow-md text-gray-700 focus:outline-hidden focus-visible:ring-2 ring-white ring-offset-2 ring-offset-rose-700">
5252
<SelectValue className="flex-1 truncate placeholder-shown:italic" />

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,16 @@ function SelectInner<T extends object>({props, selectRef: ref, collection}: Sele
213213
data-open={state.isOpen || undefined}
214214
data-disabled={props.isDisabled || undefined}
215215
data-invalid={validation.isInvalid || undefined}
216-
data-required={props.isRequired || undefined} />
217-
<HiddenSelect
218-
autoComplete={props.autoComplete}
219-
state={state}
220-
triggerRef={buttonRef}
221-
label={label}
222-
name={props.name}
223-
isDisabled={props.isDisabled} />
216+
data-required={props.isRequired || undefined}>
217+
{renderProps.children}
218+
<HiddenSelect
219+
autoComplete={props.autoComplete}
220+
state={state}
221+
triggerRef={buttonRef}
222+
label={label}
223+
name={props.name}
224+
isDisabled={props.isDisabled} />
225+
</div>
224226
</Provider>
225227
);
226228
}

packages/react-aria-components/stories/Autocomplete.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ export const AutocompleteMenuInPopoverDialogTrigger = {
831831
let manyItems = [...Array(100)].map((_, i) => ({id: i, name: `Item ${i}`}));
832832

833833
export const AutocompleteSelect = () => (
834-
<Select style={{marginBottom: 40}}>
834+
<Select style={{marginBottom: 40, position: 'relative'}}>
835835
<Label style={{display: 'block'}}>Test</Label>
836836
<Button>
837837
<SelectValue />

packages/react-aria-components/stories/Form.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const FormAutoFillExample = () => {
4343
<Label>Zip</Label>
4444
<Input name="city" type="text" id="city" autoComplete="shipping postal-code" />
4545
</TextField>
46-
<Select name="country" id="country" autoComplete="shipping country">
46+
<Select style={{position: 'relative'}} name="country" id="country" autoComplete="shipping country">
4747
<Label>Country</Label>
4848
<Button>
4949
<SelectValue />

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default {
2222
};
2323

2424
export const SelectExample = () => (
25-
<Select data-testid="select-example" id="select-example-id">
25+
<Select data-testid="select-example" id="select-example-id" style={{position: 'relative'}}>
2626
<Label style={{display: 'block'}}>Test</Label>
2727
<Button>
2828
<SelectValue />
@@ -43,7 +43,7 @@ export const SelectExample = () => (
4343
);
4444

4545
export const SelectRenderProps = () => (
46-
<Select data-testid="select-render-props">
46+
<Select data-testid="select-render-props" style={{position: 'relative'}}>
4747
{({isOpen}) => (
4848
<>
4949
<Label style={{display: 'block'}}>Test</Label>
@@ -67,7 +67,7 @@ export const SelectRenderProps = () => (
6767
let manyItems = [...Array(100)].map((_, i) => ({id: i, name: `Item ${i}`}));
6868

6969
export const SelectManyItems = () => (
70-
<Select>
70+
<Select style={{position: 'relative'}}>
7171
<Label style={{display: 'block'}}>Test</Label>
7272
<Button>
7373
<SelectValue />
@@ -85,7 +85,7 @@ export const SelectManyItems = () => (
8585
);
8686

8787
export const VirtualizedSelect = () => (
88-
<Select>
88+
<Select style={{position: 'relative'}}>
8989
<Label style={{display: 'block'}}>Test</Label>
9090
<Button>
9191
<SelectValue />
@@ -138,7 +138,7 @@ export const AsyncVirtualizedCollectionRenderSelect = (args) => {
138138
});
139139

140140
return (
141-
<Select>
141+
<Select style={{position: 'relative'}}>
142142
<Label style={{display: 'block'}}>Async Virtualized Collection render Select</Label>
143143
<Button style={{position: 'relative'}}>
144144
<SelectValue />

starters/tailwind/src/Select.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function Select<T extends object>(
3939
{ label, description, errorMessage, children, items, ...props }: SelectProps<T>
4040
) {
4141
return (
42-
<AriaSelect {...props} className={composeTailwindRenderProps(props.className, 'group flex flex-col gap-1')}>
42+
<AriaSelect {...props} className={composeTailwindRenderProps(props.className, 'group flex flex-col gap-1 relative')}>
4343
{label && <Label>{label}</Label>}
4444
<Button className={styles}>
4545
<SelectValue className="flex-1 text-sm placeholder-shown:italic" />

0 commit comments

Comments
 (0)