diff --git a/src/components/data-entry/QueryItem/Cascader.stories.tsx b/src/components/data-entry/QueryItem/Cascader.stories.tsx
index e46f39077..5d6d42dbe 100644
--- a/src/components/data-entry/QueryItem/Cascader.stories.tsx
+++ b/src/components/data-entry/QueryItem/Cascader.stories.tsx
@@ -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'] = [
{
@@ -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: (
+ <>
+
+
+ IJK
+
+ >
+ ),
+ searchLabel: 'IJK',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+}
diff --git a/src/components/data-entry/QueryItem/Cascader.tsx b/src/components/data-entry/QueryItem/Cascader.tsx
index 7582fce0f..69dee0452 100644
--- a/src/components/data-entry/QueryItem/Cascader.tsx
+++ b/src/components/data-entry/QueryItem/Cascader.tsx
@@ -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,
@@ -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
}
@@ -31,18 +33,20 @@ export interface IQueryItemCascaderProps {
loadData?: (value: string) => Promise
value?: Array
disabled?: boolean
+ placement?: IBaseCascaderProps['placement']
+ defaultOpen?: IBaseCascaderProps['defaultOpen']
}
const Cascader = (props: IQueryItemCascaderProps) => {
- type DefaultOptionType = GetProp[number]
+ type ICascaderOption = GetProp[number]
const options: ICascaderOption[] = []
const [items, setItems] = useState(props.options ?? options)
const [searchValue, setSearchValue] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [selectedValue, setSelectedValue] = useState>(props.value ?? [])
- const [selectedDisplayValue, setSelectedDisplayValue] = useState(
- props.value && props.value.length > 0 ? (props.value.slice(-1)[0] as any).label : '',
+ const [selectedOption, setSelectedOption] = useState(
+ props.value?.length ? props.value.slice(-1)[0] : undefined,
)
const [isOpen, setIsOpen] = useState(false)
@@ -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) {
@@ -98,9 +104,11 @@ const Cascader = (props: IQueryItemCascaderProps) => {
searchValue,
disabled: props.disabled,
value: selectedValue,
+ defaultOpen: props.defaultOpen,
+ placement: props.placement ?? 'bottomLeft',
onChange: (values: Array, 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 => (
@@ -111,6 +119,7 @@ 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 => {
@@ -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 (
<>
@@ -164,7 +171,7 @@ const Cascader = (props: IQueryItemCascaderProps) => {
prefix={getIcon()}
/>
- {props.errorMessage && {props.errorMessage}}
+ {props.errorMessage && {props.errorMessage}}
>
)
@@ -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 }])