Skip to content

Commit

Permalink
Merge pull request #385 from IQSS/feature/355-select-multiple-component
Browse files Browse the repository at this point in the history
Feature/355 select multiple component
  • Loading branch information
GPortas authored May 6, 2024
2 parents 27b8a25 + 5a12f28 commit 4fa9b65
Show file tree
Hide file tree
Showing 21 changed files with 1,247 additions and 13 deletions.
12 changes: 8 additions & 4 deletions packages/design-system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
- **FormChecboxGroup:** refactor styles.
- **FormGroupWithMultipleFields:** refactor styles and conditional render logic.
- **FormGroup:** ability to clone children wrapped by react fragments.
- **FormCheckbox:** ability to forward react ref to input.
- **FormInput:** ability to forward react ref to input.
- **FormSelect:** ability to forward react ref to input.
- **FormTextArea:** ability to forward react ref to input.
- **FormCheckbox:** ability to forward react ref to input and export FormCheckboxProps interface.
- **FormInput:** ability to forward react ref to input and export FormInputProps interface.
- **FormSelect:** ability to forward react ref to input, add `isInvalid` `isValid` & `disabled` props and export FormSelectProps interface.
- **FormTextArea:** ability to forward react ref to input and export FormTextAreaProps interface.
- **FormFeedback:** remove `span: 9` from styles.
- **FormGroup:** controlId is now optional.
- **FormLabel:** extend Props Interface to accept `htmlFor` prop.
- **SelectMultiple:** NEW multiple selector for occasions when you can choose more than one option.
- **FormSelectMultiple:** The new multiple selector is added to the "FormGroup" components.
- **DropdownButtonItem:** extend Props Interface to accept `as` prop.

# [1.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@1.0.1...@iqss/dataverse-design-system@1.1.0) (2024-03-12)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import { Col, ColProps } from '../../grid/Col'
import { Row } from '../../grid/Row'
import { FormCheckbox } from './form-element/FormCheckbox'
import { FormFeedback } from './form-element/FormFeedback'
import { FormSelectMultiple } from './form-element/FormSelectMultiple'

interface FormGroupProps extends ColProps {
as?: typeof Col | typeof Row
required?: boolean
controlId: string
controlId?: string
fieldIndex?: string
}

Expand All @@ -29,7 +30,7 @@ function FormGroup({

return (
<FormBS.Group
controlId={fieldIndex ? `${controlId}-${fieldIndex}` : controlId}
controlId={controlId ? (fieldIndex ? `${controlId}-${fieldIndex}` : controlId) : undefined}
className="mb-3"
as={as}
{...props}>
Expand Down Expand Up @@ -64,6 +65,7 @@ function cloneThroughFragments(
FormGroup.Label = FormLabel
FormGroup.Input = FormInput
FormGroup.Select = FormSelect
FormGroup.SelectMultiple = FormSelectMultiple
FormGroup.TextArea = FormTextArea
FormGroup.Text = FormText
FormGroup.Checkbox = FormCheckbox
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Form as FormBS } from 'react-bootstrap'
import * as React from 'react'

interface FormCheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
export interface FormCheckboxProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
id: string
label: string
isValid?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as React from 'react'

export type FormInputElement = HTMLInputElement | HTMLTextAreaElement

interface FormInputProps extends React.HTMLAttributes<FormInputElement> {
export interface FormInputProps extends React.HTMLAttributes<FormInputElement> {
type?: 'text' | 'email' | 'password'
readOnly?: boolean
withinMultipleFieldsGroup?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ interface FormLabelProps {
required?: boolean
message?: string
withinMultipleFieldsGroup?: boolean
htmlFor?: string
}

export function FormLabel({
required,
message,
withinMultipleFieldsGroup,
htmlFor,
children
}: PropsWithChildren<FormLabelProps>) {
const layoutProps = withinMultipleFieldsGroup ? {} : { column: true, sm: 3 }

return (
<FormBS.Label {...layoutProps}>
<FormBS.Label htmlFor={htmlFor} {...layoutProps}>
{children}
{required && <RequiredInputSymbol />}{' '}
{message && <QuestionMarkTooltip placement="right" message={message}></QuestionMarkTooltip>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,33 @@ import { Form as FormBS } from 'react-bootstrap'
import { FormElementLayout } from './FormElementLayout'
import * as React from 'react'

interface FormSelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
export interface FormSelectProps
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
isInvalid?: boolean
isValid?: boolean
disabled?: boolean
withinMultipleFieldsGroup?: boolean
}

export const FormSelect = React.forwardRef(function FormSelect(
{ withinMultipleFieldsGroup, children, ...props }: PropsWithChildren<FormSelectProps>,
{
withinMultipleFieldsGroup,
isInvalid,
isValid,
disabled,
children,
...props
}: PropsWithChildren<FormSelectProps>,
ref
) {
return (
<FormElementLayout withinMultipleFieldsGroup={withinMultipleFieldsGroup}>
<FormBS.Select ref={ref as React.ForwardedRef<HTMLSelectElement>} {...props}>
<FormBS.Select
isInvalid={isInvalid}
isValid={isValid}
disabled={disabled}
ref={ref as React.ForwardedRef<HTMLSelectElement>}
{...props}>
{children}
</FormBS.Select>
</FormElementLayout>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PropsWithChildren, forwardRef } from 'react'
import { FormElementLayout } from './FormElementLayout'
import { SelectMultiple, SelectMultipleProps } from '../../../select-multiple/SelectMultiple'

export interface FormSelectMultipleProps extends SelectMultipleProps {
withinMultipleFieldsGroup?: boolean
inputButtonId: string
}

export const FormSelectMultiple = forwardRef(
({ withinMultipleFieldsGroup, ...props }: PropsWithChildren<FormSelectMultipleProps>, ref) => {
return (
<FormElementLayout withinMultipleFieldsGroup={withinMultipleFieldsGroup}>
<SelectMultiple ref={ref as React.ForwardedRef<HTMLInputElement>} {...props} />
</FormElementLayout>
)
}
)

FormSelectMultiple.displayName = 'FormSelectMultiple'
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormElementLayout } from './FormElementLayout'
import * as React from 'react'

export type FormInputElement = HTMLInputElement | HTMLTextAreaElement
interface FormTextAreaProps extends Omit<React.HTMLAttributes<FormInputElement>, 'rows'> {
export interface FormTextAreaProps extends Omit<React.HTMLAttributes<FormInputElement>, 'rows'> {
withinMultipleFieldsGroup?: boolean
name?: string
disabled?: boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
@use 'sass:color';
@import 'src/lib/assets/styles/design-tokens/colors.module';
@import 'src/lib/assets/styles/design-tokens/typography.module';

:root {
--select-multiple-menu-max-height: 300px;
--toggle-padding: 6px 36px 6px 12px;
}

.select-multiple-toggle {
position: relative;
display: grid;
background-color: #fff;

&.disabled {
opacity: 0.6;
pointer-events: none;
}

// Chevron icon
&::after {
position: absolute;
top: 50%;
right: 1rem;
display: inline-block;
width: 0.45em;
height: 0.45em;
border-color: rgb(0 0 0 / 75%);
border-style: solid;
border-width: 2px 2px 0 0;
transform: translateY(-60%) rotate(-225deg);
content: '';
}

&__input-button {
position: relative;
z-index: 1;
display: flex;
grid-area: 1 / 1;
align-items: center;
justify-content: space-between;
width: 100%;
padding: var(--toggle-padding);
background-color: transparent;
border: solid 1px $dv-secondary-color;
border-radius: 6px;
cursor: pointer;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;

&::after {
display: none;
}

&.invalid {
border-color: $dv-danger-color;
}

&:focus-within,
&:focus {
border-color: color.adjust($dv-primary-color, $alpha: -0.4);
outline: 0;
box-shadow: 0 0 0 0.25rem color.adjust($dv-primary-color, $alpha: -0.7);
}

&.invalid:focus-within,
&.invalid:focus {
border-color: color.adjust($dv-danger-color, $alpha: -0.4);
outline: 0;
box-shadow: 0 0 0 0.25rem color.adjust($dv-danger-color, $alpha: -0.7);
}
}

&__inner-content {
position: relative;
z-index: 2;
grid-area: 1 / 1;
padding: var(--toggle-padding);
pointer-events: none;

.selected-options-container {
display: inline-flex;
flex: 1;
flex-wrap: wrap;
gap: 0.5rem;
pointer-events: none;

&__item {
display: flex;
align-items: center;
padding: 0.25rem 0.5rem;
color: $dv-text-color;
font-weight: $dv-font-weight-bold;
font-size: $dv-font-size-sm;
background-color: $dv-secondary-color;
border-radius: 0.25rem;
cursor: pointer;
transition: background-color 0.2s;
pointer-events: all;

button {
display: grid;
place-items: center;
padding: 0;
border-radius: 50%;
}
}
}
}
}

.select-multiple-menu {
width: 100%;
max-width: 100%;
max-height: var(--select-multiple-menu-max-height);
padding-top: 0;
overflow-x: hidden;
overflow-y: auto;
box-shadow: 0 5px 10px rgb(0 0 0 / 15%);

.menu-header {
position: sticky;
top: 0;
display: flex;
gap: 0.5rem;
align-items: center;
margin-bottom: 0.25rem;
font-size: 18px;
background-color: $dv-secondary-color;

.selected-count {
margin: 0;
color: $dv-text-color;
font-size: $dv-font-size;
}
}

.option-item {
padding-block: 0;
transition: background-color 0.1s ease-in-out;

&:active,
&:hover {
color: $dv-text-color;
background-color: color.adjust($dv-secondary-color, $alpha: -0.7);
}

&:has(input[type='checkbox']:checked) {
background-color: color.adjust($dv-secondary-color, $alpha: -0.4);
}

&__checkbox-input {
padding-block: 0.25rem;

label {
width: 100%;
}
}
}
}
Loading

0 comments on commit 4fa9b65

Please sign in to comment.