Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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/bright-goats-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": minor
---

Suggestion: Added `display` prop for multiple Suggestion. Set `display="count"` on a multiple Suggestion to show a count label instead of chips.
5 changes: 5 additions & 0 deletions .changeset/great-jeans-follow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-css": minor
---

Suggestion: Added count display mode for multiple Suggestion. Use `data-count` to hide chips and show a count label inside the input. Customize the label text with `--dsc-suggestion-count-label`.
7 changes: 7 additions & 0 deletions apps/www/app/content/components/suggestion/en/code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ To allow users to select multiple options, use `data-multiple` on suggestion.

<Story story='MultipleEn' />

### Multiple choice with count tag

Use `data-count` to hide chips and show the number of selected options in the input field.
Customize the tag text with the `--dsc-suggestion-count-label` CSS variable.

<Story story='MultipleCountEn' />

### Adding new options

With `data-creatable` on suggestion, a user can create new selected options by entering text in the input and pressing <kbd>Enter</kbd>.
Expand Down
7 changes: 7 additions & 0 deletions apps/www/app/content/components/suggestion/no/code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ For å tillate at brukeren kan velge flere alternativer, bruk `data-multiple` p

<Story story='Multiple' />

### Flervalg med tag for å vise antall valgt

Bruk `data-count` for å skjule chips og vise antall valgte alternativer i inputfeltet.
Tilpass teksten med CSS-variabelen `--dsc-suggestion-count-label`.

<Story story='MultipleCount' />

### Legge til nye alternativer

Med `data-creatable` på suggestion kan en bruker opprette nye valgte alternativer ved å skrive inn tekst i input og trykke <kbd>Enter</kbd>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,99 @@ export const Creatable = () => {
</Field>
);
};
export const MultipleCount = () => {
const DATA_PLACES = [
'Sogndal',
'Oslo',
'Brønnøysund',
'Stavanger',
'Trondheim',
'Bergen',
'Lillestrøm',
];
const [selected, setSelected] = useState<string[]>(['Sogndal']);

return (
<Field>
<Label>Velg destinasjoner</Label>
<EXPERIMENTAL_Suggestion
multiple
display='count'
selected={selected}
onSelectedChange={(items) =>
setSelected(items.map((item) => item.value))
}
>
<EXPERIMENTAL_Suggestion.Input />
<EXPERIMENTAL_Suggestion.Clear />
<EXPERIMENTAL_Suggestion.List>
<EXPERIMENTAL_Suggestion.Empty>
Ingen treff
</EXPERIMENTAL_Suggestion.Empty>
{DATA_PLACES.map((place) => (
<EXPERIMENTAL_Suggestion.Option
key={place}
label={place}
value={place}
>
{place}
</EXPERIMENTAL_Suggestion.Option>
))}
</EXPERIMENTAL_Suggestion.List>
</EXPERIMENTAL_Suggestion>
</Field>
);
};

export const MultipleCountEn = () => {
const DATA_PLACES = [
'Sogndal',
'Oslo',
'Brønnøysund',
'Stavanger',
'Trondheim',
'Bergen',
'Lillestrøm',
];
const [selected, setSelected] = useState<string[]>(['Sogndal']);

return (
<Field>
<Label>Select destinations</Label>
<EXPERIMENTAL_Suggestion
multiple
display='count'
selected={selected}
onSelectedChange={(items) =>
setSelected(items.map((item) => item.value))
}
style={
{
'--dsc-suggestion-count-label': '"selected"',
} as React.CSSProperties
}
>
<EXPERIMENTAL_Suggestion.Input />
<EXPERIMENTAL_Suggestion.Clear />
<EXPERIMENTAL_Suggestion.List>
<EXPERIMENTAL_Suggestion.Empty>
No results found
</EXPERIMENTAL_Suggestion.Empty>
{DATA_PLACES.map((place) => (
<EXPERIMENTAL_Suggestion.Option
key={place}
label={place}
value={place}
>
{place}
</EXPERIMENTAL_Suggestion.Option>
))}
</EXPERIMENTAL_Suggestion.List>
</EXPERIMENTAL_Suggestion>
</Field>
);
};

export const CreatableEn = () => {
const DATA_PLACES = [
'Sogndal',
Expand Down
31 changes: 31 additions & 0 deletions packages/css/src/suggestion.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,37 @@
display: none; /* Hide data elements when not in multiple mode */
}

/* Count display mode: hide chips, show count label inside input */
&[data-count] data {
display: none;
}

/* NOTE: This styling should be reflected in tag.css */
&[data-count]::before {
content: attr(data-count) ' ' var(--dsc-suggestion-count-label, 'valgt');
position: absolute;
inset-inline-start: var(--ds-size-3);
top: 50%;
translate: 0 -50%;
pointer-events: none;
z-index: 1;
align-items: center;
background: var(--ds-color-surface-tinted);
border: solid transparent var(--ds-border-width-default);
border-radius: var(--ds-border-radius-sm);
box-sizing: border-box;
color: var(--ds-color-text-default);
font-size: var(--ds-body-sm-font-size);
line-height: var(--ds-line-height-sm);
min-height: var(--ds-size-8);
padding: 0 var(--ds-size-2);
display: inline-flex;
}

&[data-count]:focus-within::before {
display: none;
}

/* NOTE: This styling should be reflected in chip.css */
& > data {
align-items: center;
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/components/suggestion/suggestion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import * as SuggestionStories from './suggestion.stories';

<Canvas of={SuggestionStories.Multiple} />

<Canvas of={SuggestionStories.MultipleCount} />

<Canvas of={SuggestionStories.CustomFilterAlt1} />

<Canvas of={SuggestionStories.FetchExternal} />
Expand Down
30 changes: 30 additions & 0 deletions packages/react/src/components/suggestion/suggestion.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,36 @@ Multiple.args = {
multiple: true,
};

export const MultipleCount: StoryFn<SuggestionMultipleProps> = (args) => {
const [selected, setSelected] = useState<string[]>(['Sogndal']);

return (
<Field>
<Label>Velg destinasjoner</Label>
<Suggestion
{...args}
multiple
display='count'
selected={selected}
onSelectedChange={(items) =>
setSelected(items.map((item) => item.value))
}
>
<Suggestion.Input />
<Suggestion.Clear />
<Suggestion.List>
<Suggestion.Empty>Tomt</Suggestion.Empty>
{DATA_PLACES.map((place) => (
<Suggestion.Option key={place} label={place} value={place}>
{place}
</Suggestion.Option>
))}
</Suggestion.List>
</Suggestion>
</Field>
);
};

export const InDetails: StoryFn<typeof Suggestion> = (args) => {
return (
<Details>
Expand Down
16 changes: 16 additions & 0 deletions packages/react/src/components/suggestion/suggestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ type SuggestionBaseProps = {
* @default ({ label }) => label
*/
renderSelected?: (args: { label: string; value: string }) => ReactNode;
/**
* How selected items are displayed when `multiple` is true.
*
* - `chips` renders removable chips for each selected item (default)
* - `count` hides chips and shows a count label (e.g. "2 valgt")
*
* Customize the label text with the `--dsc-suggestion-count-label` CSS variable.
*
* @default 'chips'
*/
display?: 'chips' | 'count';
} & Omit<HTMLAttributes<DSSuggestionElement>, 'defaultValue'>;

type SuggestionValueProps<T extends { multiple: boolean }> = {
Expand Down Expand Up @@ -169,6 +180,7 @@ export const Suggestion = forwardRef<DSSuggestionElement, SuggestionProps>(
className,
creatable = false,
defaultSelected,
display = 'chips',
filter = true,
multiple = false,
name,
Expand Down Expand Up @@ -266,6 +278,10 @@ export const Suggestion = forwardRef<DSSuggestionElement, SuggestionProps>(
<ds-suggestion
data-multiple={multiple || undefined}
data-creatable={creatable || undefined}
data-count={
(multiple && display === 'count' && selectedItems.length) ||
undefined
}
class={cl('ds-suggestion', className)} // Using "class" since React does not translate className on custom elements
ref={mergedRefs}
suppressHydrationWarning // Since <ds-suggestion> adds attributes
Expand Down
Loading