diff --git a/.playground/pages/composant/selectCheckbox.vue b/.playground/pages/composant/selectCheckbox.vue index cc1df30..4764d96 100644 --- a/.playground/pages/composant/selectCheckbox.vue +++ b/.playground/pages/composant/selectCheckbox.vue @@ -100,6 +100,29 @@ /> +
+

Tout sélectionner

+ +
+ +
+

Tout sélectionner (label custom)

+ +
+

Liste longue

>([]) const successValue = ref>(['be']) const disabledValue = ref>(['ca']) const emptyValue = ref>([]) +const selectAllValue = ref>([]) +const selectAllCustomValue = ref>([]) const longListValue = ref>([]) const bottomValue = ref>([]) diff --git a/app/components/malio/SelectCheckbox.test.ts b/app/components/malio/SelectCheckbox.test.ts index 0092f16..8b87322 100644 --- a/app/components/malio/SelectCheckbox.test.ts +++ b/app/components/malio/SelectCheckbox.test.ts @@ -23,6 +23,8 @@ type SelectCheckboxProps = { textLabel?: string rounded?: string displayTag?: boolean + displaySelectAll?: boolean + selectAllLabel?: string disabled?: boolean } @@ -92,4 +94,85 @@ describe('MalioSelectCheckbox', () => { expect(wrapper.text()).not.toContain('0/3') expect(wrapper.text()).toContain('Aucune selection') }) + + it('does not show select all checkbox by default', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: [], options}, + }) + + await wrapper.get('button').trigger('click') + + const checkboxes = wrapper.findAll('input[type="checkbox"]') + expect(checkboxes).toHaveLength(options.length) + }) + + it('shows select all checkbox when displaySelectAll is true', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: [], options, displaySelectAll: true}, + }) + + await wrapper.get('button').trigger('click') + + const checkboxes = wrapper.findAll('input[type="checkbox"]') + expect(checkboxes).toHaveLength(options.length + 1) + expect(wrapper.text()).toContain('Tout sélectionner') + }) + + it('shows custom select all label', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: [], options, displaySelectAll: true, selectAllLabel: 'Sélectionner tout'}, + }) + + await wrapper.get('button').trigger('click') + + expect(wrapper.text()).toContain('Sélectionner tout') + }) + + it('emits all values when select all is clicked and none selected', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: [], options, displaySelectAll: true}, + }) + + await wrapper.get('button').trigger('click') + + const checkboxes = wrapper.findAll('input[type="checkbox"]') + await checkboxes[0].setValue(true) + + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([['fr', 'be', 'ca']]) + }) + + it('emits empty array when select all is clicked and all selected', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: ['fr', 'be', 'ca'], options, displaySelectAll: true}, + }) + + await wrapper.get('button').trigger('click') + + const checkboxes = wrapper.findAll('input[type="checkbox"]') + await checkboxes[0].setValue(false) + + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([[]]) + }) + + it('select all checkbox is checked when all options are selected', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: ['fr', 'be', 'ca'], options, displaySelectAll: true}, + }) + + await wrapper.get('button').trigger('click') + + const checkboxes = wrapper.findAll('input[type="checkbox"]') + expect((checkboxes[0].element as HTMLInputElement).checked).toBe(true) + }) + + it('select all checkbox is unchecked when not all options are selected', async () => { + const wrapper = mount(SelectCheckboxForTest, { + props: {modelValue: ['fr'], options, displaySelectAll: true}, + }) + + await wrapper.get('button').trigger('click') + + const checkboxes = wrapper.findAll('input[type="checkbox"]') + expect((checkboxes[0].element as HTMLInputElement).checked).toBe(false) + }) }) diff --git a/app/components/malio/SelectCheckbox.vue b/app/components/malio/SelectCheckbox.vue index 7778882..bb3547d 100644 --- a/app/components/malio/SelectCheckbox.vue +++ b/app/components/malio/SelectCheckbox.vue @@ -143,6 +143,21 @@ : 'border-m-primary' ]" > +
  • + +
  • (), { options: () => [], @@ -227,6 +244,8 @@ const props = withDefaults(defineProps<{ textLabel: 'text-sm', rounded: 'rounded-md', displayTag: false, + displaySelectAll: false, + selectAllLabel: 'Tout sélectionner', disabled: false, }) @@ -330,6 +349,19 @@ function toggle() { open() } +const allSelected = computed(() => + normalizedOptions.value.length > 0 + && normalizedOptions.value.every(opt => props.modelValue.includes(opt.value)), +) + +function toggleAll() { + if (allSelected.value) { + emit('update:modelValue', []) + } else { + emit('update:modelValue', normalizedOptions.value.map(opt => opt.value)) + } +} + function isChecked(value: string | number) { return props.modelValue.includes(value) }