Skip to content

Commit

Permalink
feat(forms/Select*): extend all select fields to support groups (#105)
Browse files Browse the repository at this point in the history
* feat(forms/Select*): extend all select fields to support groups

* chore: aggregate select group type into single definition
  • Loading branch information
sjschlapbach authored Jul 17, 2024
1 parent d6e4c6e commit 5a92307
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 119 deletions.
38 changes: 21 additions & 17 deletions src/Select.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import React, { useState } from 'react'
import Select from './Select'

const fruits = [
export const fruitsValues = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'pear', label: 'Pear' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'peach', label: 'Peach' },
{ value: 'mango', label: 'Mango' },
]
const vegetables = [
export const vegetablesValues = [
{ value: 'carrot', label: 'Carrot' },
{ value: 'cucumber', label: 'Cucumber' },
{ value: 'onion', label: 'Onion' },
{ value: 'potato', label: 'Potato' },
{ value: 'tomato', label: 'Tomato' },
{ value: 'broccoli', label: 'Broccoli' },
]
const transport = [
export const transportValues = [
{ value: 'car', label: 'Car' },
{ value: 'bike', label: 'Bike' },
{ value: 'train', label: 'Train' },
{ value: 'plane', label: 'Plane' },
{ value: 'boat', label: 'Boat' },
{ value: 'bus', label: 'Bus' },
]
const programming = [
export const programmingValues = [
{ value: 'javascript', label: 'JavaScript' },
{ value: 'typescript', label: 'TypeScript' },
{ value: 'python', label: 'Python' },
Expand All @@ -34,18 +34,22 @@ const programming = [
{ value: 'csharp', label: 'C#' },
]

const groups = [
{ items: fruits },
{ items: vegetables, showSeparator: true },
{ items: transport, showSeparator: true, label: 'Transport' },
{ items: programming, showSeparator: true, label: 'Programming Languages' },
export const groupValues = [
{ items: fruitsValues },
{ items: vegetablesValues, showSeparator: true },
{ items: transportValues, showSeparator: true, label: 'Transport' },
{
items: programmingValues,
showSeparator: true,
label: 'Programming Languages',
},
]

export const Default = () => {
return (
<Select
placeholder="Select an item"
items={fruits}
items={fruitsValues}
onChange={(newValue) => {
console.log(newValue)
}}
Expand All @@ -58,7 +62,7 @@ export const Popper = () => {
<Select
contentPosition="popper"
placeholder="Select an item"
items={fruits}
items={fruitsValues}
onChange={(newValue) => {
console.log(newValue)
}}
Expand All @@ -70,7 +74,7 @@ export const Basic = () => {
return (
<Select
placeholder="Select an item"
items={fruits}
items={fruitsValues}
onChange={(newValue) => {
console.log(newValue)
}}
Expand All @@ -83,11 +87,11 @@ export const DefaultValue = () => {
return (
<Select
placeholder="Select an item"
items={fruits}
items={fruitsValues}
onChange={(newValue) => {
console.log(newValue)
}}
defaultValue={fruits[2].value}
defaultValue={fruitsValues[2].value}
/>
)
}
Expand All @@ -106,7 +110,7 @@ export const Groups = () => {
<Select
placeholder="Select an item"
groups={[
...groups,
...groupValues,
{
label: 'Short labels',
items: [
Expand Down Expand Up @@ -181,7 +185,7 @@ export const Styled = () => {
<div className="h-12 w-full rounded-md border border-solid">
<Select
placeholder="Select an item"
groups={groups}
groups={groupValues}
onChange={(newValue) => {
console.log(newValue)
}}
Expand All @@ -203,7 +207,7 @@ export const Small = () => {
return (
<Select
placeholder="Select an item"
groups={groups}
groups={groupValues}
onChange={(newValue) => console.log(newValue)}
size="sm"
/>
Expand Down
12 changes: 7 additions & 5 deletions src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,19 @@ export interface Item {
shortLabel?: string
}

export interface Group {
label?: string
showSeparator?: boolean
items: Item[]
}

export interface SelectWithItemsProps extends SelectProps {
items: Item[]
groups?: never
}

export interface SelectWithGroupsProps extends SelectProps {
groups: {
label?: string
showSeparator?: boolean
items: Item[]
}[]
groups: Group[]
items?: never
}

Expand Down
32 changes: 32 additions & 0 deletions src/forms/FormikSelectField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Form, Formik } from 'formik'
import React from 'react'
import * as yup from 'yup'
import Button from '../Button'
import { groupValues } from '../Select.stories'
import FormikSelectField from './FormikSelectField'

export const Default = () => (
Expand Down Expand Up @@ -38,6 +39,37 @@ export const Default = () => (
</div>
)

export const Groups = () => (
<div>
<Formik
initialValues={{
name: undefined,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.name}`)
resetForm()
}}
>
{({ values }) => {
return (
<div>
<Form>
<FormikSelectField
name="name"
groups={groupValues}
label="Name"
placeholder="Select a name"
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.name}</div>
</div>
)
}}
</Formik>
</div>
)

export const Popper = () => (
<div>
<Formik
Expand Down
65 changes: 46 additions & 19 deletions src/forms/FormikSelectField.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useField } from 'formik'
import React from 'react'
import { twMerge } from 'tailwind-merge'
import Select, { Item, SelectClassName } from '../Select'
import Select, { Group, Item, SelectClassName } from '../Select'
import Label from './Label'

export interface SelectFieldProps {
interface SelectFieldProps {
id?: string
data?: {
cy?: string
Expand All @@ -16,7 +16,6 @@ export interface SelectFieldProps {
placeholder?: string
tooltip?: string | React.ReactNode
required?: boolean
items: Item[]
disabled?: boolean
hideError?: boolean
contentPosition?: 'item-aligned' | 'popper'
Expand All @@ -29,21 +28,32 @@ export interface SelectFieldProps {
}
}

export interface SelectFieldItemsProps extends SelectFieldProps {
items: Item[]
groups?: never
}

export interface SelectFieldGroupsProps extends SelectFieldProps {
groups: Group[]
items?: never
}

/**
* This component returns a select field that works as to be expected in a Formik environment.
* State is managed by Formik through the name attribute.
*
* @param id - The id of the field.
* @param data - The object of data attributes that can be used for testing (e.g. data-test or data-cy)
* @param name - The name of the field. This is used to identify the field in Formik.
* @param items - The array of items that should be available on the select component.
* @param groups - The optional groups array can be used to group items in the select component.
* @param label - The optional label is shown next to the field in the form.
* @param labelType - The optional labelType can be used to change the size and position of the label according to pre-defined standards.
* @param placeholder - The optional placeholder is shown when no value is selected / initialization with 'undefined' is chosen.
* @param disabled - The optional disabled prop disables the select component.
* @param hideError - Hide the error message below this component as is might be more appropriate to show it somewhere else.
* @param contentPosition - The position of the content of the select component. Currently only 'item-aligned' and 'popper' are supported.
* @param tooltip - The optional tooltip is shown on hover next to the label.
* @param items - The array of items that should be available on the select component.
* @param required - Indicate whether the field is required or not.
* @param className - The optional className object allows you to override the default styling.
* @returns Select component with formik state management.
Expand All @@ -52,18 +62,19 @@ export function FormikSelectField({
id,
data,
name,
items,
groups,
label,
labelType = 'normal',
placeholder,
tooltip,
required = false,
items,
disabled = false,
hideError = false,
contentPosition = 'item-aligned',
className,
...props
}: SelectFieldProps) {
}: SelectFieldItemsProps | SelectFieldGroupsProps) {
const [field, meta, helpers] = useField(name)

return (
Expand Down Expand Up @@ -93,19 +104,35 @@ export function FormikSelectField({
showTooltipSymbol={typeof tooltip !== 'undefined'}
/>
)}
<Select
data={data}
onChange={(newValue: string) => helpers.setValue(newValue)}
onBlur={() => helpers.setTouched(true)}
value={field.value}
name={name}
items={items}
placeholder={placeholder}
disabled={disabled}
className={className?.select}
contentPosition={contentPosition}
{...props}
/>
{items ? (
<Select
data={data}
onChange={(newValue: string) => helpers.setValue(newValue)}
onBlur={() => helpers.setTouched(true)}
value={field.value}
name={name}
items={items}
placeholder={placeholder}
disabled={disabled}
className={className?.select}
contentPosition={contentPosition}
{...props}
/>
) : (
<Select
data={data}
onChange={(newValue: string) => helpers.setValue(newValue)}
onBlur={() => helpers.setTouched(true)}
value={field.value}
name={name}
groups={groups}
placeholder={placeholder}
disabled={disabled}
className={className?.select}
contentPosition={contentPosition}
{...props}
/>
)}
</div>
{!hideError && meta.touched && meta.error && (
<div
Expand Down
Loading

0 comments on commit 5a92307

Please sign in to comment.