Skip to content

Commit 406f1ef

Browse files
fix selectfield portal
1 parent da58aff commit 406f1ef

5 files changed

Lines changed: 51 additions & 2 deletions

File tree

src/app/SelectMenuPortal.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React, {
2+
createContext,
3+
useCallback,
4+
useContext,
5+
useState,
6+
} from "react";
7+
8+
const SelectMenuPortalContext = createContext<HTMLElement | null>(null);
9+
10+
export function SelectMenuPortalProvider({ children }: { children: React.ReactNode }) {
11+
const [portalEl, setPortalEl] = useState<HTMLElement | null>(null);
12+
const setRef = useCallback((el: HTMLDivElement | null) => {
13+
setPortalEl(el);
14+
}, []);
15+
16+
return (
17+
<SelectMenuPortalContext.Provider value={portalEl}>
18+
{children}
19+
<div ref={setRef} data-select-menu-portal />
20+
</SelectMenuPortalContext.Provider>
21+
);
22+
}
23+
24+
export function useSelectMenuPortal(): HTMLElement | null {
25+
return useContext(SelectMenuPortalContext);
26+
}

src/app/components/Forms/MultiSelectField.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import ReactSelect, {
44
MenuPlacement,
55
} from "react-select";
66
import CreatableSelect from "react-select/creatable";
7-
import { customStyles, SingleValue } from "./SelectField";
87
import { clone } from "lodash";
8+
import { useSelectMenuPortal } from "@/app/SelectMenuPortal";
99
import { isDefined } from "@/app/utils";
10+
import { customStyles, SingleValue } from "./SelectField";
1011

1112
export type MultiSelectFieldProps = {
1213
value: string[];
@@ -49,6 +50,8 @@ export default function MultiSelectField({
4950
formatCreateLabel,
5051
validOptionPattern,
5152
}: MultiSelectFieldProps) {
53+
const menuPortalTarget = useSelectMenuPortal();
54+
5255
const map = useMemo(() => {
5356
const map = new Map<string, SingleValue>();
5457
options.forEach((opt) => {
@@ -71,6 +74,8 @@ export default function MultiSelectField({
7174
isMulti
7275
className={className}
7376
menuPlacement={menuPlacement}
77+
menuPortalTarget={menuPortalTarget}
78+
menuPosition="fixed"
7479
classNamePrefix="gb-multi-select"
7580
formatOptionLabel={formatOptionLabel}
7681
isDisabled={disabled || false}

src/app/components/Forms/SelectField.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ import ReactSelect, {
55
MenuPlacement,
66
} from "react-select";
77
import { clone } from "lodash";
8+
import { useSelectMenuPortal } from "@/app/SelectMenuPortal";
89

910
export const customStyles = {
11+
menuPortal: (provided: any) => ({
12+
...provided,
13+
zIndex: "var(--gb-select-menu-z)",
14+
}),
1015
control: (provided: any, state: any) => ({
1116
...provided,
1217
borderColor: state.isFocused ? "var(--focus-8)" : "var(--gray-a7)",
@@ -114,6 +119,8 @@ export default function SelectField({
114119
validOptionPattern,
115120
menuPlacement,
116121
}: SelectFieldProps) {
122+
const menuPortalTarget = useSelectMenuPortal();
123+
117124
const map = useMemo(() => {
118125
const map = new Map<string, SingleValue>();
119126
options.forEach((opt) => {
@@ -136,6 +143,8 @@ export default function SelectField({
136143
return (
137144
<CreatableSelect
138145
menuPlacement={menuPlacement}
146+
menuPortalTarget={menuPortalTarget}
147+
menuPosition={menuPortalTarget ? "fixed" : undefined}
139148
className={className}
140149
isClearable={isClearable}
141150
isDisabled={disabled || false}
@@ -187,6 +196,8 @@ export default function SelectField({
187196
return (
188197
<ReactSelect
189198
menuPlacement={menuPlacement}
199+
menuPortalTarget={menuPortalTarget}
200+
menuPosition={menuPortalTarget ? "fixed" : undefined}
190201
isClearable={isClearable}
191202
className={className}
192203
isDisabled={disabled || false}

src/app/css/forms.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
:root {
2+
--gb-select-menu-z: 2147483647; /* ahead of z-front */
3+
}
4+
15
.FormRoot {
26
margin: auto;
37
}

src/app/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { Attributes } from "@growthbook/growthbook";
3333
import { AppMenu } from "@/app/components/AppMenu";
3434
import Share from "@/app/components/Share";
3535
import ImportExport from "@/app/components/ImportExport";
36+
import { SelectMenuPortalProvider } from "@/app/SelectMenuPortal";
3637

3738
export const MW = 1200; // max-width
3839
export const RESPONSIVE_W = 570; // small width mode
@@ -106,7 +107,8 @@ export const App = () => {
106107
appearance={dark ? "dark" : "light"}
107108
className={dark ? "dark" : "light"}
108109
>
109-
<div id="main" className="text-indigo-12 overflow-hidden">
110+
<SelectMenuPortalProvider>
111+
<div id="main" className="text-indigo-12 overflow-hidden">
110112
<div
111113
className="shadow-sm px-3 pt-1 w-full relative bg-surface z-front"
112114
style={{ height: NAV_H }}
@@ -343,6 +345,7 @@ export const App = () => {
343345
</Dialog.Content>
344346
</Dialog.Root>
345347
</div>
348+
</SelectMenuPortalProvider>
346349
</Theme>
347350
);
348351
};

0 commit comments

Comments
 (0)