diff --git a/src/Select.stories.tsx b/src/Select.stories.tsx index 87e84cca..1f7f28f3 100644 --- a/src/Select.stories.tsx +++ b/src/Select.stories.tsx @@ -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' }, ] @@ -126,7 +126,7 @@ export const Groups = () => { label: 'Pear long label', shortLabel: 'Pear', }, - { value: 'watermeldon_short', label: 'Watermelon' }, + { value: 'watermelon_short', label: 'Watermelon' }, ], }, ]} @@ -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' }, ] @@ -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' }, ] diff --git a/src/forms/NewFormikSelectField.stories.tsx b/src/forms/NewFormikSelectField.stories.tsx new file mode 100644 index 00000000..47dd926d --- /dev/null +++ b/src/forms/NewFormikSelectField.stories.tsx @@ -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 ( + { + alert(`Form submitted with value: ${values.fruit}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.fruit}
+
+ ) + }} +
+ ) +} + +export const Required = () => { + return ( + { + alert(`Form submitted with value: ${values.fruit}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.fruit}
+
+ ) + }} +
+ ) +} + +export const Disabled = () => { + return ( + { + alert(`Form submitted with value: ${values.fruit}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.fruit}
+
+ ) + }} +
+ ) +} + +export const LargeLabel = () => { + return ( + { + alert(`Form submitted with value: ${values.fruit}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.fruit}
+
+ ) + }} +
+ ) +} + +export const Error = () => { + return ( + { + alert(`Form submitted with value: ${values.fruit}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + + +
Value: {values.fruit}
+
+ ) + }} +
+ ) +} + +export const Tooltip = () => { + return ( + { + alert(`Form submitted with value: ${values.fruit}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + + + +
Value: {values.fruit}
+
+ ) + }} +
+ ) +} diff --git a/src/forms/NewFormikSelectField.tsx b/src/forms/NewFormikSelectField.tsx new file mode 100644 index 00000000..fdb2d6f1 --- /dev/null +++ b/src/forms/NewFormikSelectField.tsx @@ -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 ( + 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 diff --git a/src/forms/SelectField.stories.tsx b/src/forms/SelectField.stories.tsx new file mode 100644 index 00000000..fe55b125 --- /dev/null +++ b/src/forms/SelectField.stories.tsx @@ -0,0 +1,169 @@ +import React, { useState } from 'react' +import SelectField from './SelectField' + +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 = () => { + const [value, setValue] = useState(items[0].value) + + return ( + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + ) +} + +export const Required = () => { + const [value, setValue] = useState(items[0].value) + + return ( + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + ) +} + +export const Disabled = () => { + const [value, setValue] = useState(items[0].value) + + return ( + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + ) +} + +export const Label = () => { + const [value, setValue] = useState(items[0].value) + + return ( + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + ) +} + +export const LargeLabel = () => { + const [value, setValue] = useState(items[0].value) + + return ( + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + ) +} + +export const Error = () => { + const [value, setValue] = useState(items[0].value) + + return ( +
+ { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> +
+ ) +} + +export const Tooltip = () => { + const [value, setValue] = useState(items[0].value) + + return ( +
+ { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> + { + alert(`Value changed to: ${newValue}`) + setValue(newValue) + }} + /> +
+ ) +} diff --git a/src/forms/SelectField.tsx b/src/forms/SelectField.tsx new file mode 100644 index 00000000..84a57eb4 --- /dev/null +++ b/src/forms/SelectField.tsx @@ -0,0 +1,147 @@ +import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React from 'react' +import { twMerge } from 'tailwind-merge' +import Select, { Item, SelectClassName } from '../Select' +import { Tooltip } from '../Tooltip' +import Label from './Label' + +export interface SelectFieldProps { + id?: string + data?: { + cy?: string + test?: string + } + name?: string + value: string + onChange: (newValue: string) => void + onBlur?: () => void + 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 value - The value of the field (external state management). + * @param onChange - The onChange function of the field (external state management). + * @param onBlur - The onBlur function of the field (external state management). + * @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 SelectField({ + id, + data, + name, + value, + onChange, + onBlur, + label, + labelType = 'small', + placeholder, + tooltip, + required = false, + items, + disabled = false, + error, + hideError = false, + contentPosition = 'item-aligned', + className, + ...props +}: SelectFieldProps) { + return ( +
+
+ {label && ( +