Skip to content

Commit

Permalink
feat(multicolumnautocomplete): first implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
masoudmanson committed Oct 31, 2023
1 parent 54b380d commit f920cfd
Show file tree
Hide file tree
Showing 22 changed files with 1,947 additions and 324 deletions.
63 changes: 40 additions & 23 deletions packages/components/src/core/Autocomplete/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
import { AutocompleteValue } from "@mui/base";
import { Args, Meta } from "@storybook/react";
import React, { SyntheticEvent, useEffect, useState } from "react";
import { Value } from "../Dropdown";
import { DefaultAutocompleteOption } from "../AutocompleteBase";
import { GITHUB_LABELS } from "../DropdownMenu/GITHUB_LABELS";
import { GITHUB_LABELS_MULTI_COLUMN } from "../DropdownMenu/GITHUB_LABELS_MULTI_COLUMN";
import TagFilter from "../TagFilter";
import RawAutocomplete, { DefaultAutocompleteOption } from "./index";

export type AutocompleteOptionValue<T, Multiple> = Multiple extends
| undefined
| false
? T | undefined
: Array<T> | undefined;
import RawAutocomplete from "./index";

const groupByOptions = [
undefined,
(option: DefaultAutocompleteOption) => option.section as string,
];

const Autocomplete = <Multiple extends boolean | undefined = false>(
const dataOptions = [GITHUB_LABELS, GITHUB_LABELS_MULTI_COLUMN];

const Autocomplete = <
T extends DefaultAutocompleteOption,
Multiple extends boolean | undefined = false
>(
props: Args
): JSX.Element => {
const {
label,
multiple,
options = GITHUB_LABELS,
options = GITHUB_LABELS_MULTI_COLUMN,
search,
value: propValue,
keepSearchOnSelect,
} = props;

const isControlled = propValue !== undefined;
const [value, setValue] = useState<
DefaultAutocompleteOption | DefaultAutocompleteOption[] | null
AutocompleteValue<T, Multiple, false, false>
>(getInitialValue());
const [pendingValue, setPendingValue] = useState<
DefaultAutocompleteOption | DefaultAutocompleteOption[] | null
AutocompleteValue<T, Multiple, false, false>
>(getInitialValue());

const [selection, setSelection] = useState<string[]>([]);
Expand All @@ -46,7 +47,11 @@ const Autocomplete = <Multiple extends boolean | undefined = false>(

useEffect(() => {
setSelection([]);
}, [multiple]);
setValue(null as AutocompleteValue<T, Multiple, false, false>);
setPendingValue(
[] as unknown as AutocompleteValue<T, Multiple, false, false>
);
}, [multiple, options]);

return (
<div style={{ margin: "16px 0 0 24px", width: 300 }}>
Expand Down Expand Up @@ -83,17 +88,19 @@ const Autocomplete = <Multiple extends boolean | undefined = false>(

function handleChange(
_: SyntheticEvent<Element, Event>,
newValue: DefaultAutocompleteOption | DefaultAutocompleteOption[] | null
newValue: AutocompleteValue<T, Multiple, false, false>
) {
if (multiple) {
const newSelection = Array.isArray(newValue)
? newValue?.map((item) => item.name)
: [];
setSelection(newSelection);
return setPendingValue(newValue);
return setPendingValue(
newValue as AutocompleteValue<T, Multiple, false, false>
);
} else {
if (newValue && !Array.isArray(newValue) && newValue.name) {
setValue(newValue);
setValue(newValue as AutocompleteValue<T, Multiple, false, false>);
setSelection([newValue.name]);
}
}
Expand All @@ -104,26 +111,26 @@ const Autocomplete = <Multiple extends boolean | undefined = false>(
const index = pendingValue?.findIndex((item) => item.name === tag);
const newValue = [...pendingValue];
newValue.splice(index, 1);
setPendingValue(newValue);
setPendingValue(newValue as AutocompleteValue<T, Multiple, false, false>);

const newSelection = [...selection];
const deleteIndex = newSelection.indexOf(tag);
newSelection.splice(deleteIndex, 1);
setSelection(newSelection);
} else {
setValue(null);
setValue(null as AutocompleteValue<T, Multiple, false, false>);
setSelection([]);
}
}

function getInitialValue(): Value<DefaultAutocompleteOption, Multiple> {
function getInitialValue(): AutocompleteValue<T, Multiple, false, false> {
if (isControlled) {
return propValue;
}

return multiple
? ([] as unknown as Value<DefaultAutocompleteOption, Multiple>)
: null;
? ([] as unknown as AutocompleteValue<T, Multiple, false, false>)
: (null as AutocompleteValue<T, Multiple, false, false>);
}
};

Expand All @@ -146,10 +153,18 @@ export default {
multiple: {
control: { type: "boolean" },
},
options: {
control: {
labels: ["Single Column Autocomplete", "Multi Column Autocomplete"],
type: "select",
},
mapping: dataOptions,
options: Object.keys(dataOptions),
},
},
component: Autocomplete,
// (masoudmanson) For the purpose of storybook, the button is removed
// from the Autocomplete component which may cause some accessibility
// from the RawAutocomplete component which may cause some accessibility
// violations related to ARIA roles and attributes. However, this
// should not be a concern as the component is always used with a button
// in real applications. To avoid false positive test failures, the following
Expand Down Expand Up @@ -240,5 +255,7 @@ export const Test = {
skip: true,
},
},
render: (args: Args) => <TestDemo data-testid="autocomplete" {...args} />,
render: (args: Args) => (
<TestDemo data-testid="autocomplete-base" {...args} />
),
};
4 changes: 2 additions & 2 deletions packages/components/src/core/Autocomplete/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const Test = composeStory(TestStory, Meta);
describe("<Autocomplete />", () => {
generateSnapshots(snapshotTestStoryFile);

it("renders Autocomplete component", () => {
it("renders AutocompleteBase component", () => {
render(<Test {...Test.args} />);
const AutocompleteElement = screen.getByTestId("autocomplete");
const AutocompleteElement = screen.getByTestId("autocomplete-base");
expect(AutocompleteElement).not.toBeNull();
});
});
Loading

0 comments on commit f920cfd

Please sign in to comment.