Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(formst/SelectField): add new select field component with corresponding formik wrapper component #104

Merged
merged 1 commit into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading