Skip to content

Commit 041a2be

Browse files
author
Amrit Kashyap Borah
committed
feat: use select picker in RJSF
fix: RJSF select menu stacking context issue
1 parent 45d359c commit 041a2be

7 files changed

Lines changed: 63 additions & 62 deletions

File tree

src/Common/RJSF/Form.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import RJSF from '@rjsf/core'
2020
import { SCHEMA_07_VALIDATOR } from '@Shared/validations'
2121

2222
import { templates, widgets } from './config'
23+
import { RJSF_FORM_SELECT_PORTAL_TARGET_ID } from './constants'
2324
import { FormProps } from './types'
2425
import {
2526
getFormStateFromFormData,
@@ -82,28 +83,32 @@ export const RJSFForm = forwardRef((props: FormProps, ref: FormProps['ref']) =>
8283
}
8384

8485
return (
85-
<Form
86-
noHtml5Validate
87-
showErrorList={false}
88-
autoComplete="off"
89-
{...props}
90-
formData={formState}
91-
{...(isUpdatePathKeywordPresent
92-
? {
93-
onChange: handleOnChange,
94-
onSubmit: handleOnSubmit,
95-
}
96-
: {})}
97-
className={`rjsf-form-template__container ${props.className || ''}`}
98-
validator={validator}
99-
templates={{
100-
...templates,
101-
...props.templates,
102-
}}
103-
formContext={formState}
104-
widgets={{ ...widgets, ...props.widgets }}
105-
translateString={translateString}
106-
ref={ref}
107-
/>
86+
<>
87+
<Form
88+
noHtml5Validate
89+
showErrorList={false}
90+
autoComplete="off"
91+
{...props}
92+
formData={formState}
93+
{...(isUpdatePathKeywordPresent
94+
? {
95+
onChange: handleOnChange,
96+
onSubmit: handleOnSubmit,
97+
}
98+
: {})}
99+
className={`rjsf-form-template__container ${props.className || ''}`}
100+
validator={validator}
101+
templates={{
102+
...templates,
103+
...props.templates,
104+
}}
105+
formContext={formState}
106+
widgets={{ ...widgets, ...props.widgets }}
107+
translateString={translateString}
108+
ref={ref}
109+
/>
110+
{/* NOTE: due to stacking context issues, we send this id to SelectPicker menuPortalTarget prop */}
111+
<div id={RJSF_FORM_SELECT_PORTAL_TARGET_ID} />
112+
</>
108113
)
109114
})

src/Common/RJSF/common/FieldRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const FieldRowWithLabel = ({
3131
<div
3232
className={
3333
showLabel
34-
? `display-grid dc__gap-12 rjsf-form-template__field ${shouldAlignCenter ? 'flex-align-center' : ''}`
34+
? `display-grid dc__gap-12 rjsf-form-template__field ${!shouldAlignCenter ? 'rjsf-form-template__field--align-top' : ''}`
3535
: ''
3636
}
3737
>

src/Common/RJSF/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ export const HIDE_SUBMIT_BUTTON_UI_SCHEMA = {
2727
norender: true,
2828
},
2929
}
30+
31+
export const RJSF_FORM_SELECT_PORTAL_TARGET_ID = 'rjsf-form-select-portal-target'

src/Common/RJSF/rjsfForm.scss

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@
3333
}
3434
}
3535

36+
&__field {
37+
align-items: center;
38+
}
39+
40+
&__field:has([class^="devtron-rjsf-select__"]),
41+
&__field.rjsf-form-template__field--align-top {
42+
align-items: flex-start;
43+
}
44+
45+
&__field:has([class^="devtron-rjsf-select__"]) {
46+
label {
47+
line-height: 32px;
48+
}
49+
}
50+
51+
3652
&__additional-fields {
3753
grid-template-columns: 1fr 1fr;
3854
}
@@ -90,4 +106,4 @@
90106
transition: all 100ms ease-out;
91107
}
92108
}
93-
}
109+
}

src/Common/RJSF/widgets/Select.tsx

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,10 @@
1515
*/
1616

1717
import { WidgetProps } from '@rjsf/utils'
18-
import ReactSelect, { MenuListProps, components } from 'react-select'
19-
import { PLACEHOLDERS } from '../constants'
18+
import { PLACEHOLDERS, RJSF_FORM_SELECT_PORTAL_TARGET_ID } from '../constants'
2019

21-
import { ReactComponent as ArrowDown } from '../../../Assets/Icon/ic-chevron-down.svg'
2220
import { deepEqual } from '@Common/Helper'
23-
import { commonSelectStyles } from '@Shared/Components'
24-
25-
const MenuList = ({ children, ...props }: MenuListProps) => (
26-
<components.MenuList {...props}>{Array.isArray(children) ? children.slice(0, 20) : children}</components.MenuList>
27-
)
28-
29-
const DropdownIndicator = (props) => (
30-
<components.DropdownIndicator {...props}>
31-
<ArrowDown className="icon-dim-20 icon-n5" data-testid="overview-project-edit-dropdown" />
32-
</components.DropdownIndicator>
33-
)
21+
import { SelectPicker } from '@Shared/Components'
3422

3523
export const SelectWidget = (props: WidgetProps) => {
3624
const {
@@ -47,7 +35,7 @@ export const SelectWidget = (props: WidgetProps) => {
4735
placeholder,
4836
} = props
4937
const { enumOptions: selectOptions = [] } = options
50-
const emptyValue = multiple ? [] : ''
38+
const emptyValue = multiple ? [] : null
5139

5240
const handleChange = (option) => {
5341
onChange(multiple ? option.map((o) => o.value) : option.value)
@@ -59,8 +47,8 @@ export const SelectWidget = (props: WidgetProps) => {
5947
: selectOptions.find((option) => deepEqual(value, option.value))
6048

6149
return (
62-
<ReactSelect
63-
id={id}
50+
<SelectPicker
51+
inputId={`devtron-rjsf-select__${id}`}
6452
name={id}
6553
isMulti={multiple}
6654
value={typeof value === 'undefined' ? emptyValue : getOption(value)}
@@ -71,23 +59,8 @@ export const SelectWidget = (props: WidgetProps) => {
7159
onFocus={() => onFocus(id, value)}
7260
placeholder={placeholder || PLACEHOLDERS.SELECT}
7361
isDisabled={disabled || readonly}
74-
styles={{
75-
...commonSelectStyles,
76-
control: (base, state) => ({
77-
...commonSelectStyles.control(base, state),
78-
minHeight: '36px',
79-
}),
80-
multiValue: (base, state) => ({
81-
...commonSelectStyles.multiValue(base, state),
82-
margin: '2px 8px 2px 2px',
83-
}),
84-
}}
85-
components={{
86-
IndicatorSeparator: null,
87-
DropdownIndicator,
88-
MenuList,
89-
}}
90-
menuPlacement="auto"
62+
menuPortalTarget={document.getElementById(RJSF_FORM_SELECT_PORTAL_TARGET_ID)}
63+
menuPosition='fixed'
9164
/>
9265
)
9366
}

src/Shared/Components/SelectPicker/SelectPicker.component.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,12 @@ const SelectPicker = <OptionValue, IsMulti extends boolean>({
376376
onKeyDown?.(e)
377377
}
378378

379-
const handleFocus: ReactSelectProps['onFocus'] = () => {
380-
setIsFocussed(true)
379+
const handleFocus: ReactSelectProps['onFocus'] = (e) => {
380+
if (!shouldRenderTextArea) {
381+
setIsFocussed(true)
382+
}
383+
384+
props.onFocus?.(e)
381385
}
382386

383387
const handleBlur: ReactSelectProps['onFocus'] = (e) => {
@@ -483,7 +487,7 @@ const SelectPicker = <OptionValue, IsMulti extends boolean>({
483487
onKeyDown={handleKeyDown}
484488
shouldRenderTextArea={shouldRenderTextArea}
485489
customDisplayText={customDisplayText}
486-
{...(!shouldRenderTextArea ? { onFocus: handleFocus } : {})}
490+
onFocus={handleFocus}
487491
onBlur={handleBlur}
488492
onChange={handleChange}
489493
controlShouldRenderValue={controlShouldRenderValue}

src/Shared/Components/SelectPicker/type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export type SelectPickerProps<OptionValue = number | string, IsMulti extends boo
170170
| 'filterOption'
171171
| 'noOptionsMessage'
172172
| 'defaultMenuIsOpen'
173+
| 'onFocus'
173174
> &
174175
Partial<
175176
Pick<

0 commit comments

Comments
 (0)