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

Feature/combobox show add new value while autocompleting #3225

Merged
5 changes: 5 additions & 0 deletions .changeset/silver-rings-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@navikt/ds-react": patch
it-vegard marked this conversation as resolved.
Show resolved Hide resolved
---

Combobox: Enable option to add a new value while autocompleting and highlight matches in FilteredOptions.
5 changes: 5 additions & 0 deletions @navikt/core/css/form/combobox.css
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@
border-radius: calc(var(--a-border-radius-medium) - 1px);
}

.navds-combobox__list-item mark {
background-color: transparent;
font-weight: bold;
}

/* Mobile */

@media (max-width: 479px) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const AddNewOption = () => {
const {
inputProps: { id },
size,
value,
searchTerm,
} = useInputContext();
const {
setIsMouseLastUsedInputDevice,
Expand All @@ -34,8 +34,8 @@ const AddNewOption = () => {
}
}}
onPointerUp={(event) => {
toggleOption(toComboboxOption(value), event);
if (!isMultiSelect && !isInList(value, selectedOptions))
toggleOption(toComboboxOption(searchTerm), event);
if (!isMultiSelect && !isInList(searchTerm, selectedOptions))
toggleIsListOpen(false);
}}
id={filteredOptionsUtil.getAddNewOptionId(id)}
Expand All @@ -53,7 +53,7 @@ const AddNewOption = () => {
<BodyShort size={size}>
Legg til{" "}
<Label as="span" size={size}>
&#8220;{value}&#8221;
&#8220;{searchTerm}&#8221;
</Label>
</BodyShort>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@ import { ComboboxOption } from "../types";
import filteredOptionsUtil from "./filtered-options-util";
import { useFilteredOptionsContext } from "./filteredOptionsContext";

const useTextHighlight = (text: string, searchTerm: string) => {
const indexOfHighlightedText = text
.toLowerCase()
.indexOf(searchTerm.toLowerCase());
const start = text.substring(0, indexOfHighlightedText);
const highlight = text.substring(
indexOfHighlightedText,
indexOfHighlightedText + searchTerm.length,
);
const end = text.substring(indexOfHighlightedText + searchTerm.length);
return [start, highlight, end];
};

const FilteredOptionsItem = ({ option }: { option: ComboboxOption }) => {
const {
inputProps: { id },
size,
searchTerm,
} = useInputContext();
const {
setIsMouseLastUsedInputDevice,
Expand All @@ -22,9 +36,11 @@ const FilteredOptionsItem = ({ option }: { option: ComboboxOption }) => {
} = useFilteredOptionsContext();
const { isMultiSelect, maxSelected, selectedOptions, toggleOption } =
useSelectedOptionsContext();
const [start, highlight, end] = useTextHighlight(option.label, searchTerm);

const isDisabled = (_option: ComboboxOption) =>
maxSelected?.isLimitReached && !isInList(_option.value, selectedOptions);

return (
<li
className={cl("navds-combobox__list-item", {
Expand Down Expand Up @@ -64,7 +80,11 @@ const FilteredOptionsItem = ({ option }: { option: ComboboxOption }) => {
aria-selected={isInList(option.value, selectedOptions)}
aria-disabled={isDisabled(option) || undefined}
>
<BodyShort size={size}>{option.label}</BodyShort>
<BodyShort size={size}>
{start}
{highlight && <mark>{highlight}</mark>}
{end}
</BodyShort>
{isInList(option.value, selectedOptions) && <CheckmarkIcon />}
</li>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ const FilteredOptionsProvider = ({

const isValueNew = useMemo(
() =>
Boolean(value) &&
!filteredOptionsMap[filteredOptionsUtils.getOptionId(id, value)],
[filteredOptionsMap, id, value],
Boolean(searchTerm) &&
!filteredOptionsMap[filteredOptionsUtils.getOptionId(id, searchTerm)],
[filteredOptionsMap, id, searchTerm],
);

const ariaDescribedBy = useMemo(() => {
Expand Down
7 changes: 1 addition & 6 deletions @navikt/core/react/src/form/combobox/combobox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ const complexOptions = [
];

export const WithAddNewOptions: StoryFn = ({ open }: { open?: boolean }) => {
const [value, setValue] = useState<string | undefined>("hello");
const comboboxRef = useRef<HTMLInputElement>(null);
return (
<UNSAFE_Combobox
Expand All @@ -186,8 +185,6 @@ export const WithAddNewOptions: StoryFn = ({ open }: { open?: boolean }) => {
options={options}
allowNewValues={true}
shouldAutocomplete={true}
value={value}
onChange={setValue}
it-vegard marked this conversation as resolved.
Show resolved Hide resolved
isListOpen={open ?? (comboboxRef.current ? true : undefined)}
ref={comboboxRef}
/>
Expand All @@ -199,7 +196,6 @@ export const MultiSelectWithAddNewOptions: StoryFn = ({
}: {
open?: boolean;
}) => {
const [value, setValue] = useState<string | undefined>("world");
const [selectedOptions, setSelectedOptions] = useState<string[]>(["hello"]);
const comboboxRef = useRef<HTMLInputElement>(null);
return (
Expand All @@ -209,9 +205,8 @@ export const MultiSelectWithAddNewOptions: StoryFn = ({
label="Multiselect komboboks med mulighet for å legge til nye verdier"
options={options}
allowNewValues={true}
value={value}
shouldAutocomplete={true}
selectedOptions={selectedOptions}
onChange={setValue}
onToggleSelected={(option, isSelected) =>
isSelected
? setSelectedOptions([...selectedOptions, option])
Expand Down
Loading