Skip to content

Commit

Permalink
Komp-355-searchAsync (#635)
Browse files Browse the repository at this point in the history
* Oppdaterer searchasync i henhold til mal

* Legger til flere props i searchAsync

* Endrer searchAsync docs i henhold til ny mal

* Legger til changeset

* Fjerner typo i kode
  • Loading branch information
adrianflatner authored Nov 13, 2023
1 parent eb6276a commit e013f34
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-readers-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kvib/react": patch
---

SearchAsync - Legger til to nye props: isDisabled og focusBorderColor.
189 changes: 163 additions & 26 deletions apps/storybook/stories/components/search/search-async/SearchAsync.mdx
Original file line number Diff line number Diff line change
@@ -1,33 +1,152 @@
import { Meta, Canvas, Story, Controls, Source } from "@storybook/blocks";
import * as SearchAsyncStories from "./SearchAsync.stories";
import { DocsHeading, DocsAnatomy, DocsContainer, DocsStory, Feedback } from "../../../templates";
import { Text, Code, Box, Link, UnorderedList, ListItem } from "@kvib/react/src/";

<Meta of={SearchAsyncStories} />

# SearchAsync

SearchAsync er et søkefelt som laster inn søkeresultater fra en liste med objekter. Resultatene blir vist i en dropdown der brukeren kan velge hvilket resultat som skal bli valgt.

## Ta i bruk

```jsx
import { SearchAsync } from "@kvib/react";
```

## Props

<Canvas of={SearchAsyncStories.SearchAsync} />
<Controls of={SearchAsyncStories.SearchAsync} />

## Async søkeresultater
<DocsContainer>

**Egnet til:**

- Raskt finne relevant innhold
- Laste inn innhold asynkront
- Select med søk

**Uegnet til:**

- Innhold som lett kan navigeres til uten søk

<Feedback component="SearchAsync" />

</DocsContainer>

<DocsHeading>Alternativer</DocsHeading>
<DocsContainer>

<DocsStory
title="Required props"
description={
<Box>
<Text as="b">loadOptions</Text>
<Text>SearchAsync komponentens prop <Code>loadOptions</Code> er en funksjon ment for asynkron lasting av alternativer basert på brukerinput i inputfeltet. Den tar to parametere:</Text>
<UnorderedList>
<ListItem>
<Text><Code>inputValue</Code>: Teksten brukeren skriver inn.</Text>
</ListItem>
<ListItem>
<Text>
<Code>callback</Code>: En funksjon som tar en liste av objekter (<Code>options</Code>) og brukes til å returnere søkeresultatene. Hvert objekt i <Code>options</Code> må ha en <Code>label</Code> (tekst vist i dropdown) og en <Code>value</Code> (verdien returnert ved valg).
</Text>
</ListItem>
</UnorderedList>

<Text as="b">onChange</Text>
<Text><Code>onChange</Code> er en funksjon som håndterer endringer i valgt verdi. Den tar ett parameter:</Text>
<UnorderedList>
<ListItem>
<Text>
<Code>newValue</Code>: Den nye verdien som er valgt av brukeren. Denne verdien er av typen <Code>T</Code> eller <Code>null</Code>. <Code>T</Code> representerer typen av objektene i dine <Code>options</Code>, og <Code>null</Code> brukes når det ikke er noen valgt verdi.
</Text>
</ListItem>
</UnorderedList>
</Box>
}
story={<Canvas of={SearchAsyncStories.SearchAsyncResults} />}
/>

<Canvas of={SearchAsyncStories.SearchAsyncResults} />
<DocsStory
title="Utseende"
description={
<Box>
<Text>
SearchAsync har fire varianter. <Code>outline</Code> er standardvarianten og skal brukes i de fleste tilfeller.
<Code>filled</Code> kan brukes som et alternativ til outline. For mindre viktige søkefelt kan
<Code>flushed</Code> og
<Code>unstyled</Code> brukes.
</Text>
<Text>
Det er mulig å velge mellom tre forskjellige størrelser: <Code>sm</Code>, <Code>md</Code> og <Code>lg</Code>.
<Code>md</Code> er standard.
</Text>
<Text>
For å endre borderColor når søkefeltet er i fokus kan du sette farge med color-token (f.eks. green.500) i{" "}
<Code>focusBorderColor</Code>.
</Text>
</Box>
}
story={<Canvas of={SearchAsyncStories.SearchAsyncVariants} />}
/>

## Egendefinert tilbakemelding ved ingen resultater
<DocsStory
title="Standardalternativer"
description={
<Box>
<Text>
SearchAsync har mulighet for å legge inn standardalternativer for resultater ved hjelp av proppen{" "}
<Code>defaultOptions</Code>. Det betyr at brukeren vil få muligheten til å velge resultat basert på en
dropdown-liste i komponenten, i tillegg til å kunne søke. Du kan velge mellom å inkludere alle søkealternativer
som standardalternativer (<Code>defaultOptions="true"</Code>), eller bare legge til noen (
<Code>defaultOptions=["eple", "banan"]</Code>). Dersom det forekommer et høyt antall av søkealternativer
anbefales det å ikke legge til alle.
</Text>
<Text>
Legg til et <Code>Icon</Code> i <Code>dropDownIndicator</Code> for å poengtere at det er en dropdown, dersom det
er ønskelig.
</Text>
</Box>
}
story={<Canvas of={SearchAsyncStories.SearchAsyncDropdown} />}
/>

Det er mulig å endre på tilbakemeldingen som dukker opp i dropdown ved å bruke propen `noOptionsMessage`. Denne tar inn en funksjon som returnerer en React-komponent. Funksjonen tar inn et objekt med `inputValue` som inneholder det brukeren har skrevet i søkefeltet.
Kodeblokken under eksempelet viser hvordan man kan bruke `noOptionsMessage` og `loadOptions` til å sørge for at brukeren skriver inn minst tre tegn før vi starter et søk.
<DocsStory
title="Multi"
description={
<Box>
<Text>
Det er mulig å la brukeren velge flere resultater med <Code>isMulti</Code>. Her er det også mulig å legge inn
standardalternativer, slik at brukeren kan velge fra dropdown - som i eksempelet.
</Text>
<Text>
Typen til
<Code>onChange</Code> endrer seg ved <Code>isMulti=true</Code> for å kunne håndtere flere resultater: <Code>{`(newValue: readonly T[] | null,`}</Code> <Code>{`actionMeta: ActionMeta<T>) => void`}</Code>

.<Code>newValue</Code> er en liste med valgte resultater, og <Code>actionMeta</Code> er en liste med objekter
som inneholder informasjon om handlingen som ble utført. Disse to blir logget til konsollen i eksempelet,
inspiser siden for å se.
</Text>
</Box>

}
story={<Canvas of={SearchAsyncStories.SearchAsyncMultiDropdown} />}
/>

<Canvas of={SearchAsyncStories.SearchAsyncNoOptions} />
<DocsStory
title="Egendefinert feedback ved ingen resultater"
description={
<Box>
<Text>
Det er mulig å endre på tilbakemeldingen som dukker opp i dropdown ved å bruke propen{" "}
<Code>noOptionsMessage</Code>. Denne tar inn en funksjon som returnerer en React-komponent. Funksjonen tar inn
et objekt med <Code>inputValue</Code> som inneholder det brukeren har skrevet i søkefeltet.
</Text>
<Text>
Kodeblokken under viser hvordan man kan bruke
<Code>noOptionsMessage</Code> og <Code>loadOptions</Code> til å sørge for at brukeren skriver inn minst tre tegn
før vi starter et søk.
</Text>
</Box>
}
story={<Canvas of={SearchAsyncStories.SearchAsyncNoOptions} />}
/>

<Source
code={`const mockLoadOptions = (inputValue: string, callback: (options: typeof fruits) => void) => {
Expand All @@ -53,26 +172,44 @@ const noOptionsMessage = ({ inputValue }: { inputValue: string }) => {
dark
/>

## Søkeresultater med delay
</DocsContainer>

<Canvas of={SearchAsyncStories.SearchAsyncResultsDebounce} />
<DocsHeading>Retningslinjer</DocsHeading>

## Standardalternativer
<DocsContainer>

<Canvas inline of={SearchAsyncStories.SearchAsyncDropdown} />
### Bredde

## Multi
Søkefeltets bredde bør tilsvare lengden på søkeordene som brukes. Feltets størrelse er et viktig signal til brukeren om hva de kan skrive inn. Et søkefelt som brukes til å søke på personnumre bør ha en bredde som tilsvarer et personnummer, mens hovedsøket for et nettsted bør ha et bredere felt som lar brukerne se flere ord samtidig.

<Canvas of={SearchAsyncStories.SearchAsyncMulti} />
Ta gjerne en kikk på søkeloggene dine for å se hvordan brukerne dine ordlegger seg når de søker på sidene dine. Unngå at teksten i søkefeltet må scrolles for å romme typiske søk.

## Multi med dropdown
### Innsending av query

<Canvas of={SearchAsyncStories.SearchAsyncMultiDropdown} />
Det er anbefalt å bruke `<form>` rundt SearchAsync dersom du skal bruke det valgte resultatet. Dette gjør det mulig å bruke `<button type="submit">` for å sende inn søket.
`FormControl` kan hjelpe til med å gi SearchAsync kontekst.

## Størrelser

<Canvas of={SearchAsyncStories.SearchAsyncSizes} />
<Source
code={`
<form onSubmit={handleSubmit}>
<FormControl>
<FormLabel htmlFor="async-search">Søk etter frukt</FormLabel>
<SearchAsync
loadOptions={mockLoadOptions}
onChange={handleChange}
placeholder="Søk etter frukt..."
isMulti={false}
/>
<Button mt={4} colorScheme="blue" type="submit">
Send
</Button>
</FormControl>
</form>`}
dark
/>

## Varianter
</DocsContainer>

<Canvas of={SearchAsyncStories.SearchAsyncVariants} />
<DocsHeading>Props</DocsHeading>
<Canvas of={SearchAsyncStories.SearchAsync} />
<Controls of={SearchAsyncStories.SearchAsync} />
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const meta: Meta<typeof KvibSearchAsync> = {
parameters: {
docs: {
story: { inline: true },
canvas: { sourceState: "shown" },
canvas: { sourceState: "hidden" },
},
a11y: {
// Label warnings + contrast ratio because of chakra wrapper.
Expand Down Expand Up @@ -101,6 +101,20 @@ const meta: Meta<typeof KvibSearchAsync> = {
},
control: "text",
},
isDisabled: {
table: {
type: { summary: "boolean" },
defaultValue: { summary: "false" },
},
control: "boolean",
},
focusBorderColor: {
table: {
type: { summary: "string" },
defaultValue: { summary: "blue.500" },
},
control: "text",
},
},
args: { onChange: undefined, loadOptions: undefined },
};
Expand All @@ -110,6 +124,13 @@ type Story = StoryObj<typeof KvibSearchAsync>;

export const SearchAsync: Story = {
args: {},
parameters: {
docs: {
canvas: {
sourceState: "shown",
},
},
},
render: (args) => (
<Box h={20}>
<KvibSearchAsync {...args} />
Expand Down Expand Up @@ -154,7 +175,7 @@ export const SearchAsyncResults: Story = {
isMulti: false,
},
render: (args) => (
<Box h={40}>
<Box h="20rem">
<KvibSearchAsync {...args} />
</Box>
),
Expand Down Expand Up @@ -217,7 +238,7 @@ export const SearchAsyncDropdown: Story = {
onChange: handleChange,
dropdownIndicator: <Icon icon="expand_more" weight={400} />,
defaultOptions: true,
placeholder: "Søk etter frukt...",
placeholder: "Søk eller velg frukt...",
},
render: (args) => (
<Box h="20rem">
Expand Down Expand Up @@ -247,7 +268,7 @@ export const SearchAsyncMultiDropdown: Story = {
isMulti: true,
dropdownIndicator: <Icon icon="expand_more" weight={400} />,
defaultOptions: true,
placeholder: "Søk etter frukt...",
placeholder: "Søk eller velg frukt...",
},
render: (args) => (
<Box h="20rem">
Expand Down Expand Up @@ -276,9 +297,10 @@ export const SearchAsyncVariants: Story = {
loadOptions: mockLoadOptions,
onChange: handleChange,
placeholder: "Søk etter frukt...",
focusBorderColor: "green.500",
},
render: (args) => (
<KvibStack h={60}>
<KvibStack h={"12rem"}>
<KvibSearchAsync {...args} variant="outline" />
<KvibSearchAsync {...args} variant="filled" />
<KvibSearchAsync {...args} variant="flushed" />
Expand Down
10 changes: 10 additions & 0 deletions packages/react/src/search-async/SearchAsync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export type BaseProps<T> = {
/** Id set to the SelectContainer component */
id?: string;

/** Determines if the input is disabled */
isDisabled?: boolean;

/** Determines the color of the border when focused. Use color keys in `theme.colors`. */
focusBorderColor?: string;

/** Function to map inputValue to a text output when no options are loaded */
noOptionsMessage?: ((obj: { inputValue: string }) => ReactNode) | undefined;
};
Expand Down Expand Up @@ -74,6 +80,8 @@ export const SearchAsync = <T extends unknown>({
id,
isMulti = false,
noOptionsMessage,
isDisabled,
focusBorderColor,
}: SearchAsyncProps<T>) => {
const noOptionsMessageDefault = ({ inputValue }: { inputValue: string }): ReactNode => {
if (inputValue.replaceAll(/\s/g, "").length < 1) {
Expand Down Expand Up @@ -118,6 +126,8 @@ export const SearchAsync = <T extends unknown>({
variant={variant}
id={id}
isMulti={isMulti}
isDisabled={isDisabled}
focusBorderColor={focusBorderColor}
/>
);
};
Expand Down

0 comments on commit e013f34

Please sign in to comment.