Skip to content

Commit 8cd1c3d

Browse files
authored
Revert "add support in Dropdown for creating new options using allowCreateOptions prop (#1107)"This reverts commit 646d154. (#1123)
1 parent 808b5d0 commit 8cd1c3d

File tree

5 files changed

+88
-255
lines changed

5 files changed

+88
-255
lines changed

.changeset/chatty-mails-yell.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@ensembleui/react-kitchen-sink": patch
3+
"@ensembleui/react-runtime": patch
4+
---
5+
6+
Revert "add support in Dropdown for creating new options using allowCreateOptions prop (#1107)"This reverts commit 646d1543151dd84a30da641cc1cf2b08e24a0b2a.

apps/kitchen-sink/src/ensemble/screens/forms.yaml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,6 @@ View:
175175
onChange:
176176
executeCode: |
177177
console.log('dropdown changed', value);
178-
- Dropdown:
179-
id: dropdownWithCreateOptions
180-
label: Dropdown with create options
181-
hintText: Try typing "New Option"
182-
allowCreateOptions: true
183-
value: option2
184-
items:
185-
- label: Option 1
186-
value: option1
187-
- label: Option 2
188-
value: option2
189-
onChange:
190-
executeCode: |
191-
console.log('dropdownWithCreateOptions changed', value);
192178
# If you omit id, the form value key will be the label
193179
- TextInput:
194180
id: formTextInput

packages/runtime/src/widgets/Form/Dropdown.tsx

Lines changed: 10 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import { Select, Form, Space } from "antd";
2-
import { PlusCircleOutlined } from "@ant-design/icons";
3-
import React, {
4-
useCallback,
5-
useEffect,
6-
useMemo,
7-
useRef,
8-
useState,
9-
} from "react";
1+
import { Select, Form } from "antd";
2+
import React, { useCallback, useEffect, useMemo, useState } from "react";
103
import {
114
CustomScopeProvider,
125
evaluate,
@@ -20,7 +13,7 @@ import type {
2013
EnsembleAction,
2114
Expression,
2215
} from "@ensembleui/react-framework";
23-
import { get, isEmpty, isNull, isObject, isString } from "lodash-es";
16+
import { get, isArray, isEmpty, isNull, isObject, isString } from "lodash-es";
2417
import { WidgetRegistry } from "../../registry";
2518
import type {
2619
EnsembleWidgetProps,
@@ -33,7 +26,6 @@ import { getComponentStyles } from "../../shared/styles";
3326
import type { HasBorder } from "../../shared/hasSchema";
3427
import type { FormInputProps } from "./types";
3528
import { EnsembleFormItem } from "./FormItem";
36-
import { updateFieldValue } from "./Form";
3729

3830
const widgetName = "Dropdown";
3931

@@ -64,7 +56,6 @@ export type DropdownProps = {
6456
autoComplete: Expression<boolean>;
6557
hintStyle?: EnsembleWidgetStyles;
6658
panel?: { [key: string]: unknown };
67-
allowCreateOptions?: boolean;
6859
} & EnsembleWidgetProps<DropdownStyles> &
6960
HasItemTemplate & {
7061
"item-template"?: { value: Expression<string> };
@@ -73,49 +64,11 @@ export type DropdownProps = {
7364
const DropdownRenderer = (
7465
menu: React.ReactElement,
7566
panel?: { [key: string]: unknown },
76-
newOption?: string,
77-
onAddNewOption?: (value: string) => void,
7867
): React.ReactElement => {
7968
const panelOption = useMemo(() => {
8069
return panel ? EnsembleRuntime.render([unwrapWidget(panel)]) : null;
8170
}, []);
8271

83-
// if we have a new option to add, show custom content along with the menu
84-
if (newOption) {
85-
return (
86-
<>
87-
<div style={{ padding: "10px 15px" }}>
88-
<span>There are no matches</span>
89-
</div>
90-
<Space
91-
style={{
92-
padding: "10px 15px",
93-
display: "flex",
94-
flexDirection: "column",
95-
alignItems: "flex-start",
96-
width: "100%",
97-
cursor: "pointer",
98-
}}
99-
onClick={() => onAddNewOption?.(newOption)}
100-
>
101-
<Space>
102-
<PlusCircleOutlined />
103-
<span>{`Add "${newOption}"`}</span>
104-
</Space>
105-
</Space>
106-
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
107-
<div
108-
onMouseDown={(e) => {
109-
e.preventDefault();
110-
e.stopPropagation();
111-
}}
112-
>
113-
{panelOption}
114-
</div>
115-
</>
116-
);
117-
}
118-
11972
return (
12073
<>
12174
{menu}
@@ -136,10 +89,8 @@ const Dropdown: React.FC<DropdownProps> = (props) => {
13689
const [selectedValue, setSelectedValue] = useState<
13790
string | number | undefined
13891
>();
139-
const [newOption, setNewOption] = useState("");
92+
14093
const [isOpen, setIsOpen] = useState<boolean>(false);
141-
const [items, setItems] = useState<SelectOption[]>([]);
142-
const initializedRef = useRef(false);
14394
const {
14495
"item-template": itemTemplate,
14596
onChange,
@@ -173,21 +124,8 @@ const Dropdown: React.FC<DropdownProps> = (props) => {
173124
(value?: number | string) => {
174125
setSelectedValue(value);
175126
onChangeAction?.callback({ value });
176-
177-
// if the selected value is a new option that doesn't exist in items, add it
178-
if (value && values?.allowCreateOptions) {
179-
const optionExists = items.find((item) => item.value === value);
180-
if (!optionExists) {
181-
const newItem: SelectOption = {
182-
label: value.toString(),
183-
value: value,
184-
};
185-
setItems((prevItems) => [...prevItems, newItem]);
186-
}
187-
}
188-
setNewOption("");
189127
},
190-
[onChangeAction?.callback, values?.allowCreateOptions, items],
128+
[onChangeAction?.callback],
191129
);
192130

193131
const onItemSelectAction = useEnsembleAction(onItemSelect);
@@ -199,60 +137,16 @@ const Dropdown: React.FC<DropdownProps> = (props) => {
199137
[onItemSelectAction?.callback],
200138
);
201139

202-
const handleSearch = (value: string): void => {
203-
if (!values?.allowCreateOptions) {
204-
return;
205-
}
206-
207-
const isOptionExist = items.find((option) =>
208-
option.label.toString().toLowerCase().includes(value.toLowerCase()),
209-
);
210-
211-
if (!isOptionExist && value.trim()) {
212-
setNewOption(value);
213-
} else {
214-
setNewOption("");
215-
}
216-
};
217-
218-
const handleAddNewOption = useCallback(
219-
(value: string) => {
220-
const newItem: SelectOption = {
221-
label: value,
222-
value: value,
223-
};
224-
setItems((prevItems) => [...prevItems, newItem]);
225-
226-
setSelectedValue(value);
227-
onChangeAction?.callback({ value });
228-
229-
// trigger form's onChange action
230-
updateFieldValue(formInstance, values?.id ?? values?.label ?? "", value);
231-
232-
setNewOption("");
233-
setIsOpen(false);
234-
},
235-
[onChangeAction?.callback],
236-
);
237-
238140
const { namedData } = useTemplateData({
239141
data: itemTemplate?.data,
240142
name: itemTemplate?.name,
241143
});
242144

243-
// initialize items from props only once
244-
useEffect(() => {
245-
if (values?.items && !initializedRef.current) {
246-
setItems(values.items);
247-
initializedRef.current = true;
248-
}
249-
}, [values?.items]);
250-
251145
const options = useMemo(() => {
252146
let dropdownOptions: React.ReactNode[] | null = null;
253147

254-
if (items.length > 0) {
255-
const tempOptions = items.map((item) => {
148+
if (values?.items && isArray(values.items)) {
149+
const tempOptions = values.items.map((item) => {
256150
if (item.type === "group") {
257151
// Render a group item with sub-items
258152
return (
@@ -312,7 +206,7 @@ const Dropdown: React.FC<DropdownProps> = (props) => {
312206
}
313207

314208
return dropdownOptions;
315-
}, [items, namedData, itemTemplate]);
209+
}, [values?.items, namedData, itemTemplate]);
316210

317211
const { backgroundColor: _, ...formItemStyles } = values?.styles ?? {};
318212

@@ -409,24 +303,17 @@ const Dropdown: React.FC<DropdownProps> = (props) => {
409303
<div ref={rootRef} style={{ flex: 1, ...formItemStyles }}>
410304
<EnsembleFormItem values={values}>
411305
<Select
412-
key={`${id}-${items.length}`}
413306
allowClear={values?.allowClear ?? true}
414307
className={`${values?.styles?.names || ""} ${id}_input`}
415308
defaultValue={values?.value}
416309
disabled={values?.enabled === false}
417310
dropdownRender={(menu): React.ReactElement =>
418-
DropdownRenderer(
419-
menu,
420-
values?.panel,
421-
newOption,
422-
handleAddNewOption,
423-
)
311+
DropdownRenderer(menu, values?.panel)
424312
}
425313
dropdownStyle={values?.styles}
426314
id={values?.id}
427315
onChange={handleChange}
428316
onDropdownVisibleChange={(state): void => setIsOpen(state)}
429-
onSearch={values?.allowCreateOptions ? handleSearch : undefined}
430317
onSelect={onItemSelectCallback}
431318
open={isOpen}
432319
placeholder={
@@ -437,10 +324,7 @@ const Dropdown: React.FC<DropdownProps> = (props) => {
437324
)
438325
}
439326
optionFilterProp="children"
440-
showSearch={
441-
Boolean(values?.autoComplete) ||
442-
Boolean(values?.allowCreateOptions)
443-
}
327+
showSearch={Boolean(values?.autoComplete)}
444328
value={values?.selectedValue}
445329
>
446330
{options}

packages/runtime/src/widgets/Form/Form.tsx

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
import { Form as AntForm } from "antd";
77
import type { FormProps as AntFormProps } from "antd";
88
import { useCallback, useState } from "react";
9-
import type { FormInstance, FormLayout } from "antd/es/form/Form";
9+
import type { FormLayout } from "antd/es/form/Form";
1010
import { set } from "lodash-es";
1111
import { WidgetRegistry } from "../../registry";
1212
import { EnsembleRuntime } from "../../runtime";
@@ -129,44 +129,3 @@ const getLayout = (labelPosition?: string): FormLayout => {
129129
};
130130

131131
WidgetRegistry.register(widgetName, Form);
132-
133-
/**
134-
* Programmatically update the value of a field and trigger Ant Design's `onFieldsChange`
135-
* (or Ensemble Form's `onChange` action).
136-
*
137-
* Normally, calling `form.setFieldValue` or `form.setFieldsValue` does not fire `onFieldsChange`.
138-
* This helper uses Ant Design's internal `dispatch` mechanism to register the update
139-
* as if it were user-driven, so that `onFieldsChange` fires consistently.
140-
*
141-
* This is especially useful for widgets that update values programmatically
142-
* (e.g. Dropdown with `allowCreateOptions`).
143-
*
144-
* Alternatively, one could use Ant Design's `Form.useWatch`, but this approach
145-
* is less performant for large forms.
146-
*
147-
* ⚠️ Relies on Ant Design's private API (`RC_FORM_INTERNAL_HOOKS`), which may change
148-
* in future versions.
149-
*
150-
* Reference: https://github.com/ant-design/ant-design/issues/23782#issuecomment-2114700558
151-
*/
152-
export function updateFieldValue<Values>(
153-
form: FormInstance<Values>,
154-
name: string,
155-
value: unknown,
156-
): void {
157-
type InternalForm = FormInstance<Values> & {
158-
getInternalHooks: (hook: string) => {
159-
dispatch: (action: {
160-
type: string;
161-
namePath: string[];
162-
value: unknown;
163-
}) => void;
164-
};
165-
};
166-
167-
(form as InternalForm).getInternalHooks("RC_FORM_INTERNAL_HOOKS").dispatch({
168-
type: "updateValue",
169-
namePath: [name],
170-
value,
171-
});
172-
}

0 commit comments

Comments
 (0)