From 72c5daf2184af0e0a3ab9c7b807f0b98fcec9167 Mon Sep 17 00:00:00 2001 From: Bilta <64406758+bilta-keyvalue@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:12:31 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20Added=20new=20props=20to=20control=20cl?= =?UTF-8?q?ear=20search=20icon,=20support=20for=20icons=20as=20js=E2=80=A6?= =?UTF-8?q?=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 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 | 43 ++++++++------- src/lib/multi-select/searchComponent.tsx | 37 +++++++++---- src/lib/multi-select/styles.module.scss | 10 ++++ src/lib/multi-select/types.d.ts | 19 ++++--- src/lib/multi-select/utils/utils.ts | 3 +- src/stories/Component.stories.tsx | 69 ++++++++++++++++-------- 10 files changed, 166 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index d42e9ae..32744ee 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..0b2d3e8 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 showDefault = 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..24ed5a1 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); } @@ -131,15 +132,18 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => { [options, selectedIds] ); + const onClick = (): void =>{ + setShowAllChips(true); + setIsModalVisible(true); + } + return (
{ - setShowAllChips(true); - }} + onClick={onClick} role="presentation" >
@@ -161,11 +165,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..bd1dec7 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..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%; @@ -15,6 +16,8 @@ font-size: inherit; font-family: "Poppins"; background-color: inherit; + text-overflow: ellipsis; + min-width: 25px; } .icon { width: 16px; @@ -87,6 +90,9 @@ opacity: 0.7; } } +.selectedItem{ + background-color: #D9E2F0; +} .checkbox { width: 20px; height: 20px; @@ -118,6 +124,7 @@ display: flex; flex-wrap: wrap; gap: 10px; + flex: 1 } .error { color: #ff0000; @@ -137,3 +144,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..8a19ff4 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..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({}); @@ -142,6 +142,7 @@ LimitingNumberOfVisibleChips.args = { hideSelected: false, hasError: false, thresholdForBubble: 2, + showCheckbox: false }; export const WithHelperText = Template.bind({}); @@ -303,6 +304,30 @@ WithLoadingBar.args = { name: "Read books", }, ], + hideSearch: true, onSearch: undefined, isLoading: true, }; + + +export const HiddenSearchBar = Template.bind({}); +HiddenSearchBar.args = { + options: defaultOptions, + hideSearch: true, + onSearch: undefined, + isLoading: true, +}; + + + +export const IconsAsJSXElement = Template.bind({}); +IconsAsJSXElement.args = { + options: defaultOptions, + icons:{ + Arrow:
+ } +};