Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix showing proper highlight index when value is object in Combobox #815

Merged
merged 18 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-rockets-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@saleor/macaw-ui": patch
---

You can now see selected option in combobox dropdown when provided value does not match same referance with options
1 change: 1 addition & 0 deletions src/components/BaseSelect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./LoadingListItem";
export * from "./types";
export * from "./helpers";
export * from "./NoOptions";
export * from "./useHighlightedIndex";
82 changes: 82 additions & 0 deletions src/components/BaseSelect/useHighlightedIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
UseComboboxStateChangeTypes,
UseSelectStateChangeTypes,
useCombobox as useDownshiftCombobox,
useSelect as useDownshiftSelect,
} from "downshift";
import { useEffect, useState } from "react";

import { Option } from "~/components/BaseSelect";

export function useHighlightedIndex<T extends Option>(
items: T[],
selectedItem: T | null | undefined
): {
highlightedIndex: number | undefined;
onHighlightedIndexChange: (
index: number | undefined,
type: UseComboboxStateChangeTypes | UseSelectStateChangeTypes
) => void;
} {
// Inistiali we don't show any item as highlighted
poulch marked this conversation as resolved.
Show resolved Hide resolved
const [highlightedIndex, setHighlightedIndex] = useState<number | undefined>(
-1
);

// When data from API comes we can calulate intial highlighted index
// Or when we change selected item
useEffect(() => {
// If we don't have selected item leave highlighted index as -1
if (!selectedItem) {
// setHighlightedIndex(-1);
poulch marked this conversation as resolved.
Show resolved Hide resolved
return;
}

// Find hilighted index in items to select base on selected item value
// If there is no match, leave highlighted index as -1
setHighlightedIndex(getIndexToHighlight(items, selectedItem));
}, [items, selectedItem]);

const handleHighlightedIndexChange = (
highlightedIndex: number | undefined,
type: UseComboboxStateChangeTypes | UseSelectStateChangeTypes
) => {
switch (type) {
// Restore highlighted index to -1 when input value is changed and there is no selected item
case useDownshiftCombobox.stateChangeTypes.InputChange:
setHighlightedIndex(!selectedItem ? -1 : highlightedIndex);
break;
// Restore highlighted index to last selected item when leaving menu
case useDownshiftCombobox.stateChangeTypes.MenuMouseLeave:
case useDownshiftSelect.stateChangeTypes.MenuMouseLeave:
setHighlightedIndex(
selectedItem
? getIndexToHighlight(items, selectedItem)
: highlightedIndex
);
break;
case useDownshiftCombobox.stateChangeTypes.ItemClick:
case useDownshiftSelect.stateChangeTypes.ItemClick:
case useDownshiftCombobox.stateChangeTypes.ItemMouseMove:
case useDownshiftSelect.stateChangeTypes.ItemMouseMove:
setHighlightedIndex(highlightedIndex);
break;
}
};

return {
highlightedIndex,
onHighlightedIndexChange: handleHighlightedIndexChange,
};
}

function getIndexToHighlight<T extends Option>(
items: T[],
selectedItem: T
): number {
if (typeof selectedItem === "string") {
return items.findIndex((item) => item.value === selectedItem);
}

return items.findIndex((item) => item.value === selectedItem?.value);
}
16 changes: 14 additions & 2 deletions src/components/Combobox/Common/useCombobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
} from "downshift";
import { FocusEvent, useState } from "react";

import { Option, SingleChangeHandler } from "~/components/BaseSelect";
import {
Option,
SingleChangeHandler,
useHighlightedIndex,
} from "~/components/BaseSelect";

const getItemsFilter = <T extends Option>(
inputValue: string | undefined,
Expand Down Expand Up @@ -44,24 +48,32 @@ export const useCombobox = <T extends Option, V extends string | Option>({
const typed = Boolean(selectedItem || active || inputValue);

const itemsToSelect = getItemsFilter<T>(inputValue, options);
const { highlightedIndex, onHighlightedIndexChange } = useHighlightedIndex(
itemsToSelect,
selectedItem
);

const {
isOpen,
getToggleButtonProps,
getLabelProps,
getMenuProps,
getInputProps: _getInputProps,
highlightedIndex,
getItemProps,
} = useDownshiftCombobox({
items: itemsToSelect,
itemToString: (item) => item?.label ?? "",
selectedItem,
highlightedIndex,
onHighlightedIndexChange: ({ highlightedIndex, type }) => {
onHighlightedIndexChange(highlightedIndex, type);
},
onSelectedItemChange: ({ selectedItem }) => {
if (selectedItem) {
const selectedValue = isValuePassedAsString
? selectedItem.value
: selectedItem;
setInputValue("");
onChange?.(selectedValue as V);
}
},
Expand Down
13 changes: 10 additions & 3 deletions src/components/Select/useSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from "downshift";
import { FocusEvent, useState } from "react";

import { Option } from "../BaseSelect";
import { Option, useHighlightedIndex } from "../BaseSelect";
import { SelectProps } from "./Select";

export const useSelect = <T extends Option, V extends string | Option>({
Expand All @@ -25,19 +25,26 @@ export const useSelect = <T extends Option, V extends string | Option>({
}) => {
const [active, setActive] = useState(false);
const typed = Boolean(value || active);
const { highlightedIndex, onHighlightedIndexChange } = useHighlightedIndex(
options,
value
);

const {
isOpen,
getToggleButtonProps: _getToggleButtonProps,
getLabelProps,
getMenuProps,
highlightedIndex,
getItemProps,
selectedItem,
} = useDownshiftSelect({
items: options,
selectedItem: value ?? null,
itemToString: (item) => item?.label ?? "",
highlightedIndex,
onHighlightedIndexChange({ highlightedIndex, type }) {
onHighlightedIndexChange(highlightedIndex, type);
},
itemToString: (item) => item?.value ?? "",
onSelectedItemChange: ({ selectedItem }) => {
if (selectedItem) {
const selectedValue = isValuePassedAsString
Expand Down
Loading