Skip to content

Commit

Permalink
feat(formst/SelectField): add new select field component with corresp…
Browse files Browse the repository at this point in the history
…onding formik wrapper component (#104)
  • Loading branch information
sjschlapbach authored Jul 17, 2024
1 parent 5520b16 commit ac74e58
Show file tree
Hide file tree
Showing 5 changed files with 652 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const fruits = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'pear', label: 'Pear' },
{ value: 'watermeldon', label: 'Watermelon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'peach', label: 'Peach' },
{ value: 'mango', label: 'Mango' },
]
Expand Down Expand Up @@ -126,7 +126,7 @@ export const Groups = () => {
label: 'Pear long label',
shortLabel: 'Pear',
},
{ value: 'watermeldon_short', label: 'Watermelon' },
{ value: 'watermelon_short', label: 'Watermelon' },
],
},
]}
Expand Down Expand Up @@ -160,7 +160,7 @@ export const DisabledElements = () => {
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana', disabled: true },
{ value: 'pear', label: 'Pear' },
{ value: 'watermeldon', label: 'Watermelon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'peach', label: 'Peach', disabled: true },
{ value: 'mango', label: 'Mango' },
]
Expand Down Expand Up @@ -215,7 +215,7 @@ export const CustomTriggerLabel = () => {
{ value: 'apple', label: 'Apple long label', shortLabel: 'Apple' },
{ value: 'banana', label: 'Banana long label', shortLabel: 'Banana' },
{ value: 'pear', label: 'Pear long label' },
{ value: 'watermeldon', label: 'Watermelon long label' },
{ value: 'watermelon', label: 'Watermelon long label' },
{ value: 'peach', label: 'Peach long label' },
{ value: 'mango', label: 'Mango long label', shortLabel: 'Mango' },
]
Expand Down
237 changes: 237 additions & 0 deletions src/forms/NewFormikSelectField.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import { Form, Formik } from 'formik'
import React from 'react'
import { Button } from '../Button'
import FormikSelectField from './NewFormikSelectField'

const items = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'pear', label: 'Pear' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'peach', label: 'Peach' },
{ value: 'mango', label: 'Mango' },
]

export const Default = () => {
return (
<Formik
initialValues={{
fruit: undefined,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.fruit}`)
resetForm()
}}
>
{({ values }) => {
return (
<div className="flex flex-col gap-2">
<Form>
<FormikSelectField
name="fruit"
items={items}
label="Fruits"
placeholder="Select a fruit"
className={{ root: 'mb-2' }}
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.fruit}</div>
</div>
)
}}
</Formik>
)
}

export const Required = () => {
return (
<Formik
initialValues={{
fruit: undefined,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.fruit}`)
resetForm()
}}
>
{({ values }) => {
return (
<div className="flex flex-col gap-2">
<Form>
<FormikSelectField
required
name="fruit"
items={items}
label="Fruits"
placeholder="Select a fruit"
className={{ root: 'mb-2' }}
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.fruit}</div>
</div>
)
}}
</Formik>
)
}

export const Disabled = () => {
return (
<Formik
initialValues={{
fruit: items[0].value,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.fruit}`)
resetForm()
}}
>
{({ values }) => {
return (
<div className="flex flex-col gap-2">
<Form>
<FormikSelectField
disabled
name="fruit"
items={items}
label="Fruits"
placeholder="Select a fruit"
className={{ root: 'mb-2' }}
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.fruit}</div>
</div>
)
}}
</Formik>
)
}

export const LargeLabel = () => {
return (
<Formik
initialValues={{
fruit: undefined,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.fruit}`)
resetForm()
}}
>
{({ values }) => {
return (
<div className="flex flex-col gap-2">
<Form>
<FormikSelectField
name="fruit"
items={items}
label="Fruits"
labelType="large"
placeholder="Select a fruit"
className={{ root: 'mb-2' }}
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.fruit}</div>
</div>
)
}}
</Formik>
)
}

export const Error = () => {
return (
<Formik
initialValues={{
fruit: undefined,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.fruit}`)
resetForm()
}}
>
{({ values }) => {
return (
<div className="flex flex-col gap-2">
<Form>
<FormikSelectField
name="fruit"
items={items}
label="Fruits"
labelType="large"
error="Error message"
placeholder="Select a fruit"
className={{ root: 'mb-2' }}
/>
<FormikSelectField
name="fruit"
items={items}
label="Fruits"
error="Error message"
placeholder="Select a fruit"
className={{ root: 'mb-2' }}
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.fruit}</div>
</div>
)
}}
</Formik>
)
}

export const Tooltip = () => {
return (
<Formik
initialValues={{
fruit: items[0].value,
}}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.fruit}`)
resetForm()
}}
>
{({ values }) => {
return (
<div>
<Form className="flex flex-col gap-3">
<FormikSelectField
required
name="fruit"
label="Label"
tooltip="Tooltip for this input"
items={items}
/>
<FormikSelectField
required
name="fruit"
label="Label"
labelType="large"
tooltip="Tooltip for this input"
items={items}
/>
<FormikSelectField
required
name="fruit"
label="Label"
labelType="large"
tooltip="Tooltip for this input"
error="Error message"
items={items}
/>
<Button type="submit" className={{ root: 'w-max' }}>
Submit
</Button>
</Form>
<div>Value: {values.fruit}</div>
</div>
)
}}
</Formik>
)
}
95 changes: 95 additions & 0 deletions src/forms/NewFormikSelectField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { useField } from 'formik'
import React from 'react'
import { Item, SelectClassName } from '../Select'
import SelectField from './SelectField'

export interface FormikSelectFieldProps {
id?: string
data?: {
cy?: string
test?: string
}
name: string
label?: string
labelType?: 'small' | 'large'
placeholder?: string
tooltip?: string | React.ReactNode
required?: boolean
items: Item[]
disabled?: boolean
error?: string
hideError?: boolean
contentPosition?: 'item-aligned' | 'popper'
className?: {
root?: string
label?: string
error?: string
tooltip?: string
select?: SelectClassName
}
}

/**
* 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.
* @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 error - The optional error message is shown next to the 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.
*/
export function FormikSelectField({
id,
data,
name,
label,
labelType = 'small',
placeholder,
tooltip,
required = false,
items,
disabled = false,
error,
hideError = false,
contentPosition = 'item-aligned',
className,
...props
}: FormikSelectFieldProps) {
const [field, meta, helpers] = useField(name)

return (
<SelectField
id={id}
data={data}
name={name}
value={field.value}
onChange={(newValue: string) => helpers.setValue(newValue)}
onBlur={() => helpers.setTouched(true)}
label={label}
labelType={labelType}
placeholder={placeholder}
tooltip={tooltip}
required={required}
items={items}
disabled={disabled}
error={!!meta.error && meta.touched ? meta.error : error}
hideError={hideError}
contentPosition={contentPosition}
className={className}
{...props}
/>
)
}

export default FormikSelectField
Loading

0 comments on commit ac74e58

Please sign in to comment.