Skip to content

Commit

Permalink
feat: intial add of QueryItem.ValueSelector.Cascader
Browse files Browse the repository at this point in the history
  • Loading branch information
ZoeAstra committed Apr 18, 2024
1 parent cd097f8 commit c022aa2
Show file tree
Hide file tree
Showing 17 changed files with 344 additions and 129 deletions.
2 changes: 1 addition & 1 deletion design/LightTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const LightTheme: IMpThemeConfig = {
mpQueryItem_borderWidth_active: '0 0 2px 0',
mpQueryItem_shadow_focus: '0 0 0 2px rgba(54, 0, 209, 0.1)',
mpQueryItem_color_disabled: '#505249',
'mpQueryItem|valueSelector_fontWeight': '600',
'mpQueryItem|valueSelector_fontWeight': '500',
'mpQueryItem|valueSelector_color': '#20007a',
mpQueryItem_padding: 4,
mpQueryItem_gap: 4,
Expand Down
2 changes: 1 addition & 1 deletion design/MpThemeConfig.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export type IMpThemeConfig = ThemeConfig & {
mpQueryItem_borderWidth_active: '0 0 2px 0'
mpQueryItem_shadow_focus: '0 0 0 2px rgba(54, 0, 209, 0.1)'
mpQueryItem_color_disabled: '#505249'
'mpQueryItem|valueSelector_fontWeight': '600'
'mpQueryItem|valueSelector_fontWeight': '500'
'mpQueryItem|valueSelector_color': '#20007a'
mpQueryItem_padding: 4
mpQueryItem_gap: 4
Expand Down
5 changes: 5 additions & 0 deletions src/assets/svg/circle-dashed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/components/data-entry/QueryItem/Action.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ export const Disabled: Story = {
isDisabled: true,
},
}

export const OnClick: Story = {
args: {
text: 'On Click Action',
isPrimary: true,
onClick: () => alert('You clicked the QueryItem.Action!')
},
}
13 changes: 6 additions & 7 deletions src/components/data-entry/QueryItem/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@ export interface IActionProps {
isPrimary?: boolean
isDisabled?: boolean
text?: string
onClick?: () => void
}

function Action(props: IActionProps) {
export const Action = (props: IActionProps) => {
const buttonClassNames: string = props.isPrimary
? 'query-item query-item-action'
: 'query-item query-item-action query-item-action--secondary'
? 'query-item query-item--action'
: 'query-item query-item--action query-item--secondary'
return (
<>
<Button className={buttonClassNames} type={props.isPrimary ? 'primary' : 'default'} disabled={props.isDisabled}>
<AddIcon className="query-item-action__icon" />
<Button className={buttonClassNames} type={props.isPrimary ? 'primary' : 'default'} disabled={props.isDisabled} onClick={props.onClick}>
<AddIcon className="query-item__icon" />
<span>{props.text}</span>
</Button>
</>
)
}

export default Action
112 changes: 112 additions & 0 deletions src/components/data-entry/QueryItem/Cascader.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Meta, StoryObj } from '@storybook/react'
import { 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 = [
{
value: "United States1",
label: "United States",
children: [
{
value: "Michigan1",
label: "Michigan",
children: [
{
value: "Detroit1",
label: "Detroit",
},
{
value: "Lansing1",
label: "Lansing",
},
],
},
{
value: "California1",
label: "California",
children: [
{
value: "San Francisco1",
label: "San Francisco",
},
{
value: "San Jose1",
label: "San Jose",
},
],
},
],
},
{
value: "Canada1",
label: "Canada",
children: [
{
value: "Ontario1",
label: "Ontario",
children: [
{
value: "Toronto1",
label: "Toronto",
},
],
},
],
},
];

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

export const SimpleList: Story = {
args: {
placeholder: "QueryItem.ValueSelector.Cascader Simple",
options: [
{
value: 'United States',
label: 'United States',
},
{
value: 'Canada',
label: 'Canada',
},
]
},
}

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

export const OnSelect: Story = {
args: {
placeholder: "QueryItem.ValueSelector.Cascader Error",
options: exampleOptions,
onChange: (value) => console.log(value),
},
}
102 changes: 102 additions & 0 deletions src/components/data-entry/QueryItem/Cascader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import './query-item.css'
import { GetProp } from 'antd'
import { ReactNode, useState } from 'react'
import {
AddIcon,
Cascader as BaseCascader,
CircleDashedIcon,
Flex,
ICascaderProps as IBaseCascaderProps,
Input,
Space,
Typography,
} from 'src/components'

export interface CascaderOption {
value: string
label: ReactNode
selectedLabel?: string
children?: CascaderOption[]
disabled?: boolean
}

export type CascaderIcons = "blank" | "attribute" | "user" | "event"

const CascaderIconList = {
"blank": <CircleDashedIcon className="query-item__icon"/>,
"attribute": <AddIcon className="query-item__icon"/>,
"user": <AddIcon className="query-item__icon"/>,
"event": <AddIcon className="query-item__icon"/>,
}

export interface ICascaderProps {
options: CascaderOption[]
icon?: CascaderIcons
errorMessage?: string
placeholder?: string
onChange?: (selectedValue: string[]) => void
}

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

const options: CascaderOption[] = []
const [items] = useState(props.options ?? options)
const [searchValue, setSearchValue] = useState('')
const [selectedValue, setSelectedValue] = useState<string[]>()
const [isOpen, setIsOpen] = useState(false)

const onSearch = (value: string) => {
setSearchValue(value)
}

const filter = (inputValue: string, path: DefaultOptionType[]) =>
path.some(option => (option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1)

const baseProps: IBaseCascaderProps = {
getPopupContainer: triggerNode => triggerNode.parentElement,
searchValue: searchValue,
value: selectedValue,
onChange: (value: (string | number)[]): void => {
setSelectedValue(value as string[])
if (props.onChange) {
props.onChange(value as string[])
}
},
dropdownRender: menu => (
<div className={'query-item__dropdown'}>
<Input.Search
allowClear
className={'query-item__input-search'}
placeholder="Search"
onSearch={onSearch}
/>
<Flex justify="center">{menu}</Flex>
</div>
),
showSearch: { filter },
options: items,
onDropdownVisibleChange: value => setIsOpen(value),
}

let inputClasses = `query-item`
if (isOpen) inputClasses += ' query-item--open'
if (selectedValue) inputClasses += ' query-item--selected'
if (props.errorMessage) inputClasses += ' query-item--error'

return (
<>
<BaseCascader {...baseProps}>
<Input
readOnly={true}
placeholder={props.placeholder}
status={props.errorMessage ? 'error' : undefined}
className={inputClasses}
value={selectedValue?.slice(-1)}
prefix={props.icon ?? <CircleDashedIcon className="query-item__icon" />}
/>
</BaseCascader>
{props.errorMessage && <Typography.Text type={'danger'}>{props.errorMessage}</Typography.Text>}
</>
)
}
36 changes: 12 additions & 24 deletions src/components/data-entry/QueryItem/Qualifier.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,34 @@ const meta: Meta<typeof QueryItem.Qualifier> = {
}
export default meta

const defaultOptions = [
{ value: '0', label: 'is equal to' },
{ value: '1', label: 'is not equal to' },
{ value: '2', label: 'is greater than' },
{ value: '3', label: 'is greater or equal to' },
{ value: '4', label: 'is less than' },
{ value: '5', label: 'is less or equal to' },
]

// stories
export const Empty: Story = {}

export const Simple: Story = {
args: {
options: [
{ value: '0', label: 'is equal to' },
{ value: '1', label: 'is not equal to' },
{ value: '2', label: 'is greater than to' },
{ value: '3', label: 'is greater or equal to' },
{ value: '4', label: 'is less than' },
{ value: '5', label: 'is less or equal to' },
],
options: defaultOptions
},
}

export const Error: Story = {
args: {
errorMessage: 'This is an error message for the Qualifier component',
options: [
{ value: '0', label: 'is equal to' },
{ value: '1', label: 'is not equal to' },
{ value: '2', label: 'is greater than to' },
{ value: '3', label: 'is greater or equal to' },
{ value: '4', label: 'is less than' },
{ value: '5', label: 'is less or equal to' },
],
options: defaultOptions,
},
}

export const Disabled: Story = {
args: {
disabled: true,
options: [
{ value: '0', label: 'is equal to' },
{ value: '1', label: 'is not equal to' },
{ value: '2', label: 'is greater than to' },
{ value: '3', label: 'is greater or equal to' },
{ value: '4', label: 'is less than' },
{ value: '5', label: 'is less or equal to' },
],
options: defaultOptions,
},
}
11 changes: 10 additions & 1 deletion src/components/data-entry/QueryItem/Qualifier.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import './query-item.css'
import type { DefaultOptionType } from 'antd/es/select'
import { useState } from 'react'
import { CheckIcon } from 'src/components/icons'
import { Typography } from 'src/components/general/Typography/Typography'
import { type ISelectProps, Select } from 'src/components'
Expand All @@ -14,20 +15,28 @@ export interface IQueryItemQualifierProps {
}

export const Qualifier = (props: IQueryItemQualifierProps) => {
const [isOpen, setIsOpen] = useState(false)
const selectProps: ISelectProps = {
defaultValue: props.options?.length ? props.options[0].value : undefined,
menuItemSelectedIcon: node =>
node.isSelected ? <CheckIcon className="query-item-qualifier__item-selected-icon" /> : null,
onChange: props.onChange,
onDropdownVisibleChange: () => setIsOpen(true),
placement: 'bottomLeft',
popupMatchSelectWidth: false,
status: props.errorMessage ? 'error' : undefined,
suffixIcon: null,
variant: 'borderless',
options: props.options,
disabled: props.disabled,
}

let className = 'query-item'
if (isOpen) className += ' query-item--open'

return (
<>
<Select className="query-item query-item-qualifier__select" {...selectProps}></Select>
<Select className={className} {...selectProps}></Select>
{props.errorMessage && <Typography.Text type="danger">{props.errorMessage}</Typography.Text>}
</>
)
Expand Down
4 changes: 3 additions & 1 deletion src/components/data-entry/QueryItem/QueryItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import Action from './Action'
import { ValueSelector } from 'src/components/data-entry/QueryItem/ValueSelector'
import { Action } from './Action'
import { Qualifier } from './Qualifier'
import { Text } from './Text'

Expand All @@ -8,4 +9,5 @@ export const QueryItem = () => {
}
QueryItem.Action = Action
QueryItem.Qualifier = Qualifier
QueryItem.ValueSelector = ValueSelector
QueryItem.Text = Text
Loading

0 comments on commit c022aa2

Please sign in to comment.