From 21ba8b29b966092e53a485dd359f0395e77e7162 Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Fri, 17 Jan 2025 19:28:38 +0100 Subject: [PATCH 1/5] feat: add useUpdateEffect hook --- .eslintrc.cjs | 3 +++ src/hooks/index.ts | 1 + src/hooks/useUpdateEffect.ts | 15 +++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 src/hooks/index.ts create mode 100644 src/hooks/useUpdateEffect.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 91c0dade..12e303c7 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -29,5 +29,8 @@ module.exports = { singleReturnOnly: false, }, ], + "react-hooks/exhaustive-deps": ["warn", { + "additionalHooks": "(useUpdateEffect)" + }] }, }; diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 00000000..56dee9b4 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export * from "./useUpdateEffect"; diff --git a/src/hooks/useUpdateEffect.ts b/src/hooks/useUpdateEffect.ts new file mode 100644 index 00000000..7fc766c9 --- /dev/null +++ b/src/hooks/useUpdateEffect.ts @@ -0,0 +1,15 @@ +import { useEffect, useRef } from "react"; + +export const useUpdateEffect: typeof useEffect = (effect, deps) => { + const isFirstMount = useRef(true); + + useEffect(() => { + if (isFirstMount) { + isFirstMount.current = false; + return; + } + + return effect(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); +}; From eb4dfa2284ec9d0521fcf03e98b1fcae47bbd67a Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Fri, 17 Jan 2025 19:28:55 +0100 Subject: [PATCH 2/5] fix(SingleSelect): respect defaultValue --- src/components/Select/SingleSelect.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Select/SingleSelect.tsx b/src/components/Select/SingleSelect.tsx index 6d58cf82..bcae0026 100644 --- a/src/components/Select/SingleSelect.tsx +++ b/src/components/Select/SingleSelect.tsx @@ -1,4 +1,7 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useState } from "react"; + +import { useUpdateEffect } from "@/hooks"; + import { SelectContainerProps, SelectOptionProp, SelectionType } from "./common/types"; import { InternalSelect, SelectGroup, SelectItem } from "./common/InternalSelect"; @@ -65,7 +68,7 @@ export const Select = ({ [selectedValues, onSelect] ); - useEffect(() => { + useUpdateEffect(() => { setSelectedValues(valueProp ? [valueProp] : []); }, [valueProp]); From d6563d4f8c8cdd5d988d315835e7c486aa0b8654 Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Fri, 17 Jan 2025 19:29:07 +0100 Subject: [PATCH 3/5] test(SingleSelect): respect defaultValue --- src/components/Select/SingleSelect.test.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/Select/SingleSelect.test.tsx b/src/components/Select/SingleSelect.test.tsx index d5141d74..3bc96088 100644 --- a/src/components/Select/SingleSelect.test.tsx +++ b/src/components/Select/SingleSelect.test.tsx @@ -115,6 +115,14 @@ describe("Select", () => { expect(queryByTestingText(selectTrigger, "Content0")).not.toBeNull(); }); + it("should respect given defaultValue in select", () => { + const { getByTestId } = renderSelect({ + defaultValue: "content0", + }); + const selectedValue = getByTestId("select-trigger"); + expect(selectedValue.textContent).toBe("Content0"); + }); + it("should render options", () => { const { queryByText, getByTestId } = renderSelect({ options: selectOptions, From 30a42749415c8a0e58bb46e78d0065472c93fa7e Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Fri, 17 Jan 2025 19:29:15 +0100 Subject: [PATCH 4/5] fix(MultiSelect): respect defaultValue --- src/components/Select/MultiSelect.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Select/MultiSelect.tsx b/src/components/Select/MultiSelect.tsx index 820c1a39..37e5e912 100644 --- a/src/components/Select/MultiSelect.tsx +++ b/src/components/Select/MultiSelect.tsx @@ -1,4 +1,6 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useState } from "react"; + +import {useUpdateEffect} from "@/hooks"; import { SelectContainerProps, SelectOptionProp, SelectionType } from "./common/types"; import { @@ -43,7 +45,7 @@ export const MultiSelect = ({ [onOpenChangeProp] ); - useEffect(() => { + useUpdateEffect(() => { setSelectedValues(valueProp ?? []); }, [valueProp]); From 25f1aafdc156485b39db1eb6fa6a12aec8432507 Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Fri, 17 Jan 2025 19:29:24 +0100 Subject: [PATCH 5/5] test(MultiSelect): respect defaultValue --- src/components/Select/MultiSelect.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/Select/MultiSelect.test.tsx b/src/components/Select/MultiSelect.test.tsx index 489d2145..65b8828f 100644 --- a/src/components/Select/MultiSelect.test.tsx +++ b/src/components/Select/MultiSelect.test.tsx @@ -83,6 +83,16 @@ describe("MultiSelect", () => { expect(queryByTestingText(selectTrigger, "Content3")).toBeNull(); }); + it("should respect given defaultValue in select", () => { + const { getByTestId } = renderSelect({ + defaultValue: ["content0", "content2"], + }); + const selectTrigger = getByTestId("select-trigger"); + expect(selectTrigger).not.toBeNull(); + expect(queryByTestingText(selectTrigger, "Content0")).not.toBeNull(); + expect(queryByTestingText(selectTrigger, "Content2")).not.toBeNull(); + }); + it("should show error", () => { const { queryByText } = renderSelect({ error: "Select Error",