Skip to content

Commit

Permalink
chore: cascader loading state (#360)
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 Aug 6, 2024
1 parent 2248b2d commit 45fc483
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 162 deletions.
2 changes: 1 addition & 1 deletion src/components/data-entry/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface IFormProps<Values = any> extends AntFormProps<Values> {
children: ReactNode
}

export const Form = <Values = any>(props: IFormProps<Values>) => {
export const Form = <Values = any,>(props: IFormProps<Values>) => {
return (
<ConfigProvider>
<AntForm {...props}>{props.children}</AntForm>
Expand Down
80 changes: 45 additions & 35 deletions src/components/data-entry/QueryItem/Cascader.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
import { type Meta, type StoryObj } from '@storybook/react'
import { QueryItem } from 'src/components'
import { type IQueryItemCascaderProps, QueryItem } from 'src/components'

const meta: Meta<typeof QueryItem.ValueSelector.Cascader> = {
title: 'Aquarium/Data Entry/QueryItem/ValueSelector/Cascader',
component: QueryItem.ValueSelector.Cascader,
parameters: {
docs: {
description: {
component:
'This is the "Action" component of the QueryItem component group. This component is currently meant to trigger a single action, but will eventually support a list of actions via a dropdown list interface.',
},
},
},

args: {},
}
export default meta

type Story = StoryObj<typeof QueryItem.ValueSelector.Cascader>

const exampleOptions = [
const options: IQueryItemCascaderProps['options'] = [
{
value: 'United States1',
label: 'United States',
Expand Down Expand Up @@ -72,16 +54,35 @@ const exampleOptions = [
},
]

const meta: Meta<typeof QueryItem.ValueSelector.Cascader> = {
title: 'Aquarium/Data Entry/QueryItem/ValueSelector/Cascader',
component: QueryItem.ValueSelector.Cascader,
parameters: {
docs: {
description: {
component:
'This is the "Action" component of the QueryItem component group. This component is currently meant to trigger a single action, but will eventually support a list of actions via a dropdown list interface.',
},
},
},

args: {
options,
},
}
export default meta

type Story = StoryObj<typeof QueryItem.ValueSelector.Cascader>

export const Default: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader Default',
options: exampleOptions,
placeholder: 'Default',
},
}

export const SimpleList: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader Simple',
placeholder: 'Simple List',
options: [
{
value: 'United States',
Expand All @@ -97,24 +98,21 @@ export const SimpleList: Story = {

export const Error: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader Error',
options: exampleOptions,
placeholder: 'Error',
errorMessage: 'test error',
},
}

export const WithIcon: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader Icon',
options: exampleOptions,
placeholder: 'With Icon',
icon: 'event',
},
}

export const OnSelect: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader Error',
options: exampleOptions,
placeholder: 'On Select',
onChange: async value => {
console.log(value)
},
Expand All @@ -123,8 +121,7 @@ export const OnSelect: Story = {

export const PreSelectedValue: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader PreSelected',
options: exampleOptions,
placeholder: 'PreSelected',
value: ['Canada1', 'Ontario1', 'Toronto1'],
onChange: async (values, _) => {
console.log(values)
Expand All @@ -134,10 +131,23 @@ export const PreSelectedValue: Story = {

export const LoadData: Story = {
args: {
placeholder: 'QueryItem.ValueSelector.Cascader Load',
options: exampleOptions,
loadData: (value: string) => {
placeholder: 'Load',
loadData: async (value: string) => {
console.log(value)
},
},
}
}

export const Loading: Story = {
args: {
placeholder: 'Loading Story',
options: undefined,
loadData: async (value: string) => {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(options)
}, 3000)
})
},
},
}
55 changes: 40 additions & 15 deletions src/components/data-entry/QueryItem/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
Input,
Typography,
Icon,
Skeleton,
} from 'src/components'
import { type Icons } from 'src/constants/Icons'
import { useMount } from 'src/hooks/useMount'
import { debounce } from 'src/utils/utils'

export interface ICascaderOption {
Expand All @@ -26,7 +28,7 @@ export interface IQueryItemCascaderProps {
errorMessage?: string
placeholder?: string
onChange?: (values: Array<number | string>, selectedOptions: any) => Promise<void>
loadData?: (value: string) => void
loadData?: (value: string) => Promise<void>
value?: Array<number | string>
disabled?: boolean
}
Expand All @@ -37,6 +39,7 @@ const Cascader = (props: IQueryItemCascaderProps) => {
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 : '',
Expand All @@ -53,6 +56,18 @@ const Cascader = (props: IQueryItemCascaderProps) => {
}
}, [props.value])

useMount(() => {
async function init(): Promise<void> {
if (props.loadData && !items?.length) {
setIsLoading(true)
await props.loadData('')
setIsLoading(false)
}
}

void init()
})

const onSearch = ({ target: { value } }: { target: { value: string } }) => {
if (debouncedLoadData) {
if (value.length > 3) {
Expand All @@ -70,32 +85,42 @@ const Cascader = (props: IQueryItemCascaderProps) => {

let debouncedLoadData: (value: string) => void
if (props.loadData) {
debouncedLoadData = useCallback(debounce(props.loadData, 500), [])
debouncedLoadData = useCallback(
debounce((value: string) => {
void props.loadData?.(value)
}, 500),
[],
)
}

const baseProps: IBaseCascaderProps = {
getPopupContainer: triggerNode => triggerNode.parentElement,
searchValue,
disabled: props.disabled,
value: selectedValue,
onChange: (values: Array<number | string>, selectedOptions: any): void => {
setSelectedValue(values as string[])
setSelectedDisplayValue(selectedOptions.slice(-1)[0].label)
if (props.onChange) {
void props.onChange(values, selectedOptions)
}
void props.onChange?.(values, selectedOptions)
},
dropdownRender: menu => (
<div className="query-item__dropdown">
<Input
allowClear
value={searchValue}
className="query-item__input-search"
placeholder="Search"
onChange={a => {
onSearch(a)
}}
/>
<Flex justify="center">{menu}</Flex>
{isLoading ? (
<Skeleton />
) : (
<>
<Input
allowClear
value={searchValue}
className="query-item__input-search"
placeholder="Search"
onChange={a => {
onSearch(a)
}}
/>
<Flex justify="center">{menu}</Flex>
</>
)}
</div>
),
showSearch: {
Expand Down
Loading

0 comments on commit 45fc483

Please sign in to comment.