Skip to content

Commit

Permalink
refactor: moved useMultiselect
Browse files Browse the repository at this point in the history
  • Loading branch information
mwargan committed Apr 23, 2024
1 parent 78e683e commit 1a0d006
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/components/DropdownSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
filterOptions,
orderOptionsBySelectedFirst,
} from "@/helpers/normaliseOptions";
import { useMultiselect } from "@/stories/Composables/useMultiselect";
import { useMultiselect } from "@/composables/useMultiselect";
import type { selectOption } from "@/types/listItem";
import { type PropType, computed, onMounted, ref, watch } from "vue";
Expand Down
2 changes: 1 addition & 1 deletion src/components/TabNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { PropType } from "vue";
import BaseButton from "./BaseButton.vue";
import type { selectOption } from "@/types/listItem";
import { useMultiselect } from "@/stories/Composables/useMultiselect";
import { useMultiselect } from "@/composables/useMultiselect";
const emit = defineEmits([
/** The page the user has navigated to, either by clicking directly on a page or by using the previous and next buttons */
Expand Down
65 changes: 65 additions & 0 deletions src/composables/__tests__/useMultiselect.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { describe, expect, it, vi } from "vitest";
import {
type requiredEmits,
type requiredProps,
useMultiselect,
} from "../useMultiselect";

// Setup the spy on emit, so we can check if it was called
const emit = vi.spyOn(
{
emit: () => {},
},
"emit"
) as unknown as requiredEmits;

describe("useMultiselect", () => {
// Mock props and emit function
const props = {
options: [
{ id: "1", render: "Option 1", disabled: false },
{ id: "2", render: "Option 2", disabled: true },
{ id: "3", render: "Option 3", disabled: false },
],
displayKey: "render",
multiple: true,
modelValue: ["1"],
modelKey: "id",
} as requiredProps;

it("should compute normalisedOptions correctly", async () => {
const { normalisedOptions } = useMultiselect(props, emit);
expect(normalisedOptions.value).toEqual([
{ id: "1", render: "Option 1", disabled: false },
{ id: "2", render: "Option 2", disabled: true },
{ id: "3", render: "Option 3", disabled: false },
]);
});

it("should compute selecteableOptions correctly", async () => {
const { selecteableOptions } = useMultiselect(props, emit);
expect(selecteableOptions.value).toEqual([
{ id: "1", render: "Option 1", disabled: false },
{ id: "3", render: "Option 3", disabled: false },
]);
});

it("should return correct label for an option", async () => {
const { getLabel } = useMultiselect(props, emit);
expect(getLabel({ id: "1", render: "Option 1", disabled: false })).toBe(
"Option 1"
);
});

it("should update modelValue correctly when a value is selected", async () => {
const { updateModelValue } = useMultiselect(props, emit);
updateModelValue("2", true);
expect(emit).toHaveBeenCalledWith("update:modelValue", ["1", "2"]);
});

it("should update modelValue correctly when a value is unselected", async () => {
const { updateModelValue } = useMultiselect(props, emit);
updateModelValue("1", false);
expect(emit).toHaveBeenCalledWith("update:modelValue", []);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import { normaliseOptions } from "@/helpers/normaliseOptions";
import type { normalisedOptionObject, selectOption } from "@/types/listItem";
import { computed, toRaw } from "vue";

type requiredProps = {
export type requiredProps = {
options: selectOption[];
displayKey: "id" | "render";
multiple: boolean;
modelValue: string[];
modelKey: "id" | "render";
};

type requiredEmits = (evt: "update:modelValue", args_0: string[]) => void;
export type requiredEmits = (
evt: "update:modelValue",
args_0: string[]
) => void;

// A composable for a multiselect component
export function useMultiselect(props: requiredProps, emit: requiredEmits) {
Expand All @@ -36,9 +39,14 @@ export function useMultiselect(props: requiredProps, emit: requiredEmits) {
*
* @param newValue
* @param valueSelected If the value should be marked as selected or not in the mode value. Of you want the user to be toggle the value on/off with multiple clicks on the same value, set this to false
* @param existingValueIndex the value index to update - this is useful if you want to update a specific value in the array
* @returns
*/
const updateModelValue = (newValue: string | null, valueSelected = true) => {
const updateModelValue = (
newValue: string | null,
valueSelected = true,
existingValueIndex = false as number | false
) => {
if (!newValue) {
return;
}
Expand All @@ -48,10 +56,18 @@ export function useMultiselect(props: requiredProps, emit: requiredEmits) {
return;
}

// Always emit the full array of selected page IDs
const newPages = props.modelValue.includes(newValue)
? props.modelValue.filter((id) => id !== newValue)
: [...props.modelValue, newValue];
let newPages: string[] = [];

// If we are updating a specific value in the array
if (existingValueIndex !== false) {
newPages = [...props.modelValue];
newPages[existingValueIndex] = newValue;
} else {
// Always emit the full array of selected page IDs
newPages = props.modelValue.includes(newValue)
? props.modelValue.filter((id) => id !== newValue)
: [...props.modelValue, newValue];
}

emit("update:modelValue", newPages);
};
Expand Down

0 comments on commit 1a0d006

Please sign in to comment.