Skip to content

Commit

Permalink
Fix showing proper highlight index when value is object in Combobox (#…
Browse files Browse the repository at this point in the history
…815)

* Fix show highlight index

* Add changeset

* Fix highlighted index

* Fix return

* Small fixes

* Remove index check

* Improvments

* Clear input when select value

* Allow select label to be ReactNode

* Fix dependency useCombobox

* Refactor to hooks, add support for select highlight

* Refactor to use single hook for comobobox and  select

* Restore select option react node

* Add handle to clear input to useHighlightedIndex

* Update src/components/BaseSelect/useHighlightedIndex.ts

Co-authored-by: Wojciech Mista <[email protected]>

* Update src/components/BaseSelect/useHighlightedIndex.ts

Co-authored-by: Wojciech Mista <[email protected]>

---------

Co-authored-by: Wojciech Mista <[email protected]>
  • Loading branch information
poulch and Cloud11PL authored Jul 22, 2024
1 parent f032235 commit df82ca9
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 5 deletions.
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";
81 changes: 81 additions & 0 deletions src/components/BaseSelect/useHighlightedIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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;
} {
// Initially we don't show any item as highlighted
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) {
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

0 comments on commit df82ca9

Please sign in to comment.