Skip to content

Commit

Permalink
Combobox: Add support for onBlur, and omit props that have no effect (
Browse files Browse the repository at this point in the history
  • Loading branch information
HalvorHaugan authored Sep 9, 2024
1 parent 914b0db commit c8b7909
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/silly-cycles-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@navikt/ds-react": minor
---

Combobox: Add support for `onBlur`, and omit props that have no effect.
34 changes: 23 additions & 11 deletions @navikt/core/react/src/form/combobox/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,26 @@ import React, {
useRef,
} from "react";
import { omit } from "../../../util";
import { composeEventHandlers } from "../../../util/composeEventHandlers";
import { useMergeRefs } from "../../../util/hooks";
import filteredOptionsUtil from "../FilteredOptions/filtered-options-util";
import { useFilteredOptionsContext } from "../FilteredOptions/filteredOptionsContext";
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
import { useInputContext } from "./Input.context";

interface InputProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "disabled"> {
extends Omit<
InputHTMLAttributes<HTMLInputElement>,
| "value"
| "disabled"
| "onClick"
| "onInput"
| "type"
| "role"
| "onKeyUp"
| "onKeyDown"
| "autoComplete"
> {
ref: React.Ref<HTMLInputElement>;
inputClassName?: string;
shouldShowSelectedOptions?: boolean;
Expand All @@ -22,7 +34,7 @@ interface InputProps

const Input = forwardRef<HTMLInputElement, InputProps>(
(
{ inputClassName, shouldShowSelectedOptions, placeholder, ...rest },
{ inputClassName, shouldShowSelectedOptions, placeholder, onBlur, ...rest },
ref,
) => {
const internalRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -224,24 +236,18 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
{...rest}
{...omit(inputProps, ["aria-invalid"])}
ref={mergedRefs}
type="text"
role="combobox"
value={value}
onBlur={() => virtualFocus.moveFocusToTop()}
onBlur={composeEventHandlers(onBlur, virtualFocus.moveFocusToTop)}
onClick={() => {
setHideCaret(!!maxSelected?.isLimitReached);
value !== searchTerm && onChange(value);
}}
onInput={onChangeHandler}
type="text"
role="combobox"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
aria-controls={filteredOptionsUtil.getFilteredOptionsId(inputProps.id)}
aria-expanded={!!isListOpen}
autoComplete="off"
aria-autocomplete={shouldAutocomplete ? "both" : "list"}
aria-activedescendant={activeDecendantId}
aria-describedby={ariaDescribedBy}
aria-invalid={inputProps["aria-invalid"]}
placeholder={selectedOptions.length ? undefined : placeholder}
className={cl(
inputClassName,
Expand All @@ -250,6 +256,12 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
`navds-body-short--${size}`,
{ "navds-combobox__input--hide-caret": hideCaret },
)}
aria-controls={filteredOptionsUtil.getFilteredOptionsId(inputProps.id)}
aria-expanded={!!isListOpen}
aria-autocomplete={shouldAutocomplete ? "both" : "list"}
aria-activedescendant={activeDecendantId}
aria-describedby={ariaDescribedBy}
aria-invalid={inputProps["aria-invalid"]}
/>
);
},
Expand Down
12 changes: 11 additions & 1 deletion @navikt/core/react/src/form/combobox/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,17 @@ export interface ComboboxProps
extends FormFieldProps,
Omit<
InputHTMLAttributes<HTMLInputElement>,
"size" | "onChange" | "value" | "defaultValue"
| "size"
| "onChange"
| "value"
| "defaultValue"
| "onClick"
| "onInput"
| "type"
| "role"
| "onKeyUp"
| "onKeyDown"
| "autoComplete"
> {
/**
* Combobox label.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,6 @@ export const Demo = {
};

export const args = {
index: 1,
index: 5,
desc: "Du kan overstyre blant annet value, selectedOptions, filteredOptions.",
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ export const Demo = {
};

export const args = {
index: 1,
index: 6,
desc: "Ved Multi Select kan brukeren velge flere valg fra nedtrekkslisten.",
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ export const Demo = {
};

export const args = {
index: 1,
index: 4,
desc: "Ved Multi Select kan brukeren velge flere valg fra listen.",
};
81 changes: 81 additions & 0 deletions aksel.nav.no/website/pages/eksempler/combobox/react-hook-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { Button, UNSAFE_Combobox, VStack } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";

type Inputs = { transportmiddel: string[] };

const Example = () => {
const {
handleSubmit,
control,
formState: { errors },
} = useForm<Inputs>({
shouldFocusError: false,
defaultValues: { transportmiddel: [] },
});

const onValidSubmit: SubmitHandler<Inputs> = (data) => {
alert("Du valgte: " + data.transportmiddel.join(", "));
};

return (
<VStack as="form" gap="8" onSubmit={handleSubmit(onValidSubmit)}>
<Controller
control={control}
rules={{ required: "Du må velge minst ett transportmiddel." }}
name="transportmiddel"
render={({ field }) => (
<UNSAFE_Combobox
id="transportmiddel"
label="Hva er de kuleste transportmidlene?"
options={options}
isMultiSelect
error={errors.transportmiddel?.message}
ref={field.ref}
name={field.name}
onBlur={field.onBlur}
onToggleSelected={(option, isSelected) => {
if (isSelected) {
field.onChange([...field.value, option]);
} else {
field.onChange(field.value.filter((v) => v !== option));
}
}}
/>
)}
/>
<div>
<Button type="submit">Send inn</Button>
</div>
</VStack>
);
};

const options = [
"car",
"bus",
"train",
"skateboard",
"bicycle",
"motorcycle",
"boat",
"airplane",
"helicopter",
"truck",
"van",
"scooter",
];

// EXAMPLES DO NOT INCLUDE CONTENT BELOW THIS LINE
export default withDsExample(Example, { variant: "static" });

/* Storybook story */
export const Demo = {
render: Example,
};

export const args = {
index: 9,
desc: "Eksempel på bruk med react-hook-form.",
sandbox: false,
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ export const Demo = {
};

export const args = {
index: 0,
index: 2,
desc: "Ved Single Select velger brukeren ett valg fra listen. Med autocomplete foreslås et valg fra listen som matcher det brukeren skriver.",
};
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export const Demo = {
};

export const args = {
index: 1,
index: 8,
desc: "Ved å sende inn options som objekter er det mulig å vise en brukervennlig tekst til brukeren, samtidig som systemet i bakkant kan forholde seg til en ID.",
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ export const Demo = {
};

export const args = {
index: 0,
index: 3,
desc: "Ved Single Select velger brukeren kun ett valg fra nedtrekkslisten.",
};
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ export const Demo = {
};

export const args = {
index: 1,
index: 7,
desc: "Ved å sette en grense for maks antall valgte vil brukeren få opp en beskjed om at hen ikke kan velge flere når grensen er nådd. Resterende valg vil også bli inaktive.",
};

0 comments on commit c8b7909

Please sign in to comment.