Skip to content

Commit

Permalink
chore: cascader updates (#378)
Browse files Browse the repository at this point in the history
Co-authored-by: Gary Kaganas <[email protected]>
Co-authored-by: Gary Kaganas <[email protected]>
Co-authored-by: Gabriel Tibúrcio <[email protected]>
Co-authored-by: mparticle-automation <[email protected]>
  • Loading branch information
5 people authored Sep 17, 2024
1 parent a99793f commit 6250652
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 29 deletions.
34 changes: 33 additions & 1 deletion src/components/data-entry/QueryItem/Cascader.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Meta, type StoryObj } from '@storybook/react'
import { type IQueryItemCascaderProps, QueryItem } from 'src/components'
import { Flex, Icon, type IQueryItemCascaderProps, QueryItem, Typography } from 'src/components'

const options: IQueryItemCascaderProps['options'] = [
{
Expand Down Expand Up @@ -151,3 +151,35 @@ export const Loading: Story = {
},
},
}

export const SearchLabel: Story = {
args: {
options: [
...options,
{
value: 'G',
label: 'G',
children: [
{
value: 'H',
label: 'H',
children: [
{
value: 'IJK',
label: (
<>
<Flex>
<Icon name="alicorn" />
<Typography.Text>IJK</Typography.Text>
</Flex>
</>
),
searchLabel: 'IJK',
},
],
},
],
},
],
},
}
60 changes: 32 additions & 28 deletions src/components/data-entry/QueryItem/Cascader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import './query-item.css'
import { type GetProp } from 'antd'
import { type DefaultOptionType } from 'antd/es/select'
import { type ReactNode, useCallback, useEffect, useState } from 'react'
import {
Cascader as BaseCascader,
Expand All @@ -15,9 +16,10 @@ import { type Icons } from 'src/constants/Icons'
import { useMount } from 'src/hooks/useMount'
import { debounce } from 'src/utils/utils'

export interface ICascaderOption {
export interface ICascaderOption extends DefaultOptionType {
value: string
label: string
label: ReactNode
searchLabel?: string // useful when label is a reactNode and not a string
children?: ICascaderOption[]
disabled?: boolean
}
Expand All @@ -31,18 +33,20 @@ export interface IQueryItemCascaderProps {
loadData?: (value: string) => Promise<void>
value?: Array<number | string>
disabled?: boolean
placement?: IBaseCascaderProps['placement']
defaultOpen?: IBaseCascaderProps['defaultOpen']
}

const Cascader = (props: IQueryItemCascaderProps) => {
type DefaultOptionType = GetProp<IBaseCascaderProps, 'options'>[number]
type ICascaderOption = GetProp<IBaseCascaderProps, 'options'>[number]

const options: ICascaderOption[] = []
const [items, setItems] = useState(props.options ?? options)
const [searchValue, setSearchValue] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [selectedValue, setSelectedValue] = useState<Array<number | string>>(props.value ?? [])
const [selectedDisplayValue, setSelectedDisplayValue] = useState(
props.value && props.value.length > 0 ? (props.value.slice(-1)[0] as any).label : '',
const [selectedOption, setSelectedOption] = useState<ICascaderOption>(
props.value?.length ? props.value.slice(-1)[0] : undefined,
)
const [isOpen, setIsOpen] = useState(false)

Expand Down Expand Up @@ -79,9 +83,11 @@ const Cascader = (props: IQueryItemCascaderProps) => {
setSearchValue(value)
}

const filter = (inputValue: string, path: DefaultOptionType[]) => {
return path.some(option => (option.label as string).toLowerCase().includes(inputValue.toLowerCase()))
}
const getSearchValue = (option: ICascaderOption): string =>
option.searchLabel ?? (typeof option.label === 'string' ? (option.label as string) : '')

const filter = (inputValue: string, path: ICascaderOption[]) =>
path.some(option => getSearchValue(option).toLowerCase().includes(inputValue.toLowerCase()))

let debouncedLoadData: (value: string) => void
if (props.loadData) {
Expand All @@ -98,9 +104,11 @@ const Cascader = (props: IQueryItemCascaderProps) => {
searchValue,
disabled: props.disabled,
value: selectedValue,
defaultOpen: props.defaultOpen,
placement: props.placement ?? 'bottomLeft',
onChange: (values: Array<number | string>, selectedOptions: any): void => {
setSelectedValue(values as string[])
setSelectedDisplayValue(selectedOptions.slice(-1)[0].label)
setSelectedOption(selectedOptions.slice(-1)[0])
void props.onChange?.(values, selectedOptions)
},
dropdownRender: menu => (
Expand All @@ -111,6 +119,7 @@ const Cascader = (props: IQueryItemCascaderProps) => {
<>
<Input
allowClear
autoFocus
value={searchValue}
className="query-item__input-search"
placeholder="Search"
Expand All @@ -125,18 +134,16 @@ const Cascader = (props: IQueryItemCascaderProps) => {
),
showSearch: {
filter,
render: (inputValue: string, paths: ICascaderOption[]): ReactNode => {
return (
<>
{paths.map((path: ICascaderOption, index) => (
<>
{highlightMatches(path.label, inputValue.toLowerCase())}
{index < paths.length - 1 ? ' > ' : ''}
</>
))}
</>
)
},
render: (inputValue: string, options: ICascaderOption[]): ReactNode => (
<>
{options.map((option: ICascaderOption, index) => (
<>
{highlightMatches(getSearchValue(option), inputValue.toLowerCase())}
{index < options.length - 1 ? ' > ' : ''}
</>
))}
</>
),
},
options: items,
onDropdownVisibleChange: value => {
Expand All @@ -149,7 +156,7 @@ const Cascader = (props: IQueryItemCascaderProps) => {
if (isOpen) inputClasses += ' query-item--open'
if (selectedValue && selectedValue.length !== 0) inputClasses += ' query-item--selected'
if (props.errorMessage) inputClasses += ' query-item--error'
const displayValue = selectedDisplayValue ?? selectedValue.slice(-1)
const displayValue = (selectedOption ? getSearchValue(selectedOption) : selectedValue.slice(-1)) as string

return (
<>
Expand All @@ -164,7 +171,7 @@ const Cascader = (props: IQueryItemCascaderProps) => {
prefix={getIcon()}
/>
</BaseCascader>
{props.errorMessage && <Typography.Text type={'danger'}>{props.errorMessage}</Typography.Text>}
{props.errorMessage && <Typography.Text type="danger">{props.errorMessage}</Typography.Text>}
</>
)

Expand All @@ -189,11 +196,8 @@ const Cascader = (props: IQueryItemCascaderProps) => {
)
}

function transformOptionsToPaths(
options: DefaultOptionType[],
prefixPath: DefaultOptionType[],
): DefaultOptionType[][] {
let result: DefaultOptionType[][] = []
function transformOptionsToPaths(options: ICascaderOption[], prefixPath: ICascaderOption[]): ICascaderOption[][] {
let result: ICascaderOption[][] = []
options.forEach(option => {
if (option.children && option.children.length > 0) {
const newPrefix = prefixPath.concat([{ label: option.label, value: option.value }])
Expand Down

0 comments on commit 6250652

Please sign in to comment.