Skip to content

Commit

Permalink
Merge pull request #762 from IPG-Automotive-UK/enhancement/allow-key-…
Browse files Browse the repository at this point in the history
…value-options

Enhancement - Options to allow key value array
  • Loading branch information
syedsalehinipg authored Nov 2, 2023
2 parents 4678b98 + 5cc1647 commit ef6c4d4
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 10 deletions.
54 changes: 51 additions & 3 deletions src/Autocomplete/Autocomplete.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AutocompleteProps, KeyValueOption } from "./Autocomplete.types";
import { Meta, StoryFn, StoryObj } from "@storybook/react";

import Autocomplete from "./Autocomplete";
import { AutocompleteProps } from "./Autocomplete.types";
import React from "react";
import { action } from "@storybook/addon-actions";
import { useArgs } from "@storybook/preview-api";
Expand All @@ -22,30 +22,49 @@ function isReadOnlyArray<T>(
return Array.isArray(value);
}

// Define the template for the story
const Template: StoryFn<
AutocompleteProps<string, boolean | undefined>
AutocompleteProps<string | KeyValueOption, boolean | undefined>
> = args => {
// Use the useArgs hook to get and update the args
const [{ value, multiple }, updateArgs] =
useArgs<AutocompleteProps<string, boolean | undefined>>();

// Use an effect to update the value arg based on the multiple arg
React.useEffect(() => {
if (multiple && value && !isReadOnlyArray(value))
updateArgs({ value: value !== "" ? [value] : [] });
if (!multiple && isReadOnlyArray(value))
updateArgs({ value: value.length > 0 ? value[0] : "" });
}, [updateArgs, multiple, value]);

// Determine the value to use based on the multiple and value args
let theValue;
if (multiple && Array.isArray(value)) theValue = value;
if (multiple && !Array.isArray(value) && value) theValue = [value];
if (!multiple && Array.isArray(value)) theValue = value[0];
if (!multiple && !Array.isArray(value)) theValue = value;

// Return the Autocomplete component with the appropriate props
return (
<Autocomplete
{...args}
onChange={(event, newValue) => {
updateArgs({ value: newValue });
if (newValue !== null) {
if (Array.isArray(newValue)) {
updateArgs({
value: newValue.map(val =>
typeof val === "string" ? val : val.value
)
});
} else {
updateArgs({
value: typeof newValue === "string" ? newValue : newValue.value
});
}
} else {
updateArgs({ value: multiple ? [] : "" });
}
action("onChange")(newValue);
}}
value={theValue}
Expand All @@ -54,8 +73,10 @@ const Template: StoryFn<
);
};

// Define the default story
export const Default: StoryObj<typeof Autocomplete> = {
args: {
// Define the default args
disabled: false,
error: false,
helperText: "Helper Text",
Expand All @@ -79,8 +100,35 @@ export const Default: StoryObj<typeof Autocomplete> = {
render: Template
};

// Define the story for key-value options
export const KeyValueOptions: StoryObj<typeof Autocomplete> = {
args: {
// Define the args for key-value options
disabled: false,
error: false,
helperText: "Helper Text",
label: "Select options",
limitTags: -1,
margin: "normal",
multiple: false,
options: [
{ key: 1, value: "Option 1" },
{ key: 2, value: "Option 2" },
{ key: 3, value: "Option 3" },
{ key: 4, value: "Option 4" }
],
required: false,
size: "medium",
value: "Option 1",
variant: "outlined"
},
render: Template
};

// Define the story for multi-select
export const MultiSelect: StoryObj<typeof Autocomplete> = {
args: {
// Define the args for multi-select
disableCloseOnSelect: true,
disabled: false,
error: false,
Expand Down
40 changes: 40 additions & 0 deletions src/Autocomplete/Autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import { userEvent } from "@storybook/testing-library";

// sample options
const options = ["option 1", "option 2", "option 3"];
const keyValueOptions = [
{ key: 1, value: "Option 1" },
{ key: 2, value: "Option 2" },
{ key: 3, value: "Option 3" },
{ key: 4, value: "Option 4" }
];

/**
* Tests
Expand Down Expand Up @@ -97,4 +103,38 @@ describe("Select", () => {
// check the second call with "option 2"
expect(onChange.mock.calls[1][1]).toEqual(["option 2"]);
});

it("can single select with key-value pairs", async () => {
const onChange = jest.fn();

render(
<Autocomplete
multiple={false}
options={keyValueOptions}
onChange={onChange}
label="Select an option"
value={{ key: 2, value: "option 2" }}
/>
);

// click the label selector down arrow
await userEvent.click(screen.getByRole("button", { name: /open/i }));

// check that the options are rendered
keyValueOptions.forEach(opt =>
expect(screen.getByText(opt.value)).toBeInTheDocument()
);

// click the first option and check callback is called
await userEvent.click(screen.getByText("Option 1"));
expect(onChange).toHaveBeenCalledTimes(1);

// check that the onChange event is fired with the expected value
expect(onChange).toHaveBeenCalledWith(
expect.anything(),
{ key: 1, value: "Option 1" },
expect.anything(),
expect.anything()
);
});
});
16 changes: 10 additions & 6 deletions src/Autocomplete/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from "react";

import { AutocompleteProps, KeyValueOption } from "./Autocomplete.types";
import {
Box,
Checkbox,
Expand All @@ -9,10 +10,8 @@ import {
} from "@mui/material";
import { CheckBox, CheckBoxOutlineBlank } from "@mui/icons-material";

import { AutocompleteProps } from "./Autocomplete.types";

export default function Autocomplete<
Value extends string,
Value extends KeyValueOption | string,
Multiple extends boolean | undefined
>({
disableCloseOnSelect = false,
Expand All @@ -37,6 +36,9 @@ export default function Autocomplete<
multiple={multiple}
onChange={onChange}
options={options}
getOptionLabel={(option: KeyValueOption | string) =>
typeof option === "string" ? option : option.value
}
renderInput={params => (
<TextField
{...params}
Expand All @@ -60,7 +62,7 @@ export default function Autocomplete<
// renderer for a checkbox option
function Option(
props: React.HTMLAttributes<HTMLLIElement>,
option: string,
option: KeyValueOption | string,
{ selected }: { selected: boolean }
) {
return (
Expand All @@ -69,9 +71,11 @@ function Option(
icon={<CheckBoxOutlineBlank fontSize="small" />}
checkedIcon={<CheckBox fontSize="small" />}
checked={selected}
value={option}
value={typeof option === "string" ? option : option.key}
/>
<Typography>{option}</Typography>
<Typography>
{typeof option === "string" ? option : option.value}
</Typography>
</Box>
);
}
4 changes: 3 additions & 1 deletion src/Autocomplete/Autocomplete.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import {
TextFieldProps
} from "@mui/material";

export type KeyValueOption = { key: string | number; value: string };

export type AutocompleteProps<
Value extends string,
Value extends string | KeyValueOption,
Multiple extends boolean | undefined
> = Pick<
MuiAutocompleteProps<Value, Multiple, false, false>,
Expand Down
1 change: 1 addition & 0 deletions src/Select/Select.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { MenuItem, TextField } from "@mui/material";

import PropTypes from "prop-types";
import React from "react";

Expand Down

0 comments on commit ef6c4d4

Please sign in to comment.