feat(checkbox-group): add min max prop (#491)

* feat:n-input Support hidden password

* feat(form): support require-mark-placement(#171)

* Revert "feat(form): support require-mark-placement(#171)"

This reverts commit 0627777693.

* Revert "feat:n-input Support hidden password"

This reverts commit ea6491783d.

* feat(checkboxGroup): add min max prop   WIP

* feat(checkboxGroup): add min max prop WIP

* feat(checkboxGroup): add min max prop

* Update CHANGELOG.zh-CN.md

* Update CheckboxGroup.tsx

* feat(checkboxGroup): fix code

* Update Checkbox.spec.ts

* feat(checkboxGroup): fix code
This commit is contained in:
doom-9 2021-07-16 23:30:34 +08:00 committed by GitHub
parent fb2b30eec0
commit cb929ff479
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 186 additions and 9 deletions

View File

@ -1,5 +1,11 @@
# CHANGELOG # CHANGELOG
## Pending
### Feats
- `n-checkbox-group` add `min` and `max` prop.
## 2.15.5 (2021-07-16) ## 2.15.5 (2021-07-16)
### Feats ### Feats

View File

@ -1,5 +1,11 @@
# CHANGELOG # CHANGELOG
## Pending
### Feats
- `n-checkbox-group` 新增 `min``max` 属性.
## 2.15.5 (2021-07-16) ## 2.15.5 (2021-07-16)
### Feats ### Feats
@ -9,7 +15,6 @@
- `n-carousel` 新增 `show-arrow` 属性 - `n-carousel` 新增 `show-arrow` 属性
- `n-slider` 新增 `format-tooltip` 属性 - `n-slider` 新增 `format-tooltip` 属性
- `n-upload``on-finish` 回调参数中新增 `event` - `n-upload``on-finish` 回调参数中新增 `event`
- `n-slider` 新增 `format-tooltip` 属性
- `n-rate` 新增 `readonly` 属性 - `n-rate` 新增 `readonly` 属性
- `n-time-picker` 新增 `seconds`、`minutes`、`hours`属性 - `n-time-picker` 新增 `seconds`、`minutes`、`hours`属性
- `n-notification` 导出 `NotificationApi`, `NotificationOptions` and `NotificationReactive` 类型 - `n-notification` 导出 `NotificationApi`, `NotificationOptions` and `NotificationReactive` 类型

View File

@ -20,11 +20,11 @@ event
| Name | Type | Default | Description | | Name | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| checked | `boolean` | `false` | Whether it is selected in the controlled mode. | | checked | `boolean` | `false` | Whether it is selected in the controlled mode. |
| indeterminate | `boolean` | `false` | Whether partly selected. |
| default-checked | `boolean` | `false` | Whether selected by default in uncontrolled mode. | | default-checked | `boolean` | `false` | Whether selected by default in uncontrolled mode. |
| disabled | `boolean` | `false` | Whether to disable. | | disabled | `boolean` | `false` | Whether to disable. |
| focusable | `boolean` | `true` | Whether to focus on selection. | | focusable | `boolean` | `true` | Whether to focus on selection. |
| label | `string \| (() => VNodeChild)` | `undefined` | Checkbox label | | indeterminate | `boolean` | `false` | Whether partly selected. |
| label | `string \| (() => VNodeChild)` | `undefined` | Checkbox label. |
| value | `string \| number` | `undefined` | The value of the checkbox to be used in checkbox group. | | value | `string \| number` | `undefined` | The value of the checkbox to be used in checkbox group. |
| on-update:checked | `(checked: boolean) => void` | `undefined` | Callback function triggered on checked status changes. | | on-update:checked | `(checked: boolean) => void` | `undefined` | Callback function triggered on checked status changes. |
@ -34,6 +34,8 @@ event
| --- | --- | --- | --- | | --- | --- | --- | --- |
| disabled | `boolean` | `false` | Whether the checkbox group is disabled. | | disabled | `boolean` | `false` | Whether the checkbox group is disabled. |
| default-value | `Array<string \| number>` | `null` | Checkbox group's default selected value. | | default-value | `Array<string \| number>` | `null` | Checkbox group's default selected value. |
| max | `number` | `undefined` | The maximum number of checkboxes that can be checked. |
| min | `number` | `undefined` | The minimum number of checkboxes that can be checked. |
| value | `Array<string \| number> \| null` | `undefined` | Controlled value of checkbox group. | | value | `Array<string \| number> \| null` | `undefined` | Controlled value of checkbox group. |
| on-update:value | `(value: string \| number)` | `undefined` | Callback when checkbox group's value changes. | | on-update:value | `(value: string \| number)` | `undefined` | Callback when checkbox group's value changes. |

View File

@ -20,10 +20,10 @@ event
| 名称 | 类型 | 默认值 | 说明 | | 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| checked | `boolean` | `false` | 受控状态下是否选中 | | checked | `boolean` | `false` | 受控状态下是否选中 |
| indeterminate | `boolean` | `false` | 是否部分选中 |
| default-checked | `boolean` | `false` | 非受控模式下默认是否选中 | | default-checked | `boolean` | `false` | 非受控模式下默认是否选中 |
| disabled | `boolean` | `false` | 是否禁用 | | disabled | `boolean` | `false` | 是否禁用 |
| focusable | `boolean` | `true` | 是否可被 focus | | focusable | `boolean` | `true` | 是否可被 focus |
| indeterminate | `boolean` | `false` | 是否部分选中 |
| label | `string \| (() => VNodeChild)` | `undefined` | Checkbox 的标签 | | label | `string \| (() => VNodeChild)` | `undefined` | Checkbox 的标签 |
| value | `string \| number` | `undefined` | Checkbox 在 checkbox group 中使用的值 | | value | `string \| number` | `undefined` | Checkbox 在 checkbox group 中使用的值 |
| on-update:checked | `(checked: boolean) => void` | `undefined` | 当 checked 改变时触发的回调函数 | | on-update:checked | `(checked: boolean) => void` | `undefined` | 当 checked 改变时触发的回调函数 |
@ -34,6 +34,8 @@ event
| --- | --- | --- | --- | | --- | --- | --- | --- |
| disabled | `boolean` | `false` | 选项组是否禁用 | | disabled | `boolean` | `false` | 选项组是否禁用 |
| default-value | `Array<string \| number>` | `null` | 选项组非受控模式下的默认值 | | default-value | `Array<string \| number>` | `null` | 选项组非受控模式下的默认值 |
| max | `number` | `undefined` | 可被勾选的 checkbox 的最大数量 |
| min | `number` | `undefined` | 可被勾选的 checkbox 的最小数量 |
| value | `Array<string \| number> \| null` | `undefined` | 选项组受控模式下的值 | | value | `Array<string \| number> \| null` | `undefined` | 选项组受控模式下的值 |
| on-update:value | `(value: string \| number)` | `undefined` | 选项组的值改变时的回调 | | on-update:value | `(value: string \| number)` | `undefined` | 选项组的值改变时的回调 |

View File

@ -94,7 +94,28 @@ export default defineComponent({
} }
}) })
const mergedDisabledRef = computed(() => { const mergedDisabledRef = computed(() => {
return props.disabled || NCheckboxGroup?.disabledRef.value if (props.disabled) return true
if (NCheckboxGroup) {
if (NCheckboxGroup.disabledRef.value) return true
const {
maxRef: { value: max },
minRef: { value: min },
checkedCountRef: { value: checkedCount }
} = NCheckboxGroup
if (max !== undefined && min === undefined) {
return checkedCount >= max && !renderedCheckedRef.value
}
if (max === undefined && min !== undefined) {
return checkedCount <= min && renderedCheckedRef.value
}
if (max !== undefined && min !== undefined) {
return (
(checkedCount <= min && renderedCheckedRef.value) ||
(checkedCount >= max && !renderedCheckedRef.value)
)
}
}
return false
}) })
const formItem = useFormItem(props, { const formItem = useFormItem(props, {
mergedSize (NFormItem) { mergedSize (NFormItem) {

View File

@ -7,7 +7,8 @@ import {
toRef, toRef,
ref, ref,
InjectionKey, InjectionKey,
Ref Ref,
ComputedRef
} from 'vue' } from 'vue'
import { useMergedState } from 'vooks' import { useMergedState } from 'vooks'
import { useConfig, useFormItem } from '../../_mixins' import { useConfig, useFormItem } from '../../_mixins'
@ -15,17 +16,21 @@ import { warn, call, MaybeArray } from '../../_utils'
import type { ExtractPublicPropTypes } from '../../_utils' import type { ExtractPublicPropTypes } from '../../_utils'
export interface CheckboxGroupInjection { export interface CheckboxGroupInjection {
checkedCountRef: ComputedRef<number>
maxRef: Ref<number | undefined>
minRef: Ref<number | undefined>
disabledRef: Ref<boolean> disabledRef: Ref<boolean>
valueSetRef: Ref<Set<string | number>> valueSetRef: Ref<Set<string | number>>
mergedSizeRef: Ref<'small' | 'medium' | 'large'> mergedSizeRef: Ref<'small' | 'medium' | 'large'>
toggleCheckbox: (checked: boolean, checkboxValue: string | number) => void toggleCheckbox: (checked: boolean, checkboxValue: string | number) => void
} }
export const checkboxGroupInjectionKey: InjectionKey<CheckboxGroupInjection> = Symbol( export const checkboxGroupInjectionKey: InjectionKey<CheckboxGroupInjection> =
'checkboxGroup' Symbol('checkboxGroup')
)
const checkboxGroupProps = { const checkboxGroupProps = {
min: Number,
max: Number,
size: String as PropType<'small' | 'medium' | 'large'>, size: String as PropType<'small' | 'medium' | 'large'>,
value: Array as PropType<Array<string | number> | null>, value: Array as PropType<Array<string | number> | null>,
defaultValue: { defaultValue: {
@ -74,6 +79,10 @@ export default defineComponent({
controlledValueRef, controlledValueRef,
uncontrolledValueRef uncontrolledValueRef
) )
const checkedCount = computed(() => {
return mergedValueRef.value?.length || 0
})
const valueSetRef = computed<Set<string | number>>(() => { const valueSetRef = computed<Set<string | number>>(() => {
if (Array.isArray(mergedValueRef.value)) { if (Array.isArray(mergedValueRef.value)) {
return new Set(mergedValueRef.value) return new Set(mergedValueRef.value)
@ -90,6 +99,7 @@ export default defineComponent({
'onUpdate:value': _onUpdateValue, 'onUpdate:value': _onUpdateValue,
onUpdateValue onUpdateValue
} = props } = props
if (Array.isArray(mergedValueRef.value)) { if (Array.isArray(mergedValueRef.value)) {
const groupValue = Array.from(mergedValueRef.value) const groupValue = Array.from(mergedValueRef.value)
const index = groupValue.findIndex((value) => value === checkboxValue) const index = groupValue.findIndex((value) => value === checkboxValue)
@ -134,6 +144,9 @@ export default defineComponent({
} }
} }
provide(checkboxGroupInjectionKey, { provide(checkboxGroupInjectionKey, {
checkedCountRef: checkedCount,
maxRef: toRef(props, 'max'),
minRef: toRef(props, 'min'),
valueSetRef: valueSetRef, valueSetRef: valueSetRef,
disabledRef: toRef(props, 'disabled'), disabledRef: toRef(props, 'disabled'),
mergedSizeRef: formItem.mergedSizeRef, mergedSizeRef: formItem.mergedSizeRef,

View File

@ -152,4 +152,132 @@ describe('n-checkbox-group', () => {
}) })
expect(wrapper.find('.n-checkbox__label').text()).toContain('test') expect(wrapper.find('.n-checkbox__label').text()).toContain('test')
}) })
it('should work with `min` prop', async () => {
const wrapper = mount(NCheckboxGroup, {
props: {
min: 1,
value: ['Shanghai']
},
slots: {
default: () => [
h(NCheckbox, { value: 'Shanghai' }, { default: () => 'Shanghai' }),
h(NCheckbox, { value: 'Beijing' }, { default: () => 'Beijing' }),
h(NCheckbox, { value: 'Shenzhen' }, { default: () => 'Shenzhen' })
]
}
})
expect(wrapper.findAll('.n-checkbox').length).toBe(3)
expect(wrapper.findAll('.n-checkbox')[0].classes()).toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[0].classes()).toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).not.toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).not.toContain(
'n-checkbox--disabled'
)
})
it('should work with `max` prop', async () => {
const wrapper = mount(NCheckboxGroup, {
props: {
max: 2,
value: ['Shanghai', 'Beijing']
},
slots: {
default: () => [
h(NCheckbox, { value: 'Shanghai' }, { default: () => 'Shanghai' }),
h(NCheckbox, { value: 'Beijing' }, { default: () => 'Beijing' }),
h(NCheckbox, { value: 'Shenzhen' }, { default: () => 'Shenzhen' })
]
}
})
expect(wrapper.findAll('.n-checkbox').length).toBe(3)
expect(wrapper.findAll('.n-checkbox')[0].classes()).not.toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[0].classes()).toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).not.toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[2].classes()).not.toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[2].classes()).toContain(
'n-checkbox--disabled'
)
})
it('should work with `max` and `min` prop', async () => {
const wrapper = mount(NCheckboxGroup, {
props: {
max: 2,
min: 1
},
slots: {
default: () => [
h(NCheckbox, { value: 'Shanghai' }, { default: () => 'Shanghai' }),
h(NCheckbox, { value: 'Beijing' }, { default: () => 'Beijing' }),
h(NCheckbox, { value: 'Shenzhen' }, { default: () => 'Shenzhen' })
]
}
})
await wrapper.setProps({
value: ['Shanghai']
})
expect(wrapper.findAll('.n-checkbox').length).toBe(3)
expect(wrapper.findAll('.n-checkbox')[0].classes()).toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[0].classes()).toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).not.toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).not.toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[2].classes()).not.toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[2].classes()).not.toContain(
'n-checkbox--disabled'
)
await wrapper.setProps({
value: ['Shanghai', 'Beijing']
})
expect(wrapper.findAll('.n-checkbox')[0].classes()).not.toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[0].classes()).toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[1].classes()).not.toContain(
'n-checkbox--disabled'
)
expect(wrapper.findAll('.n-checkbox')[2].classes()).not.toContain(
'n-checkbox--checked'
)
expect(wrapper.findAll('.n-checkbox')[2].classes()).toContain(
'n-checkbox--disabled'
)
})
}) })