Skip to content

Commit

Permalink
fix: correct QueryItem Cascader and Action cosmetic bugs/flaws
Browse files Browse the repository at this point in the history
  • Loading branch information
ZoeAstra committed May 9, 2024
1 parent 6bae0ed commit f5e6032
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 21 deletions.
3 changes: 2 additions & 1 deletion src/components/data-entry/QueryItem/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export interface IActionProps {

export const Action = (props: IActionProps) => {
let buttonClassNames: string = 'query-item query-item--action'
if ((props.type ?? 'default') !== 'primary') buttonClassNames += ` query-item--secondary`
if ((props.type ?? 'default') === 'default') buttonClassNames += ` query-item--secondary`
if ((props.type ?? 'default') === 'disabled') buttonClassNames += ` query-item--disabled`

const baseProps: IButtonProps = {
className: buttonClassNames,
Expand Down
88 changes: 69 additions & 19 deletions src/components/data-entry/QueryItem/Cascader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './query-item.css'
import { GetProp } from 'antd'
import { useCallback, useEffect, useState } from 'react'
import { ReactNode, useCallback, useEffect, useState } from 'react'
import {
Cascader as BaseCascader,
Flex,
Expand All @@ -21,7 +21,7 @@ export interface ICascaderOption {

export interface ICascaderProps {
options: ICascaderOption[]
icon?: keyof Pick<typeof Icons, ("empty" | "event" | "userAttribute" | "eventAttribute")>
icon?: keyof Pick<typeof Icons, 'empty' | 'event' | 'userAttribute' | 'eventAttribute'>
errorMessage?: string
placeholder?: string
onChange?: (values: (number | string)[], selectedOptions: any) => Promise<void>
Expand All @@ -36,17 +36,19 @@ export const Cascader = (props: ICascaderProps) => {
const [items, setItems] = useState(props.options ?? options)
const [searchValue, setSearchValue] = useState('')
const [selectedValue, setSelectedValue] = useState<(number | string)[]>(props.value ?? [])
const [selectedDisplayValue, setSelectedDisplayValue] = useState(props.value ? (props.value.slice(-1)[0] as any).label : "")
const [selectedDisplayValue, setSelectedDisplayValue] = useState(
props.value ? (props.value.slice(-1)[0] as any).label : '',
)
const [isOpen, setIsOpen] = useState(false)

useEffect(() => {
setItems(props.options)
}, [props.options])

const onSearch = ({ target: { value: value}}: { target: { value: string}}) => {
const onSearch = ({ target: { value: value } }: { target: { value: string } }) => {
if (debouncedLoadData) {
if (value.length > 3) {
if (transformOptionsToPaths(items,[]).filter((path) => filter(value, path)).length == 0) {
if (transformOptionsToPaths(items, []).filter(path => filter(value, path)).length == 0) {
debouncedLoadData(value)
}
}
Expand All @@ -58,9 +60,9 @@ export const Cascader = (props: ICascaderProps) => {
return path.some(option => (option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1)
}

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

const baseProps: IBaseCascaderProps = {
Expand All @@ -76,13 +78,39 @@ export const Cascader = (props: ICascaderProps) => {
},
dropdownRender: menu => (
<div className={'query-item__dropdown'}>
<Input allowClear className={'query-item__input-search'} placeholder="Search" onChange={(a) => onSearch(a)} />
<Input
allowClear
value={searchValue}
className={'query-item__input-search'}
placeholder="Search"
onChange={a => onSearch(a)}
/>
<Flex justify="center">{menu}</Flex>
</div>
),
showSearch: { filter },
showSearch: {
filter,
render: (inputValue: string, paths: ICascaderOption[]): ReactNode => {
return (
<>
{paths.map((path: ICascaderOption, index) => {
const lowerLabel = path.label.toLowerCase()
return (
<>
{highlightMatches(path.label, inputValue.toLowerCase())}
{index < paths.length - 1 ? ' > ' : ''}
</>
)
})}
</>
)
},
},
options: items,
onDropdownVisibleChange: value => setIsOpen(value),
onDropdownVisibleChange: value => {
setIsOpen(value)
if (value) setSearchValue('')
},
}

let inputClasses = `query-item`
Expand All @@ -99,24 +127,46 @@ export const Cascader = (props: ICascaderProps) => {
status={props.errorMessage ? 'error' : undefined}
className={inputClasses}
value={selectedDisplayValue ?? selectedValue.slice(-1)}
prefix={props.icon ? <Icon name={props.icon} size="ms" color="primary" /> : <Icon name="empty" size="ms" color="primary" />}
prefix={
props.icon ? (
<Icon name={props.icon} size="ms" color="primary" />
) : (
<Icon name="empty" size="ms" color="primary" />
)
}
/>
</BaseCascader>
{props.errorMessage && <Typography.Text type={'danger'}>{props.errorMessage}</Typography.Text>}
</>
)

function transformOptionsToPaths(options: DefaultOptionType[], prefixPath: DefaultOptionType[]): DefaultOptionType[][] {
let result: DefaultOptionType[][] = [];
options.forEach((option) => {
function highlightMatches(source: string, valueToHighlight: string): ReactNode {
const lowerSource = source.toLowerCase()
return lowerSource.indexOf(valueToHighlight) === -1 ? (<>{ source }</>) : (
<>
{source.slice(0, lowerSource.indexOf(valueToHighlight))}
<span className="query-item__search-highlight">
{source.slice(lowerSource.indexOf(valueToHighlight), lowerSource.indexOf(valueToHighlight) + valueToHighlight.length)}
</span>
{highlightMatches(source.slice(lowerSource.indexOf(valueToHighlight) + valueToHighlight.length), valueToHighlight)}
</>
)
}

function transformOptionsToPaths(
options: DefaultOptionType[],
prefixPath: DefaultOptionType[],
): DefaultOptionType[][] {
let result: DefaultOptionType[][] = []
options.forEach(option => {
if (option.children && option.children.length > 0) {
const newPrefix = prefixPath.concat([{label: option.label, value: option.value}]);
result = result.concat(transformOptionsToPaths(option.children, newPrefix));
const newPrefix = prefixPath.concat([{ label: option.label, value: option.value }])
result = result.concat(transformOptionsToPaths(option.children, newPrefix))
} else {
const path = prefixPath.concat([{label: option.label, value: option.value}]);
result.push(path);
const path = prefixPath.concat([{ label: option.label, value: option.value }])
result.push(path)
}
})
return result;
return result
}
}
16 changes: 15 additions & 1 deletion src/components/data-entry/QueryItem/query-item.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
}
.query-item[disabled] {
color: var(--mp-query-item-color-disabled) !important;
background-color: var(--mp-query-item-bg-color) !important;
border-color: var(--mp-query-item-border-color-disabled) !important;
}
.query-item.query-item--input-text {
Expand All @@ -46,7 +47,6 @@

.query-item.query-item--action {
color: var(--mp-query-item-action-primary-color) !important;

gap: var(--mp-query-item-gap);
}
.query-item.query-item--secondary {
Expand All @@ -59,6 +59,17 @@
border-width: var(--mp-query-item-border-width-active);
background-color: var(--mp-query-item-bg-color-active) !important;
}
.query-item.query-item--disabled {
color: var(--mp-query-item-action-secondary-color) !important;
}
.query-item.query-item--disabled:hover {
background-color: var(--mp-query-item-bg-color) !important;
}
.query-item.query-item--disabled:active {
border-width: var(--mp-query-item-border-width) !important;
background-color: var(--mp-query-item-bg-color) !important;
box-shadow: none !important;
}
.query-item.query-item--selected > input {
color: var(--mp-query-item-value-selector-color) !important;
font-weight: var(--mp-query-item-value-selector-font-weight) !important;
Expand All @@ -74,6 +85,9 @@
.query-item .ant-select-selector {
padding: 0 !important;
}
.query-item__search-highlight {
color: var(--mp-query-item-action-primary-color);
}

/* This is temporary until the new icon component is available where sizes can be controlled without CSS */
.query-item__icon {
Expand Down

0 comments on commit f5e6032

Please sign in to comment.