Skip to content

Commit

Permalink
feat: selector's options support fieldNames (#6303)
Browse files Browse the repository at this point in the history
* feat: selector's options support fieldNames

* doc: update doc
  • Loading branch information
1587315093 authored Aug 15, 2023
1 parent e8f6c15 commit ac7c2be
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 25 deletions.
5 changes: 1 addition & 4 deletions src/components/cascader-view/cascader-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ import { optionSkeleton } from './option-skeleton'
import Skeleton from '../skeleton'
import { useUpdateEffect } from 'ahooks'
import { useFieldNames } from '../../hooks'
import type { FieldNamesType } from '../../hooks'
import type { FieldNamesType, BaseOptionType } from '../../hooks'

const classPrefix = `adm-cascader-view`

export type CascaderValue = string

type BaseOptionType = {
[key: string]: any
}
export type CascaderOption = {
label?: string
value?: string
Expand Down
23 changes: 18 additions & 5 deletions src/components/selector/demos/demo1.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { Selector, Space } from 'antd-mobile'
import { DemoBlock } from 'demos'
import { options } from './options'
import { options, fieldNamesOptions } from './options'

export default () => {
return (
Expand All @@ -18,7 +18,7 @@ export default () => {
<Selector
options={options}
defaultValue={['2', '3']}
multiple={true}
multiple
onChange={(arr, extend) => console.log(arr, extend.items)}
/>
</DemoBlock>
Expand All @@ -28,7 +28,7 @@ export default () => {
columns={2}
options={options}
defaultValue={['2', '3']}
multiple={true}
multiple
/>
</DemoBlock>

Expand All @@ -37,13 +37,13 @@ export default () => {
columns={3}
options={options}
defaultValue={['2', '3']}
multiple={true}
multiple
/>
</DemoBlock>

<DemoBlock title='禁用状态'>
<Space block direction='vertical'>
<Selector options={options} defaultValue={['1']} disabled={true} />
<Selector options={options} defaultValue={['1']} disabled />
<Selector
options={[
{
Expand All @@ -64,6 +64,19 @@ export default () => {
/>
</Space>
</DemoBlock>
<DemoBlock title='自定义FieldName'>
<Selector
options={fieldNamesOptions}
fieldNames={{
label: 'labelT',
value: 'valueT',
disabled: 'disabledT',
}}
defaultValue={['1']}
multiple
onChange={(arr, extend) => console.log(arr, extend.items)}
/>
</DemoBlock>
</>
)
}
16 changes: 16 additions & 0 deletions src/components/selector/demos/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,19 @@ export const options = [
value: '3',
},
]

export const fieldNamesOptions = [
{
labelT: '选项一',
valueT: '1',
},
{
labelT: '选项二',
valueT: '2',
},
{
labelT: '选项三',
valueT: '3',
disabledT: true,
},
]
1 change: 1 addition & 0 deletions src/components/selector/index.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Provides multiple options for the user to choose from, typically used in filters
| columns | Number of the displayed columns (Note that `grid` layout not support in IOS 9) | `number` | - |
| defaultValue | Selected value by default | `SelectorValue[]` | `[]` |
| disabled | Whether to disable selecting | `boolean` | `false` |
| fieldNames | Custom field name for label and value and disabled | `{ label: string, value: string, disabled: string }` | `{ label: 'label', value: 'value',disabled:'disabled' }` |
| multiple | Whether to allow multiple selections | `boolean` | `false` |
| onChange | Triggered when the value is changed | `(value: SelectorValue[], extend: { items: SelectorOption[] }) => void` | - |
| options | Optional selector | `SelectorOption[]` | - |
Expand Down
1 change: 1 addition & 0 deletions src/components/selector/index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
| columns | 列数(注意 `grid` 布局在 IOS 9 下不支持) | `number` | - |
| defaultValue | 默认项 | `SelectorValue[]` | `[]` |
| disabled | 是否全局禁止选中 | `boolean` | `false` |
| fieldNames | 自定义 options 中 label value disabled 的字段 | `{ label: string, value: string, disabled: string }` | `{ label: 'label', value: 'value',disabled:'disabled' }` |
| multiple | 是否允许多选 | `boolean` | `false` |
| onChange | 选项改变时触发 | `(value: SelectorValue[], extend: { items: SelectorOption[] }) => void` | - |
| options | 可选项 | `SelectorOption[]` | - |
Expand Down
36 changes: 22 additions & 14 deletions src/components/selector/selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import Grid, { GridProps } from '../grid'
import { usePropsValue } from '../../utils/use-props-value'
import { CheckMark } from './check-mark'
import { useConfig } from '../config-provider'
import { useFieldNames } from '../../hooks'
import type { FieldNamesType, BaseOptionType } from '../../hooks'

const classPrefix = `adm-selector`

type SelectorValue = string | number

export interface SelectorOption<V> {
label: ReactNode
export type SelectorOption<V> = {
label?: ReactNode
description?: ReactNode
value: V
value?: V
disabled?: boolean
}
} & BaseOptionType

export type SelectorProps<V> = {
options: SelectorOption<V>[]
Expand All @@ -29,6 +31,7 @@ export type SelectorProps<V> = {
value?: V[]
onChange?: (v: V[], extend: { items: SelectorOption<V>[] }) => void
showCheckMark?: boolean
fieldNames?: FieldNamesType
} & NativeProps<
| '--color'
| '--checked-color'
Expand All @@ -51,23 +54,25 @@ const defaultProps = {

export const Selector = <V extends SelectorValue>(p: SelectorProps<V>) => {
const props = mergeProps(defaultProps, p)
const [labelName, valueName, , disabledName] = useFieldNames(props.fieldNames)
const [value, setValue] = usePropsValue({
value: props.value,
defaultValue: props.defaultValue,
onChange: val => {
const extend = {
get items() {
return props.options.filter(option => val.includes(option.value))
return props.options.filter(option => val.includes(option[valueName]))
},
}
props.onChange?.(val, extend)
},
})

const { locale } = useConfig()

const items = props.options.map(option => {
const active = (value || []).includes(option.value)
const disabled = option.disabled || props.disabled
const active = (value || []).includes(option[valueName])
const disabled = option[disabledName] || props.disabled
const itemCls = classNames(`${classPrefix}-item`, {
[`${classPrefix}-item-active`]: active && !props.multiple,
[`${classPrefix}-item-multiple-active`]: active && props.multiple,
Expand All @@ -76,19 +81,19 @@ export const Selector = <V extends SelectorValue>(p: SelectorProps<V>) => {

return (
<div
key={option.value}
key={option[valueName]}
className={itemCls}
onClick={() => {
if (disabled) {
return
}
if (props.multiple) {
const val = active
? value.filter(v => v !== option.value)
: [...value, option.value]
? value.filter(v => v !== option[valueName])
: [...value, option[valueName]]
setValue(val)
} else {
const val = active ? [] : [option.value]
const val = active ? [] : [option[valueName]]
setValue(val)
}
}}
Expand All @@ -97,7 +102,7 @@ export const Selector = <V extends SelectorValue>(p: SelectorProps<V>) => {
(active && !props.multiple) || (active && props.multiple)
}
>
{option.label}
{option[labelName]}
{option.description && (
<div className={`${classPrefix}-item-description`}>
{option.description}
Expand All @@ -119,8 +124,11 @@ export const Selector = <V extends SelectorValue>(p: SelectorProps<V>) => {
role='listbox'
aria-label={locale.Selector.name}
>
{!props.columns && <Space wrap>{items}</Space>}
{props.columns && <Grid columns={props.columns}>{items}</Grid>}
{props.columns ? (
<Grid columns={props.columns}>{items}</Grid>
) : (
<Space wrap>{items}</Space>
)}
</div>
)
}
39 changes: 37 additions & 2 deletions src/components/selector/tests/selector.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import React from 'react'
import { render, testA11y, fireEvent, screen } from 'testing'
import Selector from '..'
import { options } from '../demos/options'
import { options, fieldNamesOptions } from '../demos/options'

const classPrefix = `adm-selector`

describe('Selector', () => {
test('a11y', async () => {
await testA11y(<Selector options={options} />)
})

test('a11y fieldNames', async () => {
await testA11y(
<Selector
options={fieldNamesOptions}
fieldNames={{
label: 'labelT',
value: 'valueT',
disabled: 'disabledT',
}}
/>
)
})
test('onChange should be work', () => {
const onChange = jest.fn()
render(<Selector options={options} onChange={onChange} />)
Expand All @@ -20,7 +31,31 @@ describe('Selector', () => {
expect(onChange.mock.calls[0][0]).toMatchObject([options[1].value])
expect(onChange.mock.calls[0][1].items).toMatchObject([options[1]])
})
test('fieldNames onChange should be work', () => {
const onChange = jest.fn()
render(
<Selector
options={fieldNamesOptions}
fieldNames={{
label: 'labelT',
value: 'valueT',
disabled: 'disabledT',
}}
onChange={onChange}
/>
)

const label = screen.getByText(fieldNamesOptions[1].labelT)
fireEvent.click(label)

expect(label).toHaveClass(`${classPrefix}-item-active`)
expect(onChange.mock.calls[0][0]).toMatchObject([
fieldNamesOptions[1].valueT,
])
expect(onChange.mock.calls[0][1].items).toMatchObject([
fieldNamesOptions[1],
])
})
test('disabled should be work', () => {
const onChange = jest.fn()
render(
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { useFieldNames } from './useFieldNames'
export type { FieldNamesType } from './useFieldNames'

export type BaseOptionType = {
[key: string]: any
}

0 comments on commit ac7c2be

Please sign in to comment.