Skip to content

Commit

Permalink
fix: handling of selectAll toggle when disabled elements are present
Browse files Browse the repository at this point in the history
  • Loading branch information
mwargan committed Apr 11, 2024
1 parent af6d490 commit aceb658
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 8 deletions.
5 changes: 3 additions & 2 deletions src/components/DropdownSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const emit = defineEmits<{
const {
normalisedOptions,
selecteableOptions,
getLabel,
isOptionSelected,
toggleAllOptions,
Expand Down Expand Up @@ -199,7 +200,7 @@ const showSelectAll = computed(() => {
if (!props.multiple) return false;
// If we are in search, we don't show it because we don't want to confuse the user user about what is being selected
if (props.search) return false;
if (!normalisedOptions.value) return false;
if (!selecteableOptions.value) return false;
return true;
});
Expand Down Expand Up @@ -328,7 +329,7 @@ watch(
<label>
<input
type="checkbox"
:checked="props.modelValue.length === normalisedOptions?.length"
:checked="props.modelValue.length === selecteableOptions?.length"
@click="toggleAllOptions"
:disabled="props.disabled"
value="all"
Expand Down
51 changes: 50 additions & 1 deletion src/components/__tests__/DropdownSelect.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";

import { mount } from "@vue/test-utils";
import { DOMWrapper, mount } from "@vue/test-utils";
import DropdownSelect from "../DropdownSelect.vue";
import "html-validate/vitest";

Expand Down Expand Up @@ -404,6 +404,55 @@ describe("Dropdown Select", () => {
expect(wrapper.emitted("update:modelValue")?.[1]).toEqual([[]]);
});

it("allows to toggle select all on and off when one of the options is disabled", async () => {
const wrapper = mount(DropdownSelect, {
props: {
selectAll: true,
multiple: true,
"onUpdate:modelValue": (value) => {
wrapper.setProps({ modelValue: value });
},
options: [
"One",
"Two",
"Three",
{ id: "disabledTest", render: "Disabled", disabled: true },
],
},
});

// There should be 5 options, the 4 passed and the selectAll
const options = wrapper.findAll("label");
expect(options.length).toBe(5);

// Clicking selectAll should check all options except the disabled one
const selectAll = wrapper.find(
"input[type='checkbox'][value='all']"
) as DOMWrapper<HTMLInputElement>;
await selectAll.trigger("click");
expect(wrapper.emitted("update:modelValue")?.[0]).toEqual([
["One", "Two", "Three"],
]);

// Clicking selectAll again should uncheck all options
await selectAll.trigger("click");
expect(wrapper.emitted("update:modelValue")?.[1]).toEqual([[]]);

// Clicking on the first option should check it, but keep the selectAll unchecked
await options[1].trigger("click");
expect(wrapper.emitted("update:modelValue")?.[2]).toEqual([["One"]]);
expect(selectAll.element.checked).toBeFalsy();

// Checking the rest of the options should make the selectAll checked
await options[2].trigger("click");
await options[3].trigger("click");
expect(wrapper.emitted("update:modelValue")?.[4]).toEqual([
["One", "Two", "Three"],
]);

expect(selectAll.element.checked).toBe(true);
});

it("should render a large number of options, 1000000, in less than 400ms", () => {
const options = Array.from({ length: 1000000 }, (_, i) => `Option ${i}`);
const start = performance.now();
Expand Down
15 changes: 10 additions & 5 deletions src/stories/Composables/useMultiselect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export function useMultiselect(props: requiredProps, emit: requiredEmits) {
return normaliseOptions(toRaw(props.options));
});

// The selecteable options, which are the ones that are not disabled
const selecteableOptions = computed(() => {
return normalisedOptions.value.filter((option) => !option.disabled);
});

const getLabel = (option: selectOption) => {
if (typeof option === "string") return option;
return option[props.displayKey];
Expand Down Expand Up @@ -56,10 +61,9 @@ export function useMultiselect(props: requiredProps, emit: requiredEmits) {
};

const selectAllOptions = () => {
const allIds = normalisedOptions.value
// First skip the disabled options
.filter((option) => !option.disabled)
.map((option) => option[props.modelKey]);
const allIds = selecteableOptions.value.map(
(option) => option[props.modelKey]
);
emit("update:modelValue", allIds);
};

Expand All @@ -68,14 +72,15 @@ export function useMultiselect(props: requiredProps, emit: requiredEmits) {
};

const toggleAllOptions = () => {
if (props.modelValue.length === normalisedOptions.value.length) {
if (props.modelValue.length === selecteableOptions.value.length) {
return unselectAllOptions();
}
return selectAllOptions();
};

return {
normalisedOptions,
selecteableOptions,
getLabel,
updateModelValue,
isOptionSelected,
Expand Down

0 comments on commit aceb658

Please sign in to comment.