From ebbb8bf95cd2204ca025534a20fb5936fc1ef408 Mon Sep 17 00:00:00 2001 From: Bilta John Date: Wed, 20 Dec 2023 23:11:07 +0530 Subject: [PATCH 1/6] Added new props to control clear search icon, support for icons as jsx, bug fixesrun coverage --- README.md | 21 ++++++++++---- src/lib/multi-select/chips.tsx | 16 +++++----- src/lib/multi-select/constants.ts | 1 + src/lib/multi-select/menuItems.tsx | 30 +++++++++++-------- src/lib/multi-select/multiSelect.tsx | 34 ++++++++++++---------- src/lib/multi-select/searchComponent.tsx | 37 +++++++++++++++++------- src/lib/multi-select/styles.module.scss | 7 +++++ src/lib/multi-select/types.d.ts | 19 +++++++----- src/lib/multi-select/utils/utils.ts | 3 +- src/stories/Component.stories.tsx | 2 ++ 10 files changed, 112 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index d42e9ae..49c46be 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,13 @@ Provides you with an object to replace the default icons used. undefined + +clearSearchClick?: function + +The callback function which will be triggered on clicking close icon inside search box + +undefined + @@ -257,6 +264,7 @@ the below code shows all the overridable styles: SearchIcon?: {...styles}, ArrowIcon?: {...styles}, HiddenChipsIndicator?: {...styles}, + ClearSearchIcon?: {....styles}, SelectedMenuItem?: (id) => ({...styles}), UnSelectedMenuItem?: (id) => ({...styles}), ChipComponent?: (id) => ({...styles}), @@ -270,9 +278,10 @@ To customize the style of various components, you can use the following prop nam - `Container`: Overrides the style of the multi-selection UI container. - `CheckedIcon`: Overrides the style of the checked icon. - `ChipCloseIcon`: Overrides the style of the close icon within the chip. +- `ClearSearchIcon`: Overrides the style of the close icon within the search box. - `HelperText`: Overrides the style of the helper text. - `HiddenChipsIndicator`: Overrides the style of the bubble indicating the number of hidden chips if the thresholdForBubble prop has a value. -- `InputBox`: Overrides the style of the box containing the chips and search bar. +- `InputBox`: Overrides the style of the box containing the chips and search bar. Can be used to style the placeholder if the search is hidden. - `SearchIcon`: Overrides the style of the search icon. - `SearchComponent`: Overrides the styles of the search component. - `UnCheckedIcon`: Overrides the style of the unchecked box. @@ -292,10 +301,11 @@ The following code displays the icons that can be customized ``` @@ -303,4 +313,5 @@ The following code displays the icons that can be customized - `Arrow` - Overrides the down arrow(right) - `ChipClose` - Overrides the chip close icon - `Checked` - Overrides the checkbox checked icon +- `ClearSearch` - Overrides the close icon inside search box - `Search` - Overrides the search icon diff --git a/src/lib/multi-select/chips.tsx b/src/lib/multi-select/chips.tsx index ce80703..291ef3f 100644 --- a/src/lib/multi-select/chips.tsx +++ b/src/lib/multi-select/chips.tsx @@ -2,7 +2,7 @@ import React, { MouseEvent, useMemo } from "react"; import closeIcon from "../../assets/x.svg"; import { ChipListPropType, OptionType } from "./types"; import { Elements, ElementsWithCallableStyle } from "./constants"; -import { getStyles } from "./utils/utils"; +import { getStyles, renderAsImage } from "./utils/utils"; import classes from "./styles.module.scss"; const Chips = (props: ChipListPropType): JSX.Element => { @@ -50,12 +50,14 @@ const Chips = (props: ChipListPropType): JSX.Element => { onClick(e, item.id) } > - + {renderAsImage(icon) ? + : + icon} ) diff --git a/src/lib/multi-select/constants.ts b/src/lib/multi-select/constants.ts index 9759e1e..cfbde06 100644 --- a/src/lib/multi-select/constants.ts +++ b/src/lib/multi-select/constants.ts @@ -15,6 +15,7 @@ export enum Elements { SearchIcon = "SearchIcon", ArrowIcon = "ArrowIcon", HiddenChipsIndicator = "HiddenChipsIndicator", + ClearSearchIcon = "ClearSearchIcon" } export enum ElementsWithCallableStyle { diff --git a/src/lib/multi-select/menuItems.tsx b/src/lib/multi-select/menuItems.tsx index 9a8e3f8..30ae6b4 100644 --- a/src/lib/multi-select/menuItems.tsx +++ b/src/lib/multi-select/menuItems.tsx @@ -1,5 +1,6 @@ -import React from "react"; -import { getStyles } from "./utils/utils"; +import React, { useMemo } from "react"; +import CheckMark from "../../assets/CheckBox.svg"; +import { getStyles, renderAsImage } from "./utils/utils"; import { ModalProps, OptionType } from "./types"; import { DEFAULT_EMPTY_LIST_MESSAGE, @@ -22,6 +23,9 @@ const OptionListingModal = (props: ModalProps): JSX.Element => { onOptionClick, styles = {} } = props; + + const renderOwnComponent = useMemo(()=> renderAsImage(icon), [icon]); + return ( <> {list?.length && (list.length !== selectedIds.length || !hideSelected) @@ -31,7 +35,7 @@ const OptionListingModal = (props: ModalProps): JSX.Element => { !hideSelected) && ( - )) || <> + )) || ) : !isLoading && (renderEmptyItem || ( diff --git a/src/lib/multi-select/multiSelect.tsx b/src/lib/multi-select/multiSelect.tsx index fc0dbc7..f40d5ef 100644 --- a/src/lib/multi-select/multiSelect.tsx +++ b/src/lib/multi-select/multiSelect.tsx @@ -1,9 +1,9 @@ import React, { MouseEvent, useEffect, useMemo, useRef, useState } from "react"; import DownArrow from "../../assets/DropdownArrow.svg"; -import CheckMark from "../../assets/CheckBox.svg"; import SearchComponent from "./searchComponent"; import { MultiSelectPropType, OptionType } from "./types"; import { DEFAULT_PLACEHOLDER, Elements } from "./constants"; +import { renderAsImage } from "./utils/utils"; import classes from "./styles.module.scss"; import Chips from "./chips"; import MenuListing from "./menuItems"; @@ -28,11 +28,12 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => { icons = {}, onSearch = undefined, onItemClick = undefined, - setSelectedValues = undefined + setSelectedValues = undefined, + clearSearchClick = undefined } = props; - const { Checked = CheckMark, Search, ChipClose, Arrow } = icons; - + const { Checked, Search, ChipClose, Arrow, ClearSearch } = icons; + const arrowIcon = Arrow; // to show/hide div containing the checkboxes const [isModalVisible, setIsModalVisible] = useState(false); const [list, setList] = useState([]); @@ -55,7 +56,7 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => { }, [options]); useEffect(() => { - if (typeof document!== undefined) { + if (typeof document!== 'undefined') { document.addEventListener("mouseup", onMouseUp); return () => document.removeEventListener("mouseup", onMouseUp); } @@ -161,11 +162,12 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => { onFocus={triggerModalOpen} ref={inputRef} icon={Search} + onCloseClick={clearSearchClick} + closeIcon={ClearSearch} /> )} {hideSearch && !selectedIds.length && ( - // same style for the search box is used -
{placeholder}
+
{placeholder}
)} {!isModalVisible && helperText && ( diff --git a/src/lib/multi-select/searchComponent.tsx b/src/lib/multi-select/searchComponent.tsx index da5e548..ea84269 100644 --- a/src/lib/multi-select/searchComponent.tsx +++ b/src/lib/multi-select/searchComponent.tsx @@ -1,6 +1,7 @@ import React, { ForwardedRef, forwardRef, useEffect, useState } from "react"; import searchIcon from "../../assets/Search.svg"; -import closeIcon from "../../assets/x-circle.svg"; +import defaultCloseIcon from "../../assets/x-circle.svg"; +import { renderAsImage } from './utils/utils' import { SearchComponentPropType } from "./types"; import { Elements } from "./constants"; import classes from "./styles.module.scss"; @@ -9,24 +10,30 @@ const SearchComponent = ( props: SearchComponentPropType, ref: ForwardedRef ): JSX.Element => { - const { onSearch, searchPlaceholder, styles = {}, onFocus, icon } = props; + const { onSearch, searchPlaceholder, styles = {}, onFocus, icon, onCloseClick, closeIcon } = props; const [searchTerm, setSearchTerm] = useState(""); useEffect(() => { onSearch(searchTerm); }, [searchTerm]); + const onCloseButtonClick = (): void =>{ + setSearchTerm(""); + if(onCloseClick) onCloseClick(); + } + return (
- + {renderAsImage(icon)? + : icon} setSearchTerm(e.target.value)} @@ -41,9 +48,17 @@ const SearchComponent = ( )}
); diff --git a/src/lib/multi-select/styles.module.scss b/src/lib/multi-select/styles.module.scss index 5f024ae..111ae6d 100644 --- a/src/lib/multi-select/styles.module.scss +++ b/src/lib/multi-select/styles.module.scss @@ -15,6 +15,7 @@ font-size: inherit; font-family: "Poppins"; background-color: inherit; + text-overflow: ellipsis; } .icon { width: 16px; @@ -87,6 +88,9 @@ opacity: 0.7; } } +.selectedItem{ + background-color: #D9E2F0; +} .checkbox { width: 20px; height: 20px; @@ -137,3 +141,6 @@ .elevatedContent { z-index: 1; } +.label { + padding: 10px +} diff --git a/src/lib/multi-select/types.d.ts b/src/lib/multi-select/types.d.ts index c50cbb5..439e9f8 100644 --- a/src/lib/multi-select/types.d.ts +++ b/src/lib/multi-select/types.d.ts @@ -18,6 +18,7 @@ export interface MultiSelectPropType { onSearch?: (value: string) => void; onItemClick?: (id: string | number) => void; setSelectedValues?: (ids: Array) => void; + clearSearchClick?: () => void; } export type OptionType = { @@ -30,15 +31,17 @@ export type SearchComponentPropType = { searchPlaceholder: string; styles?: StyleProp; showChips?: boolean; - icon?: string; + icon?: string | JSX.Element; onFocus: () => void; onSearch: (value: string) => void; + onCloseClick?:() => void, + closeIcon?: string | JSX.Element }; export interface ChipListPropType { list: OptionType[]; styles?: StyleProp; - icon?: string; + icon?: string | JSX.Element; onClick: (event: MouseEvent, id: string | number) => void; thresholdForBubble?: number; showAllChips: boolean; @@ -58,13 +61,15 @@ export type StyleProp = { SearchIcon?: object; ArrowIcon?: object; HiddenChipsIndicator?: object; + ClearSearchIcon?: object; }; export type IconsProps = { - Search?: string; - ChipClose?: string; - Checked?: string; - Arrow?: string; + Search?: string | JSX.Element; + ChipClose?: string | JSX.Element; + Checked?: string | JSX.Element; + Arrow?: string | JSX.Element; + ClearSearch?: string | JSX.Element; }; export type ModalProps = { @@ -72,7 +77,7 @@ export type ModalProps = { selectedIds: (string | number)[]; hideSelected: boolean; showCheckbox: boolean; - icon: string; + icon?: string | JSX.Element; isLoading: boolean; renderEmptyItem?: JSX.Element; renderLoader?: JSX.Element; diff --git a/src/lib/multi-select/utils/utils.ts b/src/lib/multi-select/utils/utils.ts index 292dc63..e9e9997 100644 --- a/src/lib/multi-select/utils/utils.ts +++ b/src/lib/multi-select/utils/utils.ts @@ -6,10 +6,11 @@ export const getStyles = ( styles: StyleProp, id: string | number ): object => { - // id will be available for styles given to user as functions const getElementStyle = styles[element]; if (getElementStyle) { return getElementStyle(id); } return {}; }; + +export const renderAsImage = (icon?: JSX.Element | string): boolean => typeof icon === 'undefined' || typeof icon === 'string' \ No newline at end of file diff --git a/src/stories/Component.stories.tsx b/src/stories/Component.stories.tsx index dccee9f..948d8e9 100644 --- a/src/stories/Component.stories.tsx +++ b/src/stories/Component.stories.tsx @@ -142,6 +142,7 @@ LimitingNumberOfVisibleChips.args = { hideSelected: false, hasError: false, thresholdForBubble: 2, + showCheckbox: false }; export const WithHelperText = Template.bind({}); @@ -303,6 +304,7 @@ WithLoadingBar.args = { name: "Read books", }, ], + hideSearch: true, onSearch: undefined, isLoading: true, }; From 5c2e884c6ae7cd0daec7f4e11a36cb87ef428e0e Mon Sep 17 00:00:00 2001 From: Bilta John Date: Thu, 21 Dec 2023 14:17:44 +0530 Subject: [PATCH 2/6] fixed PR comments, increased the intractable area --- README.md | 2 +- src/lib/multi-select/menuItems.tsx | 4 ++-- src/lib/multi-select/multiSelect.tsx | 9 ++++++--- src/stories/Component.stories.tsx | 30 ++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 49c46be..5a40e23 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ the below code shows all the overridable styles: SearchIcon?: {...styles}, ArrowIcon?: {...styles}, HiddenChipsIndicator?: {...styles}, - ClearSearchIcon?: {....styles}, + ClearSearchIcon?: {...styles}, SelectedMenuItem?: (id) => ({...styles}), UnSelectedMenuItem?: (id) => ({...styles}), ChipComponent?: (id) => ({...styles}), diff --git a/src/lib/multi-select/menuItems.tsx b/src/lib/multi-select/menuItems.tsx index 30ae6b4..0b2d3e8 100644 --- a/src/lib/multi-select/menuItems.tsx +++ b/src/lib/multi-select/menuItems.tsx @@ -24,7 +24,7 @@ const OptionListingModal = (props: ModalProps): JSX.Element => { styles = {} } = props; - const renderOwnComponent = useMemo(()=> renderAsImage(icon), [icon]); + const showDefault = useMemo(()=> renderAsImage(icon), [icon]); return ( <> @@ -49,7 +49,7 @@ const OptionListingModal = (props: ModalProps): JSX.Element => { > {showCheckbox && (selectedIds.includes(item.id) ? ( - renderOwnComponent ? + showDefault ?
{ [options, selectedIds] ); + const onClick = (): void =>{ + setShowAllChips(true); + setIsModalVisible(true); + } + return (
{ - setShowAllChips(true); - }} + onClick={onClick} role="presentation" >
diff --git a/src/stories/Component.stories.tsx b/src/stories/Component.stories.tsx index 948d8e9..b8ca45d 100644 --- a/src/stories/Component.stories.tsx +++ b/src/stories/Component.stories.tsx @@ -308,3 +308,33 @@ WithLoadingBar.args = { onSearch: undefined, isLoading: true, }; + + +export const HiddenSearchBar = Template.bind({}); +HiddenSearchBar.args = { + options: [ + { + id: 1, + name: "Upload new pictures", + }, + { + id: 2, + name: "Pay rent", + }, + { + id: 3, + name: "Go Shopping", + }, + { + id: 4, + name: "Call friends", + }, + { + id: 5, + name: "Read books", + }, + ], + hideSearch: true, + onSearch: undefined, + isLoading: true, +}; From 6151d1d5a57f28661c271d269bf83b32c2423c74 Mon Sep 17 00:00:00 2001 From: Bilta John Date: Thu, 21 Dec 2023 14:41:41 +0530 Subject: [PATCH 3/6] Added new story --- src/stories/Component.stories.tsx | 81 ++++++++++++++----------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/src/stories/Component.stories.tsx b/src/stories/Component.stories.tsx index b8ca45d..d97ded5 100644 --- a/src/stories/Component.stories.tsx +++ b/src/stories/Component.stories.tsx @@ -15,30 +15,30 @@ const Template: ComponentStory = (props) => ( ); +const defaultOptions =[{ + id: 1, + name: "Upload new pictures", + }, + { + id: 2, + name: "Pay rent", + }, + { + id: 3, + name: "Go Shopping", + }, + { + id: 4, + name: "Call friends", + }, + { + id: 5, + name: "Read books", + }]; + export const WithDefaultProps = Template.bind({}); WithDefaultProps.args = { - options: [ - { - id: 1, - name: "Upload new pictures", - }, - { - id: 2, - name: "Pay rent", - }, - { - id: 3, - name: "Go Shopping", - }, - { - id: 4, - name: "Call friends", - }, - { - id: 5, - name: "Read books", - }, - ], + options: defaultOptions, }; export const WithOptionalProps = Template.bind({}); @@ -312,29 +312,22 @@ WithLoadingBar.args = { export const HiddenSearchBar = Template.bind({}); HiddenSearchBar.args = { - options: [ - { - id: 1, - name: "Upload new pictures", - }, - { - id: 2, - name: "Pay rent", - }, - { - id: 3, - name: "Go Shopping", - }, - { - id: 4, - name: "Call friends", - }, - { - id: 5, - name: "Read books", - }, - ], + options: defaultOptions, hideSearch: true, onSearch: undefined, isLoading: true, }; + + + +export const IconsAsJSXElement = Template.bind({}); +IconsAsJSXElement.args = { + options: defaultOptions, + icons:{ + Arrow:
+ } +}; From 0cef7d20e8b77828831fc07bb04ab7988d246352 Mon Sep 17 00:00:00 2001 From: Bilta John Date: Thu, 21 Dec 2023 14:46:21 +0530 Subject: [PATCH 4/6] typo fix --- src/lib/multi-select/types.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/multi-select/types.d.ts b/src/lib/multi-select/types.d.ts index 439e9f8..8a19ff4 100644 --- a/src/lib/multi-select/types.d.ts +++ b/src/lib/multi-select/types.d.ts @@ -34,7 +34,7 @@ export type SearchComponentPropType = { icon?: string | JSX.Element; onFocus: () => void; onSearch: (value: string) => void; - onCloseClick?:() => void, + onCloseClick?:() => void; closeIcon?: string | JSX.Element }; From 61d54286d55bc80b8acfa8b24828870b9dc95c5a Mon Sep 17 00:00:00 2001 From: Bilta John Date: Thu, 21 Dec 2023 16:23:54 +0530 Subject: [PATCH 5/6] fix: Added a style fix to grow the search box size to fit the available space --- src/lib/multi-select/styles.module.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/multi-select/styles.module.scss b/src/lib/multi-select/styles.module.scss index 111ae6d..747e6a9 100644 --- a/src/lib/multi-select/styles.module.scss +++ b/src/lib/multi-select/styles.module.scss @@ -6,6 +6,7 @@ padding: 0px 12px; align-items: center; position: relative; + flex: 1 } .searchInput { width: 100%; @@ -16,6 +17,7 @@ font-family: "Poppins"; background-color: inherit; text-overflow: ellipsis; + min-width: 25px; } .icon { width: 16px; @@ -122,6 +124,7 @@ display: flex; flex-wrap: wrap; gap: 10px; + flex: 1 } .error { color: #ff0000; From 4d51e4b493b8d467886f2d511288241320b82c1a Mon Sep 17 00:00:00 2001 From: Bilta John Date: Fri, 22 Dec 2023 10:07:08 +0530 Subject: [PATCH 6/6] Reordered items in read me --- README.md | 6 +++--- src/lib/multi-select/searchComponent.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5a40e23..32744ee 100644 --- a/README.md +++ b/README.md @@ -301,11 +301,11 @@ The following code displays the icons that can be customized ``` diff --git a/src/lib/multi-select/searchComponent.tsx b/src/lib/multi-select/searchComponent.tsx index ea84269..bd1dec7 100644 --- a/src/lib/multi-select/searchComponent.tsx +++ b/src/lib/multi-select/searchComponent.tsx @@ -17,7 +17,7 @@ const SearchComponent = ( onSearch(searchTerm); }, [searchTerm]); - const onCloseButtonClick = (): void =>{ + const onCloseButtonClick = (): void => { setSearchTerm(""); if(onCloseClick) onCloseClick(); }