From 0ddd294b12d6d045165c431a4af36a461974a926 Mon Sep 17 00:00:00 2001 From: sagar davara Date: Thu, 23 Jan 2025 20:01:19 +0530 Subject: [PATCH 1/3] added support of dynamic searchKey in seach --- .changeset/fuzzy-houses-notice.md | 6 ++++++ apps/kitchen-sink/src/ensemble/screens/home.yaml | 2 ++ apps/kitchen-sink/src/ensemble/widgets/Header.yaml | 5 +++-- packages/runtime/src/widgets/Search.tsx | 8 ++++---- 4 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 .changeset/fuzzy-houses-notice.md diff --git a/.changeset/fuzzy-houses-notice.md b/.changeset/fuzzy-houses-notice.md new file mode 100644 index 000000000..8d8d4181d --- /dev/null +++ b/.changeset/fuzzy-houses-notice.md @@ -0,0 +1,6 @@ +--- +"@ensembleui/react-kitchen-sink": patch +"@ensembleui/react-runtime": patch +--- + +Added support for dynamic searchKey in search widget diff --git a/apps/kitchen-sink/src/ensemble/screens/home.yaml b/apps/kitchen-sink/src/ensemble/screens/home.yaml index f0d4a9c8a..daede95f9 100644 --- a/apps/kitchen-sink/src/ensemble/screens/home.yaml +++ b/apps/kitchen-sink/src/ensemble/screens/home.yaml @@ -22,6 +22,8 @@ View: header: title: Header: + inputs: + searchKey: id styles: className: topView diff --git a/apps/kitchen-sink/src/ensemble/widgets/Header.yaml b/apps/kitchen-sink/src/ensemble/widgets/Header.yaml index ac3e00b4f..371bfad75 100644 --- a/apps/kitchen-sink/src/ensemble/widgets/Header.yaml +++ b/apps/kitchen-sink/src/ensemble/widgets/Header.yaml @@ -1,8 +1,9 @@ Widget: inputs: - message + - searchKey onLoad: - executeCode: + executeCode: body: | console.log("hello from header", message); onComplete: @@ -42,7 +43,7 @@ Widget: template: Text: text: ${user.firstName + ' ' + user.lastName} - searchKey: id + searchKey: ${searchKey} onSearch: invokeAPI: name: findUsers diff --git a/packages/runtime/src/widgets/Search.tsx b/packages/runtime/src/widgets/Search.tsx index 3b349f752..727db3b94 100644 --- a/packages/runtime/src/widgets/Search.tsx +++ b/packages/runtime/src/widgets/Search.tsx @@ -65,7 +65,7 @@ export const Search: React.FC = ({ }); const { id, rootRef, values } = useRegisterBindings( - { styles, value, ...rest, widgetName, initialValue }, + { styles, value, ...rest, widgetName, initialValue, searchKey }, rest.id, { setValue, @@ -81,12 +81,12 @@ export const Search: React.FC = ({ (option: unknown): string | number => { return get( option, - searchKey - ? [itemTemplate?.name ?? "", searchKey] + values?.searchKey + ? [itemTemplate?.name ?? "", values.searchKey] : [(itemTemplate?.value || itemTemplate?.name) ?? ""], ) as string | number; }, - [itemTemplate?.name, itemTemplate?.value, searchKey], + [itemTemplate?.name, itemTemplate?.value, values?.searchKey], ); const renderOptions = useMemo(() => { From a3b7069d01ed6050d775a3071b58342b9c58d931 Mon Sep 17 00:00:00 2001 From: sagar davara Date: Sat, 25 Jan 2025 00:41:27 +0530 Subject: [PATCH 2/3] fix: test case added --- .../src/widgets/__tests__/Search.test.tsx | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 packages/runtime/src/widgets/__tests__/Search.test.tsx diff --git a/packages/runtime/src/widgets/__tests__/Search.test.tsx b/packages/runtime/src/widgets/__tests__/Search.test.tsx new file mode 100644 index 000000000..cc653ecc5 --- /dev/null +++ b/packages/runtime/src/widgets/__tests__/Search.test.tsx @@ -0,0 +1,56 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import { BrowserRouter } from "react-router-dom"; +import userEvent from "@testing-library/user-event"; +import { EnsembleScreen } from "../../runtime/screen"; +import "../index"; + +describe("Search Widget", () => { + test("searchKey test", async () => { + render( + , + { wrapper: BrowserRouter }, + ); + + await waitFor(() => { + userEvent.type(screen.getByRole("combobox"), "app"); + }); + + await waitFor(() => { + const optionElements = screen.getAllByRole("option"); + optionElements.forEach((option) => { + expect(option).toBeVisible(); + }); + }); + }); +}); From 028e0b06d02d188be3ac980f3e756782b6e003ce Mon Sep 17 00:00:00 2001 From: sagar davara Date: Sat, 25 Jan 2025 01:52:47 +0530 Subject: [PATCH 3/3] fix: handle search when searchKey is not defined --- packages/runtime/src/widgets/Search.tsx | 36 ++++---- .../src/widgets/__tests__/Search.test.tsx | 91 ++++++++++++++++++- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/packages/runtime/src/widgets/Search.tsx b/packages/runtime/src/widgets/Search.tsx index 727db3b94..d5b4c8771 100644 --- a/packages/runtime/src/widgets/Search.tsx +++ b/packages/runtime/src/widgets/Search.tsx @@ -83,7 +83,7 @@ export const Search: React.FC = ({ option, values?.searchKey ? [itemTemplate?.name ?? "", values.searchKey] - : [(itemTemplate?.value || itemTemplate?.name) ?? ""], + : itemTemplate?.value || itemTemplate?.name || "", ) as string | number; }, [itemTemplate?.name, itemTemplate?.value, values?.searchKey], @@ -95,21 +95,25 @@ export const Search: React.FC = ({ let dropdownOptions: JSX.Element[] = []; if (isObject(itemTemplate) && !isEmpty(namedData)) { - const tempOptions = namedData.map((item: unknown, index: number) => { - const optionValue = extractValue(item); - - return ( - - - {EnsembleRuntime.render([itemTemplate.template])} - - - ); - }); + const tempOptions = namedData + .map((item: unknown, index: number) => { + const optionValue = extractValue(item); + + if (!optionValue) return null; + + return ( + + + {EnsembleRuntime.render([itemTemplate.template])} + + + ); + }) + .filter((option) => option !== null) as JSX.Element[]; dropdownOptions = [...dropdownOptions, ...tempOptions]; } diff --git a/packages/runtime/src/widgets/__tests__/Search.test.tsx b/packages/runtime/src/widgets/__tests__/Search.test.tsx index cc653ecc5..f0a1194df 100644 --- a/packages/runtime/src/widgets/__tests__/Search.test.tsx +++ b/packages/runtime/src/widgets/__tests__/Search.test.tsx @@ -6,7 +6,7 @@ import { EnsembleScreen } from "../../runtime/screen"; import "../index"; describe("Search Widget", () => { - test("searchKey test", async () => { + test("dynamic searchKey test", async () => { render( { await waitFor(() => { const optionElements = screen.getAllByRole("option"); + expect(optionElements).toHaveLength(2); optionElements.forEach((option) => { expect(option).toBeVisible(); }); }); }); + + test("static searchKey test", async () => { + render( + , + { wrapper: BrowserRouter }, + ); + + await waitFor(() => { + userEvent.type(screen.getByRole("combobox"), "app"); + }); + + await waitFor(() => { + const optionElements = screen.getAllByRole("option"); + expect(optionElements).toHaveLength(2); + optionElements.forEach((option) => { + expect(option).toBeVisible(); + }); + }); + }); + + test("searchKey test with invalid key", async () => { + render( + , + { wrapper: BrowserRouter }, + ); + + await waitFor(() => { + userEvent.type(screen.getByRole("combobox"), "app"); + }); + + await waitFor(() => { + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + }); + }); });