Feat/checkbox (#131)

* feat(checkbox): init checkbox

* feat(checkbox): update checkbox-button checkbox-group

* feat(checkbox): init unit test

* feat(checkbox): update unit test

* fix(checkbox): improve some code

* test(checkbox): update test case
This commit is contained in:
hangzou 2020-08-13 15:16:38 +08:00 committed by GitHub
parent 22338c7055
commit dbaf83e2a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1079 additions and 2 deletions

View File

@ -54,5 +54,6 @@ module.exports = {
],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'vue/html-closing-bracket-spacing': 'error',
'@typescript-eslint/no-explicit-any': 0,
},
}

View File

@ -0,0 +1,387 @@
import { mount } from '@vue/test-utils'
import Checkbox from '../src/checkbox.vue'
import CheckboxButton from '../src/checkbox-button.vue'
import CheckboxGroup from '../src/checkbox-group.vue'
const _mount = <T={data: any; checkList: [];}>(template: string, data, otherObj?) => mount<T>({
components: {
'el-checkbox': Checkbox,
'el-checkbox-group': CheckboxGroup,
'el-checkbox-button': CheckboxButton,
},
template,
data,
...otherObj,
}, {
global: {
provide: {
elForm: {},
elFormItem: {},
CheckboxGroup: {},
},
},
})
describe('Checkbox', () => {
test('create', async (done) => {
const wrapper = _mount('<el-checkbox v-model="checkbox" label="a"/>', () => ({ checkbox: false }))
const vm = wrapper.vm
expect(wrapper.classes()).toContain('el-checkbox')
await wrapper.trigger('click')
vm.$nextTick(async () => {
expect(wrapper.classes()).toContain('is-checked')
await wrapper.trigger('click')
expect(wrapper.classes('is-checked')).toBe(false)
done()
})
})
test('disabled', async (done) => {
const wrapper = _mount('<el-checkbox v-model="checkbox" disabled label="a"/>', () => ({ checkbox: false }))
const vm = wrapper.vm
expect(wrapper.classes()).toContain('is-disabled')
await wrapper.trigger('click')
vm.$nextTick(() => {
expect(wrapper.classes()).toContain('is-disabled')
done()
})
})
test('change event', async (done) => {
const wrapper = _mount(
`
<el-checkbox v-model="checked" @change="onChange" />
`,
() => ({
data: '',
checked: false,
}),
{
methods: {
onChange(val) {
this.data = val
},
},
},
)
const vm = wrapper.vm
await wrapper.trigger('click')
vm.$nextTick(() => {
expect(vm.data).toBe(true)
done()
})
})
test('checkbox group', async (done) => {
const wrapper = _mount(
`
<el-checkbox-group v-model="checkList">
<el-checkbox label="a" ref="a"></el-checkbox>
<el-checkbox label="b" ref="b"></el-checkbox>
<el-checkbox label="c" ref="c"></el-checkbox>
<el-checkbox label="d" ref="d"></el-checkbox>
</el-checkbox-group>
`,
() => ({ checkList: [] }),
)
const vm = wrapper.vm
expect(vm.checkList.length).toBe(0)
await wrapper.findComponent({ ref: 'a' }).trigger('click')
vm.$nextTick(async () => {
expect(vm.checkList.length).toBe(1)
expect(vm.checkList).toContain('a')
await wrapper.findComponent({ ref: 'b' }).trigger('click')
vm.$nextTick(() => {
expect(vm.checkList.length).toBe(2)
expect(vm.checkList).toContain('a')
expect(vm.checkList).toContain('b')
done()
})
})
})
test('checkbox group change', async done => {
const wrapper = _mount(
`
<el-checkbox-group v-model="checkList" @change="onChange">
<el-checkbox label="a" ref="a"></el-checkbox>
<el-checkbox label="b" ref="b"></el-checkbox>
</el-checkbox-group>
`,
() => ({ checkList: [] }),
{
methods: {
onChange(val) {
this.data = val
},
},
},
)
const vm = wrapper.vm
await wrapper.findComponent({ ref: 'a' }).trigger('click')
vm.$nextTick(() => {
expect(vm.data.length).toBe(1)
expect(vm.data).toEqual(['a'])
done()
})
})
test('nested group', async done => {
const wrapper = _mount(
`
<el-checkbox-group v-model="checkList">
<div>
<el-checkbox label="a" ref="a"></el-checkbox>
<el-checkbox label="b" ref="b"></el-checkbox>
<el-checkbox label="c" ref="c"></el-checkbox>
<el-checkbox label="d" ref="d"></el-checkbox>
</div>
</el-checkbox-group>
`,
() => ({ checkList: [] }),
)
const vm = wrapper.vm
expect(vm.checkList.length).toBe(0)
await wrapper.findComponent({ ref: 'a' }).trigger('click')
vm.$nextTick(() => {
expect(vm.checkList).toEqual(['a'])
done()
})
})
test('true false lable', async done => {
const wrapper = _mount(
`<el-checkbox true-label="a" :false-label="3" v-model="checked"></el-checkbox>`,
() => ({
checked: 'a',
}),
)
const vm = wrapper.vm
await wrapper.trigger('click')
vm.$nextTick(() => {
expect((vm as any).checked).toBe(3)
done()
})
})
test('check', () => {
const wrapper = _mount(
`
<div>
<el-checkbox v-model="checked" checked></el-checkbox>
<el-checkbox-group v-model="checklist">
<el-checkbox checked label="a"></el-checkbox>
</el-checkbox-group>
</div>
`,
() => ({
checked: false,
checklist: [],
}),
) as any
expect(wrapper.vm.checked).toBe(true)
expect(wrapper.vm.checklist).toEqual(['a'])
})
})
describe('check-button', () => {
test('create', async (done) => {
const wrapper = _mount('<el-checkbox-button v-model="checkbox" label="a"/>', () => ({ checkbox: false }))
const vm = wrapper.vm
expect(wrapper.classes()).toContain('el-checkbox-button')
await wrapper.trigger('click')
vm.$nextTick(async () => {
expect(wrapper.classes()).toContain('is-checked')
await wrapper.trigger('click')
expect(wrapper.classes('is-checked')).toBe(false)
done()
})
})
test('disabled', async (done) => {
const wrapper = _mount('<el-checkbox-button v-model="checkbox" disabled label="a"/>', () => ({ checkbox: false }))
const vm = wrapper.vm
expect(wrapper.classes()).toContain('is-disabled')
await wrapper.trigger('click')
vm.$nextTick(() => {
expect(wrapper.classes()).toContain('is-disabled')
done()
})
})
test('change event', async (done) => {
const wrapper = _mount(
`
<el-checkbox-button v-model="checked" @change="onChange" />
`,
() => ({
data: '',
checked: false,
}),
{
methods: {
onChange(val) {
this.data = val
},
},
},
)
const vm = wrapper.vm
await wrapper.trigger('click')
vm.$nextTick(() => {
expect(vm.data).toBe(true)
done()
})
})
test('button group change', async done => {
const wrapper = _mount(
`
<el-checkbox-group v-model="checkList" @change="onChange">
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-checkbox-group>
`,
() => ({
data: '',
checkList: [],
}),
{
methods: {
onChange(val) {
this.data = val
},
},
},
)
const vm = wrapper.vm
await wrapper.findComponent({ ref: 'a' }).trigger('click')
vm.$nextTick(async () => {
expect(vm.data).toEqual(['a'])
await wrapper.findComponent({ ref: 'b' }).trigger('click')
vm.$nextTick(() => {
expect(vm.data).toEqual(['a', 'b'])
done()
})
})
})
test('button group props', () => {
const wrapper = _mount(
`
<el-checkbox-group v-model="checkList" size="large" fill="#FF0000" text-color="#000">
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-checkbox-group>
`,
() => ({ checkList: ['a', 'b'] }),
)
const vm = wrapper.vm
expect(vm.checkList.length).toBe(2)
expect((vm.$refs.a as any).$el.classList.contains('is-checked')).toBe(true)
expect((vm.$refs.a as any).$el.querySelector('.el-checkbox-button__inner').style.borderColor).toEqual('#ff0000')
})
test('button group min and max', async done => {
const wrapper = _mount(
`
<el-checkbox-group
v-model="checkList"
:min="1"
:max="2"
>
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-checkbox-group>
`,
() => ({
checkList: ['a'],
lastEvent: null,
}),
)
const vm = wrapper.vm
expect(vm.checkList.length).toBe(1)
await wrapper.findComponent({ ref: 'a' }).trigger('click')
vm.$nextTick(async () => {
expect(vm.checkList.length).toBe(1)
await wrapper.findComponent({ ref: 'b' }).trigger('click')
vm.$nextTick(async () => {
expect(vm.checkList.length).toBe(2)
await wrapper.findComponent({ ref: 'c' }).trigger('click')
vm.$nextTick(() => {
expect(vm.checkList.length).toBe(2)
expect(vm.checkList).toEqual(['a', 'b'])
expect((wrapper.findComponent({ ref: 'c' }).vm as any).isDisabled).toBe(true)
expect((wrapper.findComponent({ ref: 'd' }).vm as any).isDisabled).toBe(true)
done()
})
})
})
})
test('nested group', async done => {
const wrapper = _mount(
`
<el-checkbox-group v-model="checkList">
<div>
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</div>
</el-checkbox-group>
`,
() => ({ checkList: [] }),
)
const vm = wrapper.vm
expect(vm.checkList.length).toBe(0)
await wrapper.findComponent({ ref: 'a' }).trigger('click')
vm.$nextTick(() => {
expect(vm.checkList).toEqual(['a'])
done()
})
})
test('true false lable', async done => {
const wrapper = _mount(
`<el-checkbox-button true-label="a" :false-label="3" v-model="checked" />`,
() => ({
checked: 'a',
}),
)
const vm = wrapper.vm
await wrapper.trigger('click')
vm.$nextTick(() => {
expect((vm as any).checked).toBe(3)
done()
})
})
test('check', () => {
const wrapper = _mount(
`
<div>
<el-checkbox-button v-model="checked" checked />
<el-checkbox-group v-model="checklist">
<el-checkbox-button checked label="a" />
</el-checkbox-group>
</div>
`,
() => ({
checked: false,
checklist: [],
}),
) as any
expect(wrapper.vm.checked).toBe(true)
expect(wrapper.vm.checklist).toEqual(['a'])
})
})

View File

@ -0,0 +1,36 @@
<template>
<div class="block">
<el-checkbox v-model="checked" @change="onChange">{{ checked }}</el-checkbox>
<el-checkbox v-model="checked1" disabled>{{ checked1 }}</el-checkbox>
<el-checkbox v-model="checked2" disabled>{{ checked2 }}</el-checkbox>
<el-checkbox v-model="checked1" label="A" border />
<el-checkbox v-model="checked2" label="B" border />
<el-checkbox-group v-model="checkList">
<el-checkbox label="A" />
<el-checkbox label="B" />
<el-checkbox label="C" />
<el-checkbox label="D" disabled />
<el-checkbox label="Ha" disabled />
</el-checkbox-group>
{{ checkList }}
</div>
</template>
<script lang="tsx">
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
checked: false,
checked1: false,
checked2: true,
checkList: ['Ha','A'],
}
},
methods: {
onChange(val) {
console.log(val)
},
},
})
</script>

View File

@ -0,0 +1,77 @@
<template>
<div class="block">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">All Selected</el-checkbox>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkedCities" @change="handleCheckedCitiesChange">
<el-checkbox v-for="city in cities" :key="city" :label="city">{{ city }}</el-checkbox>
</el-checkbox-group>
</div>
<div class="block">
<el-checkbox-group
v-model="checkedCities"
:min="1"
:max="2"
>
<el-checkbox v-for="city in cities" :key="city" :label="city">{{ city }}</el-checkbox>
</el-checkbox-group>
</div>
<div class="block">
<div>
<el-checkbox-group v-model="checkboxGroup1">
<el-checkbox-button v-for="city in cities" :key="city" :label="city">{{ city }}</el-checkbox-button>
</el-checkbox-group>
</div>
<div style="margin-top: 20px">
<el-checkbox-group v-model="checkboxGroup2" size="medium">
<el-checkbox-button v-for="city in cities" :key="city" :label="city">{{ city }}</el-checkbox-button>
</el-checkbox-group>
</div>
<div style="margin-top: 20px">
<el-checkbox-group v-model="checkboxGroup3" size="small">
<el-checkbox-button
v-for="city in cities"
:key="city"
:label="city"
:disabled="city === 'London'"
>
{{ city }}
</el-checkbox-button>
</el-checkbox-group>
</div>
<div style="margin-top: 20px">
<el-checkbox-group v-model="checkboxGroup4" size="mini" disabled>
<el-checkbox-button v-for="city in cities" :key="city" :label="city">{{ city }}</el-checkbox-button>
</el-checkbox-group>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
const cityOptions = ['NYC', 'London', 'Tokyo', 'Singapore']
export default defineComponent({
data() {
return {
checkAll: false,
checkedCities: ['NYC', 'London'],
cities: cityOptions,
isIndeterminate: true,
checkboxGroup1: ['NYC'],
checkboxGroup2: ['NYC'],
checkboxGroup3: ['NYC'],
checkboxGroup4: ['NYC'],
}
},
methods: {
handleCheckAllChange(val) {
this.checkedCities = val ? cityOptions : []
this.isIndeterminate = false
},
handleCheckedCitiesChange(value) {
let checkedCount = value.length
this.checkAll = checkedCount === this.cities.length
this.isIndeterminate = checkedCount > 0 && checkedCount < this.cities.length
},
},
})
</script>

View File

@ -0,0 +1,6 @@
export { default as BasicUsage } from './basic.vue'
export { default as GroupUsage } from './group.vue'
export default {
title: 'Checkbox',
}

View File

@ -0,0 +1,10 @@
import { App } from 'vue'
import Checkbox from './src/checkbox.vue'
import CheckboxButton from './src/checkbox-button.vue'
import CheckboxGroup from './src/checkbox-group.vue'
export default (app: App): void => {
app.component(Checkbox.name, Checkbox)
app.component(CheckboxButton.name, CheckboxButton)
app.component(CheckboxGroup.name, CheckboxGroup)
}

View File

@ -0,0 +1,12 @@
{
"name": "@element-plus/checkbox",
"version": "0.0.0",
"main": "dist/index.js",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0-rc.1"
},
"devDependencies": {
"@vue/test-utils": "^2.0.0-beta.0"
}
}

View File

@ -0,0 +1,200 @@
<template>
<label
class="el-checkbox-button"
:class="[
size ? 'el-checkbox-button--' + size : '',
{ 'is-disabled': isDisabled },
{ 'is-checked': isChecked },
{ 'is-focus': focus },
]"
role="checkbox"
:aria-checked="isChecked"
:aria-disabled="isDisabled"
>
<input
v-if="trueLabel || falseLabel"
v-model="model"
class="el-checkbox-button__original"
type="checkbox"
:name="name"
:disabled="isDisabled"
:true-value="trueLabel"
:false-value="falseLabel"
@change="handleChange"
@focus="focus = true"
@blur="focus = false"
>
<input
v-else
v-model="model"
class="el-checkbox-button__original"
type="checkbox"
:name="name"
:disabled="isDisabled"
:value="label"
@change="handleChange"
@focus="focus = true"
@blur="focus = false"
>
<span
v-if="$slots.default || label"
class="el-checkbox-button__inner"
:style="isChecked ? activeStyle : null"
>
<slot>{{ label }}</slot>
</span>
</label>
</template>
<script lang='ts'>
import {
defineComponent,
ref,
computed,
// nextTick,
watch,
} from 'vue'
import { useCheckbox } from './useCheckbox'
export default defineComponent({
name: 'ElCheckboxButton',
props: {
modelValue: {
type: [Object, Boolean],
default: () => undefined,
},
label: {
type: [Object, Boolean, String],
default: () => ({}),
},
indeterminate: Boolean,
disabled: Boolean,
checked: Boolean,
name: {
type: String,
default: undefined,
},
trueLabel: {
type: [String, Number],
default: undefined,
},
falseLabel: {
type: [String, Number],
default: undefined,
},
},
emits: ['update:modelValue', 'change'],
setup(props, { emit }) {
const { elForm, isGroup, _checkboxGroup, _elFormItemSize, elFormItem, ELEMENT } = useCheckbox()
const selfModel = ref(false)
const focus = ref(false)
const isLimitExceeded = ref(false)
const store = computed(() => _checkboxGroup ? _checkboxGroup.modelValue.value : props.modelValue)
const model = computed({
get() {
return isGroup.value ? store.value : props.modelValue !== undefined ? props.modelValue : selfModel
},
set(val: any) {
if (isGroup.value) {
isLimitExceeded.value = false
if (_checkboxGroup.min !== undefined && val.length < _checkboxGroup.min) {
isLimitExceeded.value = true
}
if (_checkboxGroup.max !== undefined && val.length > _checkboxGroup.max) {
isLimitExceeded.value = true
}
isLimitExceeded.value === false && _checkboxGroup.changeEvent?.(val)
} else {
emit('update:modelValue', val)
selfModel.value = val
}
},
})
const isChecked = computed(() => {
if (Object.prototype.toString.call(model.value) === '[object Boolean]') {
return model.value
} else if (Array.isArray(model.value)) {
return model.value.includes(props.label)
} else if (model.value !== null && model.value !== undefined) {
return model.value === props.trueLabel
}
})
const isLimitDisabled = computed(() => {
const max = _checkboxGroup.max
const min = _checkboxGroup.min
return !!(max || min) && (model.value.length >= max && !isChecked.value) ||
(model.value.length <= min && isChecked.value)
})
const isDisabled = computed(() => {
return isGroup.value
? _checkboxGroup.disabled || props.disabled || (elForm as any || {} as any).disabled || isLimitDisabled.value
: props.disabled || (elForm as any || {} as any).disabled
})
const activeStyle = computed(() => {
return {
backgroundColor: _checkboxGroup.fill || '',
borderColor: _checkboxGroup.fill || '',
color: _checkboxGroup.textColor || '',
'box-shadow': '-1px 0 0 0 ' + _checkboxGroup.fill,
}
})
const size = computed(() => _checkboxGroup.checkboxGroupSize || _elFormItemSize || (ELEMENT || {}).size)
function addToStore() {
if (
Array.isArray(model.value) &&
!model.value.includes(props.label)
) {
model.value.push(props.label)
} else {
model.value = props.trueLabel || true
}
}
function handleChange(e: UIEvent) {
if (isLimitExceeded.value) return
let value = ref(undefined)
if ((e.target as HTMLInputElement).checked) {
value.value = props.trueLabel === undefined ? true : props.trueLabel
} else {
value.value = props.falseLabel === undefined ? false : props.falseLabel
}
emit('change', value.value, e)
/**
* to discuss it's useful
*/
// nextTick(() => {
// if (isGroup.value) {
// _checkboxGroup.changeEvent?.(_checkboxGroup.modelValue.value)
// }
// })
}
watch(() => props.modelValue, (val) => {
elFormItem.changeEvent?.(val)
})
props.checked && addToStore()
return {
focus,
isChecked,
isDisabled,
model,
handleChange,
activeStyle,
size,
}
},
})
</script>
<style scoped>
</style>

View File

@ -0,0 +1,82 @@
<template>
<div class="el-checkbox-group" role="group" aria-label="checkbox-group">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, watch, provide, nextTick } from 'vue'
import { useCheckbox } from './useCheckbox'
export default defineComponent({
name: 'ElCheckboxGroup',
props: {
modelValue: {
type: [Object, Boolean, Array],
default: () => undefined,
},
disabled: Boolean,
min: {
type: Number,
default: undefined,
},
max: {
type: Number,
default: undefined,
},
size: {
type: String,
default: undefined,
},
fill: {
type: String,
default: undefined,
},
textColor: {
type: String,
default: undefined,
},
},
emits: ['update:modelValue', 'change'],
setup(props, ctx) {
const { elFormItem, _elFormItemSize, ELEMENT } = useCheckbox()
const checkboxGroupSize = computed(() => props.size || _elFormItemSize.value || (ELEMENT || {}).size)
const changeEvent = value => {
ctx.emit('update:modelValue', value)
nextTick(() => {
ctx.emit('change', value)
})
}
const modelValue = computed({
get() {
return props.modelValue
},
set(val) {
changeEvent(val)
},
})
provide('CheckboxGroup', {
name: 'ElCheckboxGroup',
modelValue,
disabled: props.disabled,
min: props.min,
max: props.max,
size: props.size,
fill: props.fill,
textColor: props.textColor,
checkboxGroupSize: checkboxGroupSize.value,
changeEvent: changeEvent,
})
watch(() => props.modelValue, val => {
elFormItem.changeEvent?.(val)
})
},
})
</script>

View File

@ -0,0 +1,222 @@
<template>
<label
:id="id"
class="el-checkbox"
:class="[
border && checkboxSize ? 'el-checkbox--' + checkboxSize : '',
{ 'is-disabled': isDisabled },
{ 'is-bordered': border },
{ 'is-checked': isChecked }
]"
>
<span
class="el-checkbox__input"
:class="{
'is-disabled': isDisabled,
'is-checked': isChecked,
'is-indeterminate': indeterminate,
'is-focus': focus
}"
:tabindex="indeterminate ? 0 : false"
:role="indeterminate ? 'checkbox' : false"
:aria-checked="indeterminate ? 'mixed' : false"
>
<span class="el-checkbox__inner"></span>
<input
v-if="trueLabel || falseLabel"
v-model="model"
class="el-checkbox__original"
type="checkbox"
:aria-hidden="indeterminate ? 'true' : 'false'"
:name="name"
:disabled="isDisabled"
:true-value="trueLabel"
:false-value="falseLabel"
@change="handleChange"
@focus="focus = true"
@blur="focus = false"
>
<input
v-else
v-model="model"
class="el-checkbox__original"
type="checkbox"
:aria-hidden="indeterminate ? 'true' : 'false'"
:disabled="isDisabled"
:value="label"
:name="name"
@change="handleChange"
@focus="focus = true"
@blur="focus = false"
>
</span>
<span v-if="$slots.default || label" class="el-checkbox__label">
<slot></slot>
<template v-if="!$slots.default">{{ label }}</template>
</span>
</label>
</template>
<script lang='ts'>
import {
defineComponent,
ref,
computed,
getCurrentInstance,
watch,
onMounted,
// nextTick,
} from 'vue'
import { useCheckbox } from './useCheckbox'
export default defineComponent({
name: 'ElCheckbox',
props: {
modelValue: {
type: [Object, Boolean],
default: () => undefined,
},
label: {
type: [Object, Boolean, String],
default: () => ({}),
},
indeterminate: Boolean,
disabled: Boolean,
checked: Boolean,
name: {
type: String,
default: undefined,
},
trueLabel: {
type: [String, Number],
default: undefined,
},
falseLabel: {
type: [String, Number],
default: undefined,
},
id: {
type: String,
default: undefined,
},
controls: {
type: String,
default: undefined,
},
border: Boolean,
size: {
type: String,
default: undefined,
},
},
emits: ['update:modelValue', 'change'],
setup(props, { emit }) {
const { elForm, isGroup, _checkboxGroup, _elFormItemSize, elFormItem, ELEMENT } = useCheckbox()
const instance = getCurrentInstance()
const selfModel = ref(false)
const focus = ref(false)
const isLimitExceeded = ref(false)
const store = computed(() => _checkboxGroup ? _checkboxGroup.modelValue.value : props.modelValue)
const model = computed({
get() {
return isGroup.value ? store.value : props.modelValue !== undefined ? props.modelValue : selfModel
},
set(val: any) {
if (isGroup.value) {
isLimitExceeded.value = false
if (_checkboxGroup.min !== undefined && val.length < _checkboxGroup.min) {
isLimitExceeded.value = true
}
if (_checkboxGroup.max !== undefined && val.length > _checkboxGroup.max) {
isLimitExceeded.value = true
}
isLimitExceeded.value === false && _checkboxGroup.changeEvent?.(val)
} else {
emit('update:modelValue', val)
selfModel.value = val
}
},
})
const isChecked = computed(() => {
if (Object.prototype.toString.call(model.value) === '[object Boolean]') {
return model.value
} else if (Array.isArray(model.value)) {
return model.value.includes(props.label)
} else if (model.value !== null && model.value !== undefined) {
return model.value === props.trueLabel
}
})
const isLimitDisabled = computed(() => {
const max = _checkboxGroup.max
const min = _checkboxGroup.min
return !!(max || min) && (model.value.length >= max && !isChecked.value) ||
(model.value.length <= min && isChecked.value)
})
const isDisabled = computed(() => {
return isGroup.value
? _checkboxGroup.disabled || props.disabled || (elForm as any || {} as any).disabled || isLimitDisabled.value
: props.disabled || (elForm as any || {} as any).disabled
})
const checkboxSize = computed(() => {
const temCheckboxSize = props.size || _elFormItemSize.value || (ELEMENT || {} as any).size
return isGroup.value
? _checkboxGroup.checkboxGroupSize || temCheckboxSize
: temCheckboxSize
})
function addToStore() {
if (
Array.isArray(model.value) &&
!model.value.includes(props.label)
) {
model.value.push(props.label)
} else {
model.value = props.trueLabel || true
}
}
function handleChange(e: UIEvent) {
if (isLimitExceeded.value) return
let value = ref(undefined)
if ((e.target as HTMLInputElement).checked) {
value.value = props.trueLabel === undefined ? true : props.trueLabel
} else {
value.value = props.falseLabel === undefined ? false : props.falseLabel
}
emit('change', value.value, e)
/**
* to discuss does it is useful
*/
// nextTick(() => {
// if (isGroup.value) {
// _checkboxGroup.changeEvent?.(_checkboxGroup.modelValue.value)
// }
// })
}
watch(() => props.modelValue, (val) => {
elFormItem.changeEvent?.(val)
})
props.checked && addToStore()
onMounted(() => {
instance.vnode.el.setAttribute('aria-controls', props.controls)
})
return {
focus,
isChecked,
isDisabled,
checkboxSize,
model,
handleChange,
}
},
})
</script>
<style scoped>
</style>

View File

@ -0,0 +1,24 @@
import { ref, computed, inject } from 'vue'
export const useCheckbox = () => {
//todo: ELEMENT
const ELEMENT = null
const elForm = inject('elForm', {})
const elFormItem = inject('elFormItem', {}) as any
const _checkboxGroup = inject('CheckboxGroup', {}) as any
const focus = ref(false)
const isGroup = computed(() => _checkboxGroup && _checkboxGroup.name === 'ElCheckboxGroup')
const _elFormItemSize = computed(() => {
return (elFormItem || {} as any).elFormItemSize
})
return {
isGroup,
focus,
_checkboxGroup,
elForm,
ELEMENT,
_elFormItemSize,
elFormItem,
}
}

View File

@ -4,6 +4,7 @@ import ElBacktop from '@element-plus/backtop'
import ElButton from '@element-plus/button'
import ElBadge from '@element-plus/badge'
import ElCard from '@element-plus/card'
import ElCheckbox from '@element-plus/checkbox'
import ElTag from '@element-plus/tag'
import ElLayout from '@element-plus/layout'
import ElDivider from '@element-plus/divider'
@ -28,6 +29,7 @@ export {
ElButton,
ElBadge,
ElCard,
ElCheckbox,
ElDivider,
ElTag,
ElTimeline,
@ -51,6 +53,7 @@ export default function install(app: App): void {
ElButton(app)
ElBadge(app)
ElCard(app)
ElCheckbox(app)
ElTag(app)
ElLayout(app)
ElDivider(app)

View File

@ -20,7 +20,6 @@
"@element-plus/button": "^0.0.0",
"@element-plus/breadcrumb": "^0.0.0",
"@element-plus/card": "^0.0.0",
"@element-plus/tag": "^0.0.0",
"@element-plus/timeline": "^0.0.0",
"@element-plus/divider": "^0.0.0",
"@element-plus/icon": "^0.0.0",

View File

@ -7,6 +7,11 @@ describe('Rate.vue', () => {
props: {
max: 10,
},
global: {
provide: {
elForm: {},
},
},
})
const stars = wrapper.findAll('.el-rate__item')
expect(stars.length).toEqual(10)
@ -19,6 +24,11 @@ describe('Rate.vue', () => {
modelValue: 4,
texts: ['1', '2', '3', '4', '5'],
},
global: {
provide: {
elForm: {},
},
},
})
const text = wrapper.find('.el-rate__text').element
expect(text.textContent).toEqual('4')
@ -29,6 +39,11 @@ describe('Rate.vue', () => {
props: {
modelValue: 0,
},
global: {
provide: {
elForm: {},
},
},
})
const vm = wrapper.vm
await wrapper.setProps({ modelValue: 3 })
@ -52,7 +67,10 @@ describe('Rate.vue', () => {
'el-rate': Rate,
},
}, {
props: {
global: {
provide: {
elForm: {},
},
},
})
const vm = wrapper.vm