mirror of
https://github.com/element-plus/element-plus.git
synced 2025-02-17 11:49:41 +08:00
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:
parent
22338c7055
commit
dbaf83e2a4
@ -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,
|
||||
},
|
||||
}
|
||||
|
387
packages/checkbox/__tests__/checkbox.spec.ts
Normal file
387
packages/checkbox/__tests__/checkbox.spec.ts
Normal 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'])
|
||||
})
|
||||
})
|
36
packages/checkbox/doc/basic.vue
Normal file
36
packages/checkbox/doc/basic.vue
Normal 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>
|
77
packages/checkbox/doc/group.vue
Normal file
77
packages/checkbox/doc/group.vue
Normal 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>
|
6
packages/checkbox/doc/index.stories.ts
Normal file
6
packages/checkbox/doc/index.stories.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as BasicUsage } from './basic.vue'
|
||||
export { default as GroupUsage } from './group.vue'
|
||||
|
||||
export default {
|
||||
title: 'Checkbox',
|
||||
}
|
10
packages/checkbox/index.ts
Normal file
10
packages/checkbox/index.ts
Normal 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)
|
||||
}
|
12
packages/checkbox/package.json
Normal file
12
packages/checkbox/package.json
Normal 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"
|
||||
}
|
||||
}
|
200
packages/checkbox/src/checkbox-button.vue
Normal file
200
packages/checkbox/src/checkbox-button.vue
Normal 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>
|
82
packages/checkbox/src/checkbox-group.vue
Normal file
82
packages/checkbox/src/checkbox-group.vue
Normal 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>
|
222
packages/checkbox/src/checkbox.vue
Normal file
222
packages/checkbox/src/checkbox.vue
Normal 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>
|
24
packages/checkbox/src/useCheckbox.ts
Normal file
24
packages/checkbox/src/useCheckbox.ts
Normal 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,
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user