From c8afa105dd37346c11bf8ab05aea95f04df01c23 Mon Sep 17 00:00:00 2001 From: Nathan Vasse Date: Mon, 12 Jun 2023 14:27:40 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(react)=20add=20multi=20select?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding this new variant makes necessary to reorganize the files to keep a clear separations of concerns. As of now Select/index.tsx is just an entrypoint to render either the mono or multi variant of the select. --- .changeset/seven-bulldogs-bow.md | 5 + .../src/components/Forms/Select/index.scss | 126 +- .../src/components/Forms/Select/index.tsx | 364 +---- .../components/Forms/Select/mono-common.tsx | 200 +++ .../Forms/Select/mono-searchable.tsx | 79 ++ .../components/Forms/Select/mono-simple.tsx | 33 + .../Select/{index.spec.tsx => mono.spec.tsx} | 47 +- .../src/components/Forms/Select/mono.tsx | 54 + .../components/Forms/Select/multi-common.tsx | 218 +++ .../Forms/Select/multi-searchable.tsx | 143 ++ .../components/Forms/Select/multi-simple.tsx | 99 ++ .../src/components/Forms/Select/multi.mdx | 112 ++ .../components/Forms/Select/multi.spec.tsx | 1236 +++++++++++++++++ .../components/Forms/Select/multi.stories.tsx | 306 ++++ .../src/components/Forms/Select/multi.tsx | 58 + .../components/Forms/Select/test-utils.tsx | 45 + .../src/components/Forms/Select/tokens.ts | 2 + packages/react/src/cunningham-tokens.css | 2 + packages/react/src/cunningham-tokens.js | 2 +- packages/react/src/cunningham-tokens.ts | 2 +- packages/react/src/locales/en-US.json | 3 +- packages/react/src/locales/fr-FR.json | 3 +- 22 files changed, 2734 insertions(+), 405 deletions(-) create mode 100644 .changeset/seven-bulldogs-bow.md create mode 100644 packages/react/src/components/Forms/Select/mono-common.tsx create mode 100644 packages/react/src/components/Forms/Select/mono-searchable.tsx create mode 100644 packages/react/src/components/Forms/Select/mono-simple.tsx rename packages/react/src/components/Forms/Select/{index.spec.tsx => mono.spec.tsx} (95%) create mode 100644 packages/react/src/components/Forms/Select/mono.tsx create mode 100644 packages/react/src/components/Forms/Select/multi-common.tsx create mode 100644 packages/react/src/components/Forms/Select/multi-searchable.tsx create mode 100644 packages/react/src/components/Forms/Select/multi-simple.tsx create mode 100644 packages/react/src/components/Forms/Select/multi.mdx create mode 100644 packages/react/src/components/Forms/Select/multi.spec.tsx create mode 100644 packages/react/src/components/Forms/Select/multi.stories.tsx create mode 100644 packages/react/src/components/Forms/Select/multi.tsx create mode 100644 packages/react/src/components/Forms/Select/test-utils.tsx diff --git a/.changeset/seven-bulldogs-bow.md b/.changeset/seven-bulldogs-bow.md new file mode 100644 index 0000000..da78c0f --- /dev/null +++ b/.changeset/seven-bulldogs-bow.md @@ -0,0 +1,5 @@ +--- +"@openfun/cunningham-react": minor +--- + +add multi select diff --git a/packages/react/src/components/Forms/Select/index.scss b/packages/react/src/components/Forms/Select/index.scss index 49a2949..faf0540 100644 --- a/packages/react/src/components/Forms/Select/index.scss +++ b/packages/react/src/components/Forms/Select/index.scss @@ -9,7 +9,6 @@ border-color: var(--c--components--forms-select--border-color); border-style: var(--c--components--forms-select--border-style); display: flex; - align-items: center; transition: border var(--c--theme--transitions--duration) var(--c--theme--transitions--ease-out); padding: 0 0.75rem; gap: 1rem; @@ -48,8 +47,6 @@ min-width: 0; &__value { - white-space: nowrap; - overflow: hidden; text-overflow: ellipsis; flex-grow: 1; font-size: var(--c--components--forms-select--font-size); @@ -66,9 +63,11 @@ &__actions { position: relative; - top: -14px; + top: 3px; display: flex; align-items: center; + // This is made to avoid this relative element to force its container height. + height: 0; span { font-size: 1.25rem; @@ -152,7 +151,7 @@ border-color: var(--c--theme--colors--greyscale-200); cursor: default; - label { + label, input { cursor: default; color: var(--c--theme--colors--greyscale-600); } @@ -188,4 +187,121 @@ } } } + + &--mono { + .c__select__inner { + &__value { + white-space: nowrap; + overflow: hidden; + } + } + } + + &--multi { + .c__select__wrapper { + height: auto; + min-height: var(--c--components--forms-select--height); + } + + .c__select__inner { + display: block; + + &__value { + gap: 0.25rem; + + &__pill { + background-color: var(--c--components--forms-select--multi-pill-background-color); + padding: 0.375rem 0.5rem; + border-radius: var(--c--components--forms-select--multi-pill-border-radius); + display: inline-flex; + align-items: center; + gap: 0.25rem; + margin-right: 0.25rem; + margin-bottom: 0.25rem; + max-width: var(--c--components--forms-select--multi-pill-max-width); + + > span { + min-width: 0; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + &__clear { + width: auto; + height: auto; + + .material-icons { + font-size: 1.1250rem; + } + } + } + + &__input { + display: inline-grid; + width: 0; + overflow: hidden; + + &:focus-within { + width: auto; + } + + &::after, + input { + width: auto; + min-width: 1em; + grid-area: 1 / 2; + font: inherit; + padding: 0; + margin: 0; + resize: none; + background: none; + appearance: none; + border: none; + } + + &::after { + content: attr(data-value) ' '; + visibility: hidden; + white-space: pre-wrap; + } + } + } + + &__actions { + float: right; + position: relative; + height: 0; + top: 3px; + } + } + + + /** Modifiers */ + + &.c__select--populated { + .c__select__inner__value { + .c__select__inner__value__input { + // To match the height of the pills. + height: 2rem; + } + } + + .c__select__inner__actions { + // Now we need it to occupy space in order to make float: right work. + height: auto; + top: -8px; + } + + .labelled-box--no-label { + .c__select__inner__actions { + top: 4px; + } + + .c__select__inner__value { + padding-top: 0.25rem; + } + } + } + } } diff --git a/packages/react/src/components/Forms/Select/index.tsx b/packages/react/src/components/Forms/Select/index.tsx index 1ae3bfa..9676fff 100644 --- a/packages/react/src/components/Forms/Select/index.tsx +++ b/packages/react/src/components/Forms/Select/index.tsx @@ -1,367 +1,13 @@ -import React, { - HTMLAttributes, - PropsWithChildren, - useEffect, - useRef, - useState, -} from "react"; -import { - useCombobox, - useSelect, - UseSelectReturnValue, - UseSelectStateChange, -} from "downshift"; -import classNames from "classnames"; -import { useCunningham } from ":/components/Provider"; -import { Field, FieldProps } from ":/components/Forms/Field"; -import { LabelledBox } from ":/components/Forms/LabelledBox"; -import { Button } from ":/components/Button"; +import React from "react"; +import { SelectMulti } from ":/components/Forms/Select/multi"; +import { SelectMono, SelectProps } from ":/components/Forms/Select/mono"; -interface Option { - value?: string; - label: string; - disabled?: boolean; -} - -type Props = PropsWithChildren & - FieldProps & { - label: string; - hideLabel?: boolean; - options: Option[]; - searchable?: boolean; - name?: string; - defaultValue?: string | number; - value?: string | number; - onChange?: (event: { - target: { value: string | number | undefined }; - }) => void; - disabled?: boolean; - clearable?: boolean; - }; - -function getOptionsFilter(inputValue?: string) { - return (option: Option) => { - return ( - !inputValue || - option.label.toLowerCase().includes(inputValue.toLowerCase()) || - option.value?.toLowerCase().includes(inputValue.toLowerCase()) - ); - }; -} - -const optionToString = (option: Option | null) => { - return option ? option.label : ""; -}; - -const optionToValue = (option: Option) => { - return option.value ?? option.label; -}; - -interface SubProps extends Props { - defaultSelectedItem?: Option; - downshiftProps: { - initialSelectedItem?: Option; - onSelectedItemChange?: any; - }; -} - -interface SelectAuxProps extends SubProps { - options: Option[]; - labelAsPlaceholder: boolean; - downshiftReturn: { - isOpen: boolean; - wrapperProps?: HTMLAttributes; - selectedItem?: Option | null; - getLabelProps: any; - toggleButtonProps: any; - getMenuProps: any; - getItemProps: any; - highlightedIndex: number; - selectItem: UseSelectReturnValue