Skip to content

Commit

Permalink
✨ Combobox: Mulighet for å begrense hvor mange valg bruker kan ta (#2260
Browse files Browse the repository at this point in the history
)

* yarn lock

* setting up maxSelectedOptions

* added correct margin

* Update tsconfig.build.json

* Make border of "max selected"-item in FilteredOptions overlap the list border

* Hide "add new value"-button if "max selected"-limit is reached

* Make MaxSelected example a bit more interactive

* wip styling max-selected option

* Disable filteredOptions that are not selected when maxSelectedOptions is reached

* Fix scrolling/clipping content in FilteredOptions

* Remove hover-effects from non-selectable options

* add prop so user can customize message shown when max num of choices reached

* Operate directly on the DOM nodes when moving virtual focus, instead of relying on index state.

The index state became stale/non-synced when replacing elements in the list.

* make maxSelectedMessage static

* Remove max-selected:hover

Co-authored-by: Vegard Haugstvedt <it.vegard@gmail.com>

* yarn lock update

* 'cursor: not allowed' only hits disabled options

* remove li-margins, add ul-gap with negative margin on max-selected-option

* remove 'a' from initialstate maxSelectedOptions Story

* Update @navikt/core/css/form/combobox.css

* Update @navikt/core/css/form/combobox.css

* Update @navikt/core/react/src/form/combobox/FilteredOptions/FilteredOptions.tsx

* Update @navikt/core/react/src/form/combobox/FilteredOptions/useVirtualFocus.ts

* Update @navikt/core/react/src/form/combobox/FilteredOptions/useVirtualFocus.ts

* Update @navikt/core/react/src/form/combobox/combobox.stories.tsx

* type assertion

* remove tab-index on wrapper div

* prototype for sticky non-selectable options

* "yarn lock"

* Fix feil med at focus-indikator for input-feltet ikke ble vist

* Forenkle og fikse "scroll til element" i FilteredOptions

* Sørg for at disabled FilteredOptions ikke vises under "max antall"-meldingen

* Focused option should not be covered by "max selected" message

* yarn lock

* Fix "scrollIntoView is not a function" error. Possibly needs to be checked for when it occurs, but I haven't figured it out yet

* Loading indicator is no longer exposed as an option, so we have to just search for it by the accessible name.

Should have been able to use findByRole("graphics-document", { name: "Søker..." }), but that is currently broken:
testing-library/dom-testing-library#1131

* Aria-selected should not be placed on elements that are no longer role="option"

* Combine all "max selected options"-related props in one object

* Proper BEM syntax should use -- instead of __ for state-like classes, like --loading, --max-selected and --new-option

* Always show selected options in FilteredOptions, even if they don't match the search text

* Only to the getElementsAbleToReceiveFocus() call once per function, to reduce DOM lookups. Also changed isFocusOnThe[Top | Bottom] to functions, because it was needed for isFocusOnTheBottom to follow the same rule (and not store the value on the top level in the hook.

* Pressing "End" should not only move focus to the end of the list, but also open the list if it is closed. To mirror this, we close the list if moving focus back to the Input with "Home".

* Legg til Aksel-eksempel

* yarn changeset

* Update @navikt/core/css/form/combobox.css

* change 'maxSelectedOptions' prop to 'maxSelected'

* Mark disabled options with aria-disabled

* Add max selected text to aria-describedby, so it is announced when the user enters the input field

* Add role="status" to the wrapper for "no hits", "loading" and "max selected" info messages, so they are announced when they appear

* Remove aria-selected (again)

* fix: form error not showing correctly

* fix: rm left hover-border on add-new-option item

* Bare åpne listen som default, men la intern logikk styre etter første render

* Sticky trenger ikke ligger på hver enkelt av disse, da parent wrapperen også er sticky

* Combobox eksempler var ikke tilgjengelig i `yarn storybook:aksel` på grunn av manglende Demo-eksport

* Fjern ubrukt klasse som er i konflikt med navds-combobox__wrapper

* Trengs ikke etter at max selected ble flyttet ut av listen

* Denne koden gjør ingenting

* Trenger ikke bruke :not, fordi vi tok vekk data-no-focus i en commit tidligere i dag

* Bruk dynamiske selectedOptions for å kunne endre de

* Update @navikt/core/css/form/combobox.css

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* Update @navikt/core/react/src/form/combobox/FilteredOptions/FilteredOptions.tsx

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* Update aksel.nav.no/website/pages/eksempler/combobox/with-max-selected-limit.tsx

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>

* Update @navikt/core/css/form/combobox.css

* Do not render an empty ul for FilteredOptions, as it add a gap at the bottom of the container

* Do not render an empty non-selectables container, as it adds a gap at the top of the container

* Non-selectables should not have navds-combobox__list-item class, as it introduces a few issues with hovering, etc.

* Copy-pasted in an extra class that broke styling for max selected

---------

Co-authored-by: Vegard Haugstvedt <it.vegard@gmail.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
  • Loading branch information
4 people authored Jan 26, 2024
1 parent 058e012 commit 23c472e
Show file tree
Hide file tree
Showing 23 changed files with 571 additions and 307 deletions.
6 changes: 6 additions & 0 deletions .changeset/red-crabs-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@navikt/ds-react": minor
"@navikt/ds-css": minor
---

:sparkles: Combobox: Mulighet for å begrense hvor mange valg bruker kan ta
74 changes: 55 additions & 19 deletions @navikt/core/css/form/combobox.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
}

.navds-combobox__input {
z-index: 1;
flex: 1;
border: none;
padding: 0;
Expand Down Expand Up @@ -208,28 +207,24 @@
height: 1.25rem;
}

/* dropdown list */
/* dropdown & non selectable dropdown items */

.navds-combobox__list {
max-height: 290px;
overflow-y: auto;
position: absolute;
left: 0;
right: 0;
z-index: 9999;
z-index: var(--a-z-index-popover);
top: calc(100% + var(--a-spacing-2));
list-style: none;
margin: 0;
border: 1px solid var(--ac-combobox-list-border-color, var(--a-border-divider));
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 0;
box-shadow: var(--a-shadow-small);
border-radius: var(--a-border-radius-medium);
gap: var(--a-spacing-1) 0;
background-color: var(--ac-combobox-list-bg, var(--a-surface-default));
color: var(--ac-combobox-list-text, var(--a-text-default));
gap: var(--a-spacing-1) 0;
}

.navds-combobox__list--closed {
Expand All @@ -241,34 +236,70 @@
width: 1.5rem;
}

.navds-combobox__list_non-selectables {
position: sticky;
top: 0;
left: 0;
right: 0;
z-index: 1;
}

.navds-combobox__list-item,
.navds-combobox__list-item__no-options,
.navds-combobox__list-item__new-option {
.navds-combobox__list-item--loading,
.navds-combobox__list-item--no-options,
.navds-combobox__list-item--new-option,
.navds-combobox__list-item--max-selected {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: var(--a-spacing-3);
width: 100%;
background-color: var(--ac-combobox-list-item-bg, var(--a-surface-default));
scroll-margin-top: 50px;
}

.navds-form-field--small .navds-combobox__list-item,
.navds-form-field--small .navds-combobox__list-item__no-options,
.navds-form-field--small .navds-combobox__list-item__new-option {
.navds-form-field--small .navds-combobox__list-item--loading,
.navds-form-field--small .navds-combobox__list-item--no-options,
.navds-form-field--small .navds-combobox__list-item--new-option,
.navds-form-field--small .navds-combobox__list-item--max-selected {
padding: calc(var(--a-spacing-3) / 2) var(--a-spacing-2);
}

.navds-combobox__list-item--loading {
display: flex;
justify-content: center;
padding: var(--a-spacing-3);
background-color: var(--ac-combobox-list-item-loading-bg, var(--a-surface-default));
}

.navds-combobox__list-item--max-selected {
background: var(--ac-combobox-list-item-max-selected-bg, var(--a-surface-info-subtle));
border-start-start-radius: calc(var(--a-border-radius-medium) - 1px);
border-start-end-radius: calc(var(--a-border-radius-medium) - 1px);
border: 1px solid var(--ac-combobox-list-item-max-selected-border, var(--a-border-info));
margin-bottom: calc(var(--a-spacing-1) * -1);
}

.navds-combobox__list_non-selectables:hover {
cursor: default;
}

/* ul-list and selectable li-items */

.navds-combobox__list-options {
list-style: none;
margin: 0;
padding: 0;
width: 100%;
display: inherit;
flex-direction: inherit;
gap: inherit;
background-color: inherit;
align-items: flex-start;
}

.navds-combobox__list-item--focus,
.navds-combobox__list--with-hover .navds-combobox__list-item:hover {
.navds-combobox__list--with-hover
.navds-combobox__list-item:not([data-no-focus="true"], .navds-combobox__list-item--new-option):hover {
background-color: var(--ac-combobox-list-item-hover-bg, var(--a-surface-hover));
cursor: pointer;
border-left: 4px solid var(--ac-combobox-list-item-hover-border-left, var(--a-border-strong));
Expand All @@ -280,6 +311,11 @@
padding-left: calc(var(--a-spacing-2) - 4px);
}

.navds-combobox__list-item[data-no-focus="true"] {
cursor: not-allowed;
opacity: 0.4;
}

.navds-combobox__list-item--selected {
background-color: var(--ac-combobox-list-item-selected-bg, var(--a-surface-selected));
}
Expand All @@ -295,20 +331,20 @@
padding-left: calc(var(--a-spacing-3) - 4px);
}

.navds-combobox__list-item__new-option {
.navds-combobox__list-item--new-option {
border-bottom: 1px solid var(--a-border-divider);
background: var(--a-surface-neutral-subtle);
cursor: pointer;
justify-content: flex-start;
gap: 0.25rem;
}

.navds-combobox__list--with-hover .navds-combobox__list-item__new-option:hover {
.navds-combobox__list--with-hover .navds-combobox__list-item--new-option:hover {
border-bottom: 1px solid var(--a-border-divider);
background: var(--a-surface-neutral-subtle-hover);
}

.navds-combobox__list-item__new-option--focus {
.navds-combobox__list-item--new-option--focus {
box-shadow:
var(--a-shadow-focus) inset,
var(--a-border-action) 0 0 0 5px inset;
Expand Down
2 changes: 2 additions & 0 deletions @navikt/core/css/tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@
"--ac-combobox-list-item-loading-bg": "--a-surface-default",
"--ac-combobox-list-item-hover-border-left": "--a-border-strong",
"--ac-combobox-list-item-selected-hover-border-left": "--a-border-focus",
"--ac-combobox-list-item-max-selected-bg": "--a-surface-info-subtle",
"--ac-combobox-list-item-max-selected-border": "--a-border-info",
"--ac-combobox-error-border": "--a-border-danger"
},
"select": {
Expand Down
2 changes: 1 addition & 1 deletion @navikt/core/react/src/form/combobox/Combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const Combobox = forwardRef<
"navds-combobox__wrapper-inner navds-text-field__input",
{
"navds-combobox__wrapper-inner--virtually-unfocused":
activeDecendantId !== null,
activeDecendantId !== undefined,
},
)}
onClick={focusInput}
Expand Down
2 changes: 2 additions & 0 deletions @navikt/core/react/src/form/combobox/ComboboxProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const ComboboxProvider = forwardRef<HTMLInputElement, ComboboxProps>(
isMultiSelect,
onToggleSelected,
selectedOptions,
maxSelected,
options,
value,
onChange,
Expand Down Expand Up @@ -71,6 +72,7 @@ const ComboboxProvider = forwardRef<HTMLInputElement, ComboboxProps>(
allowNewValues,
isMultiSelect,
selectedOptions,
maxSelected,
onToggleSelected,
options,
}}
Expand Down
1 change: 0 additions & 1 deletion @navikt/core/react/src/form/combobox/ComboboxWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ const ComboboxWrapper = ({
)}
onFocus={onFocusInsideWrapper}
onBlur={onBlurWrapper}
tabIndex={-1}
>
{children}
</div>
Expand Down
Loading

0 comments on commit 23c472e

Please sign in to comment.