diff --git a/src/forms/NewFormikTextField.stories.tsx b/src/forms/NewFormikTextField.stories.tsx index 305d614a..efcf95f7 100644 --- a/src/forms/NewFormikTextField.stories.tsx +++ b/src/forms/NewFormikTextField.stories.tsx @@ -171,11 +171,11 @@ export const OnChangeError = () => (
{ - setFieldValue('name', newValue.replace(/\s/g, '')) + setFieldValue('name', newValue) }} label="Label" tooltip="Tooltip for this input" diff --git a/src/forms/NewFormikTextareaField.stories.tsx b/src/forms/NewFormikTextareaField.stories.tsx new file mode 100644 index 00000000..1725127e --- /dev/null +++ b/src/forms/NewFormikTextareaField.stories.tsx @@ -0,0 +1,349 @@ +import { Form, Formik } from 'formik' +import React from 'react' +import * as yup from 'yup' +import Button from '../Button' +import FormikTextareaField from './NewFormikTextareaField' + +export const Default = () => ( +
+
The default Formik field works with a "name" input
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+ + + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const MaxLength = () => ( +
+
+ The Formik field works with a "name" input and maximum length (including + label) +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const Disabled = () => ( +
+
The default Formik field works with a "name" input
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const Required = () => ( +
+
+ By adding a required attribute, the label of the field changes it + appearance +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const OnChangeFunction = () => ( +
+
+ An alternative version of the text field input allows to work with a + "value" and "onChange" attribute instead of the "name" attribute. This + field is modified in a way that whitespaces are removed from the input on + change. +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values, setFieldValue }) => { + return ( +
+
+ { + setFieldValue('name', newValue.replace(/\s/g, '')) + }} + label="Label" + tooltip="Tooltip for this input" + className={{ root: 'mb-1' }} + placeholder="Placeholder" + /> + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const OnChangeError = () => ( +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values, setFieldValue }) => { + return ( +
+
+ { + setFieldValue('name', newValue) + }} + label="Label" + tooltip="Tooltip for this input" + className={{ root: 'mb-1' }} + placeholder="Placeholder" + /> + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const Styled = () => ( +
+
The default Formik field works with a "name" input
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const Validation = () => ( +
+
+ This text field should have a maximum length of 10 characters or will + display an error otherwise. +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + validationSchema={yup.object().shape({ + name: yup + .string() + .required('This field is required') + .max(10, 'Max 10 characters'), + })} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const LargeLabel = () => ( +
+
Formik text area component with a large label
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) diff --git a/src/forms/NewFormikTextareaField.tsx b/src/forms/NewFormikTextareaField.tsx new file mode 100644 index 00000000..90617673 --- /dev/null +++ b/src/forms/NewFormikTextareaField.tsx @@ -0,0 +1,138 @@ +import { useField } from 'formik' +import React from 'react' +import TextaraeField from './TextareaField' + +interface FormikTextareaFieldProps { + id?: string + data?: { + cy?: string + test?: string + } + label?: string + labelType?: 'small' | 'large' + placeholder?: string + tooltip?: string | React.ReactNode + maxLength?: number + maxLengthUnit?: string + hideMaxLength?: boolean + required?: boolean + hideError?: boolean + disabled?: boolean + className?: { + root?: string + field?: string + icon?: string + label?: string + input?: string + error?: string + tooltip?: string + } +} + +// type structure ensures that either a name or a value and onChange function are passed +export interface FormikTextareaFieldWithNameProps + extends FormikTextareaFieldProps { + name: string + value?: never + onChange?: never + error?: never + [key: string]: any +} +export interface FormikTextareaFieldWithOnChangeProps + extends FormikTextareaFieldProps { + name?: never + value: string + onChange: (newValue: string) => void + error?: string + [key: string]: any +} + +/** + * This function returns a text field that works as to be expected in a Formik environment. + * State can be managed either through Formik or internally by passing a value and onChange function. + * + * @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 as used to keep track of the state in Formik. If no value and onChange function are provided, this field is required. + * @param value - The value of the field. This is used to manage the state internally. If no name is provided, this field is required. + * @param onChange - The onChange function is called when the value of the field changes. This is used to manage the state internally. If no name is provided, this field is required. + * @param error - The error message that is shown below the field. If a name is provided, this prop will not be used. + * @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 the field is empty. + * @param tooltip - The optional tooltip is shown on hover next to the label. + * @param maxLength - The optional maxLength is shown below the field to indicate the maximum number of characters allowed. + * @param maxLengthUnit - The optional maxLengthUnit is shown next to the maxLength to indicate the unit of the maximum number of characters allowed. + * @param hideMaxLength - Indicate whether the maxLength should be hidden or not. + * @param required - Indicate whether the field is required or not. + * @param hideError - Hide the error message below this component as is might be more appropriate to show it somewhere else. + * @param disabled - Disable the field. + * @param className - The optional className object allows you to override the default styling. + * @returns Text field component with Formik state management. + */ +export function FormikTextareaField({ + id, + data, + name, + value, + onChange, + error, + label, + labelType = 'small', + icon, + onIconClick, + placeholder, + tooltip, + required = false, + hideError = false, + disabled = false, + className, + ...props +}: FormikTextareaFieldWithNameProps | FormikTextareaFieldWithOnChangeProps) { + const [field, meta] = useField(name || '') + + if (name) { + return ( + + ) + } else { + return ( + + ) + } +} + +export default FormikTextareaField diff --git a/src/forms/TextField.stories.tsx b/src/forms/TextField.stories.tsx index 0e79dd59..1cf4dbb4 100644 --- a/src/forms/TextField.stories.tsx +++ b/src/forms/TextField.stories.tsx @@ -11,7 +11,7 @@ export const Default = () => { { label="Label" labelType="small" tooltip="Tooltip for this input" - className={{ root: 'mb-1' }} + className={{ field: 'mb-1' }} placeholder="Placeholder" value={value} onChange={setValue} @@ -39,7 +39,7 @@ export const SmallLabel = () => { label="Search" labelType="small" tooltip="Tooltip for this input" - className={{ root: 'mb-1' }} + className={{ field: 'mb-1' }} placeholder="Placeholder" value={value} onChange={setValue} @@ -50,7 +50,7 @@ export const SmallLabel = () => { label="Label 3" labelType="small" tooltip="Tooltip for this input" - className={{ root: 'mb-1' }} + className={{ field: 'mb-1' }} placeholder="Placeholder" value={value} onChange={setValue} @@ -69,7 +69,7 @@ export const Disabled = () => { disabled label="Label" tooltip="Tooltip for this input" - className={{ root: 'mb-1' }} + className={{ field: 'mb-1' }} placeholder="Placeholder" value={value} onChange={setValue} @@ -91,7 +91,7 @@ export const Required = () => { required label="Label" tooltip="Tooltip for this input" - className={{ root: 'mb-1' }} + className={{ field: 'mb-1' }} placeholder="Placeholder" value={value} onChange={setValue} @@ -113,7 +113,7 @@ export const Styled = () => { label="Label" tooltip="Tooltip for this input" className={{ - root: 'mb-1 w-1/2', + field: 'mb-1 w-1/2', label: 'text-red-500', input: 'bg-blue-100', error: 'text-red-700', diff --git a/src/forms/TextField.tsx b/src/forms/TextField.tsx index 629910cd..4e85e9aa 100644 --- a/src/forms/TextField.tsx +++ b/src/forms/TextField.tsx @@ -23,8 +23,6 @@ interface TextFieldProps { isTouched?: boolean disabled?: boolean className?: { - override?: string - root?: string field?: string label?: string input?: string @@ -55,19 +53,21 @@ export interface TextFieldOnChangeProps extends TextFieldProps { * * @param id - The id of the input 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 input field. + * @param field - The field object from formik. + * @param value - The value of the input field (external state management). + * @param onChange - The onChange function of the input field (external state management). * @param label - The text displayed as label. * @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 placeholder text for the input field. * @param tooltip - The optional tooltip is shown on hover over the tooltip next to the label. * @param required - Indicate whether the field is required or not. - * @param hideError - Indicate whether the error message should be hidden or not. - * @param hasError - Indicate whether the field has an error or not (validation is not handled by this component). * @param isTouched - Indicate whether the field has been touched or not (validation is not handled by this component). + * @param hideError - Indicate whether the error message should be hidden or not. + * @param error - The error message that is shown below the field. * @param disabled - Indicate whether the field is disabled or not. * @param className - The optional className object allows you to override the default styling. * @param icon - The optional icon is shown on the right side of the input field. - * @param value - The value of the input field (external state management). - * @param onChange - The onChange function of the input field (external state management). * @returns Text field component with optional label, tooltip, error message and icon. */ @@ -132,7 +132,6 @@ export function TextField({ placeholder={placeholder} disabled={disabled} className={twMerge( - className?.override, 'focus:border-uzh-blue-50 h-9 w-full rounded border border-uzh-grey-60 pl-2 text-slate-600', icon && 'pl-8', disabled && 'cursor-not-allowed', @@ -152,7 +151,6 @@ export function TextField({ placeholder={placeholder} disabled={disabled} className={twMerge( - className?.override, 'focus:border-uzh-blue-50 h-9 w-full rounded border border-uzh-grey-60 pl-2 text-slate-600', icon && 'pl-8', disabled && 'cursor-not-allowed', diff --git a/src/forms/TextareaField.stories.tsx b/src/forms/TextareaField.stories.tsx new file mode 100644 index 00000000..641e918c --- /dev/null +++ b/src/forms/TextareaField.stories.tsx @@ -0,0 +1,180 @@ +import { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons' +import React from 'react' +import TextareaField from './TextareaField' + +export const Default = () => { + const [value, setValue] = React.useState('') + + return ( +
+
The default TextareaField
+ +
+ ) +} + +export const MaxLength = () => { + const [value, setValue] = React.useState('') + + return ( +
+
+ This TextareaField has an additional maximum length prop and shows a + corresponding message at the bottom. +
+ + + + +
+ ) +} + +export const SmallLabel = () => { + const [value, setValue] = React.useState('') + + return ( + <> +
The TextareaField with a small label
+ + + + + ) +} + +export const Disabled = () => { + const [value, setValue] = React.useState('') + + return ( + <> +
The TextareaField can be disabled
+ + + ) +} + +export const Required = () => { + const [value, setValue] = React.useState('') + + return ( + <> +
+ By adding a required attribute, the label of the field changes it + appearance +
+ + + ) +} + +export const Styled = () => { + const [value, setValue] = React.useState('') + + return ( + <> +
+ The default TextareaField can be customized and icons from FontAwesome + can be included at the beginning of the field +
+ + + ) +} diff --git a/src/forms/TextareaField.tsx b/src/forms/TextareaField.tsx new file mode 100644 index 00000000..5ab3ef99 --- /dev/null +++ b/src/forms/TextareaField.tsx @@ -0,0 +1,192 @@ +import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { FieldInputProps } from 'formik' +import React from 'react' +import { twMerge } from 'tailwind-merge' +import { Tooltip } from '../Tooltip' +import Label from './Label' + +interface TextareaFieldProps { + id?: string + data?: { + cy?: string + test?: string + } + label?: string + labelType?: 'small' | 'large' + placeholder?: string + tooltip?: string | React.ReactNode + maxLength?: number + maxLengthUnit?: string + hideMaxLength?: boolean + required?: boolean + hideError?: boolean + error?: string + isTouched?: boolean + disabled?: boolean + className?: { + root?: string + field?: string + label?: string + input?: string + error?: string + tooltip?: string + } +} + +export interface TextareaFieldNameProps extends TextareaFieldProps { + name: string + field: FieldInputProps + value?: never + onChange?: never + [key: string]: any +} + +export interface TextareaFieldOnChangeProps extends TextareaFieldProps { + name?: never + field?: never + value: string + onChange: (newValue: string) => void + [key: string]: any +} + +/** + * This function returns a text field component for use without formik + * + * @param id - The id of the input 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 input field. + * @param field - The field object from formik. + * @param value - The value of the input field (external state management). + * @param onChange - The onChange function of the input field (external state management). + * @param label - The text displayed as label. + * @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 placeholder text for the input field. + * @param tooltip - The optional tooltip is shown on hover over the tooltip next to the label. + * @param maxLength - The optional maxLength is used to limit the number of characters that can be entered in the field. + * @param maxLengthUnit - This optional label allows to specify a custom label for the maxLength indicator (e.g. "characters left" supporting internationalization). + * @param hideMaxLength - Indicate whether the maxLength indicator should be hidden or not. + * @param required - Indicate whether the field is required or not. + * @param isTouched - Indicate whether the field has been touched or not (validation is not handled by this component). + * @param hideError - Indicate whether the error message should be hidden or not. + * @param error - The error message that is shown below the field. + * @param disabled - Indicate whether the field is disabled or not. + * @param className - The optional className object allows you to override the default styling. + * @returns Text field component with optional label, tooltip, error message and icon. + */ + +export function TextareaField({ + id, + data, + name, + field, + value, + onChange, + label, + labelType = 'large', + placeholder, + tooltip, + maxLength, + maxLengthUnit, + hideMaxLength = false, + required, + isTouched, + hideError, + error, + disabled, + className, + ...props +}: TextareaFieldNameProps | TextareaFieldOnChangeProps) { + return ( +
+
+ {label && ( +