From 6d8723881c36d2c10c9943adc6011111c6e7ee7a Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Sat, 24 Feb 2024 14:30:47 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20make=20the=20selec?= =?UTF-8?q?t=20to=20be=20controller=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/shared/Select/Select.tsx | 47 +- .../frontend/src/pages/components/index.tsx | 4 +- .../src/pages/components/renders/dropdown.tsx | 509 +++++++++++------- 3 files changed, 348 insertions(+), 212 deletions(-) diff --git a/packages/frontend/src/components/shared/Select/Select.tsx b/packages/frontend/src/components/shared/Select/Select.tsx index d3bfa5a5..eface8e7 100644 --- a/packages/frontend/src/components/shared/Select/Select.tsx +++ b/packages/frontend/src/components/shared/Select/Select.tsx @@ -53,7 +53,10 @@ export type SelectOption = { export type SelectOrientation = 'horizontal' | 'vertical'; interface SelectProps - extends Omit, 'size'>, + extends Omit< + ComponentPropsWithoutRef<'input'>, + 'size' | 'value' | 'onChange' + >, SelectTheme { /** * The options of the select @@ -95,6 +98,18 @@ interface SelectProps * Show the values of the select if it's multiple */ hideValues?: boolean; + /** + * The value of the select + */ + value?: SelectOption | SelectOption[]; + /** + * Callback function when reset the select + */ + onClear?: () => void; + /** + * Callback function when the value of the select changes + */ + onChange?: (value: SelectOption | SelectOption[]) => void; } export const Select = ({ @@ -113,6 +128,9 @@ export const Select = ({ helperText, hideValues = false, placeholder: placeholderProp = 'Select an option', + value, + onChange, + onClear, }: SelectProps) => { const theme = selectTheme({ size, error, variant, orientation }); @@ -148,9 +166,26 @@ export const Select = ({ } }, [dropdownOpen]); // Re-calculate whenever the dropdown is opened + useEffect(() => { + // If multiple selection is enabled, ensure the internal state is an array + if (multiple) { + if (Array.isArray(value)) { + // Directly use the provided array + setSelectedItems(value); + } else { + // Reset or set to empty array if the value is not an array + setSelectedItems([]); + } + } else { + // For single selection, directly set the selected item + setSelectedItem(value as SelectOption); + } + }, [value, multiple]); + const handleSelectedItemChange = (selectedItem: SelectOption | null) => { setSelectedItem(selectedItem); setInputValue(selectedItem ? selectedItem.label : ''); + onChange?.(selectedItem as SelectOption); }; const { @@ -159,6 +194,7 @@ export const Select = ({ addSelectedItem, removeSelectedItem, selectedItems, + setSelectedItems, reset, } = useMultipleSelection({ onSelectedItemsChange: multiple @@ -208,9 +244,17 @@ export const Select = ({ // If the item is not already selected, add it to the selected items if (!selectedItems.includes(selectedItem)) { addSelectedItem(selectedItem); + // Callback for `onChange` + const newSelectedItems = [...selectedItems, selectedItem]; + onChange?.(newSelectedItems); } else { // If the item is already selected, remove it from the selected items removeSelectedItem(selectedItem); + // Callback for `onChange` + const newSelectedItems = selectedItems.filter( + (item) => selectedItem !== item, + ); + onChange?.(newSelectedItems); } setInputValue(''); } @@ -237,6 +281,7 @@ export const Select = ({ reset(); setSelectedItem(null); setInputValue(''); + onClear?.(); }; const renderLabels = useMemo( diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx index e2cd964e..2171581f 100644 --- a/packages/frontend/src/pages/components/index.tsx +++ b/packages/frontend/src/pages/components/index.tsx @@ -31,7 +31,7 @@ import { import { renderDefaultTag, renderMinimalTag } from './renders/tag'; import { renderToast, renderToastsWithCta } from './renders/toast'; import { renderTooltips } from './renders/tooltip'; -import { renderDropdowns } from './renders/dropdown'; +import { DropdownExample } from './renders/dropdown'; type ValuePiece = Date | null; type Value = ValuePiece | [ValuePiece, ValuePiece]; @@ -274,7 +274,7 @@ const Page: React.FC = () => { {/* Dropdown */}

Dropdown / Select

- {renderDropdowns()} + {}
diff --git a/packages/frontend/src/pages/components/renders/dropdown.tsx b/packages/frontend/src/pages/components/renders/dropdown.tsx index 127cc4f9..d4ccf26d 100644 --- a/packages/frontend/src/pages/components/renders/dropdown.tsx +++ b/packages/frontend/src/pages/components/renders/dropdown.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { PencilIcon } from 'components/shared/CustomIcon'; import { SelectOption, Select } from 'components/shared/Select'; @@ -30,211 +30,302 @@ export const DROPDOWN_ITEMS: SelectOption[] = [ }, ]; -export const renderDropdowns = () => ( - <> -

Single – Small

-
- - -
-

Single – Medium

-
- - -
-

Multiple – Small

-
- - - - - -
-

- Single – With label, description, and helper text -

-
- - -
-

- Multiple – With label, description, and helper text -

-
- - -
-

- Error – With label, description, and helper text -

-
- - -
- -); +export const DropdownExample = () => { + const [singleValue, setSingleValue] = useState(); + const [multipleValue, setMultipleValue] = useState([]); + + const handleSelect = ( + type: 'single' | 'multiple', + value: SelectOption | SelectOption[], + ) => { + if (type === 'single') { + setSingleValue(value as SelectOption); + } else { + setMultipleValue(value as SelectOption[]); + } + }; + + return ( + <> +

Single – Small

+
+ handleSelect('single', value)} + /> + handleSelect('single', value)} + /> +
+

Single – Medium

+
+ handleSelect('single', value)} + /> + handleSelect('single', value)} + /> +
+

+ Multiple – Small +

+
+ handleSelect('multiple', value)} + /> + handleSelect('multiple', value)} + /> + handleSelect('multiple', value)} + /> + handleSelect('multiple', value)} + /> + handleSelect('multiple', value)} + /> +
+

+ Single – With label, description, and helper text +

+
+ handleSelect('single', value)} + /> + handleSelect('single', value)} + /> +
+

+ Multiple – With label, description, and helper text +

+
+ handleSelect('multiple', value)} + /> + handleSelect('multiple', value)} + /> +
+

+ Error – With label, description, and helper text +

+
+ + +
+ + ); +};