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)
}