diff --git a/src/forms/NewFormikNumberField.stories.tsx b/src/forms/NewFormikNumberField.stories.tsx new file mode 100644 index 00000000..6729c17f --- /dev/null +++ b/src/forms/NewFormikNumberField.stories.tsx @@ -0,0 +1,356 @@ +import { Form, Formik } from 'formik' +import React from 'react' +import * as yup from 'yup' +import Button from '../Button' +import FormikNumberField from './NewFormikNumberField' + +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 MinMax = () => ( +
+
+ Specifying minimum and maximum values will enable additional validation + steps. In this case, values between 0 and 1000 will be accepted +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const Disabled = () => ( +
+
+ Number inputs can also be disabled with a corresponding prop, not allowing + the user to make changes to the field. +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) + +export const Validation = () => ( +
+
+ This formik field has validation functionalities included. The value is + required and should be at least 100. +
+ { + alert(`Form submitted with value: ${parseFloat(values.name || '')}`) + resetForm() + }} + validationSchema={yup.object().shape({ + name: yup.number().required('This field is required.').min(100), + })} + > + {({ values }) => { + return ( +
+
+ + + +
+ Value that will be submitted with parseFloat():{' '} + {parseFloat(values.name || '')} +
+
+ ) + }} +
+
+) + +export const Decimals = () => ( +
+
+ The default Formik field works with a "name" input and allows the user to + input decimal numbers as well as integers. The number of decimal places + can be specified through the precision prop (set to 2 for this example). +
+ { + alert(`Form submitted with value: ${parseFloat(values.name || '')}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
+ Value that will be submitted with parseFloat():{' '} + {parseFloat(values.name || '')} +
+
+ ) + }} +
+
+) + +export const Integer = () => ( +
+
+ By fixing the precision parameter to 0, the user can only input integers. +
+ { + alert(`Form submitted with value: ${parseFloat(values.name || '')}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
+ Value that will be submitted with parseFloat():{' '} + {parseFloat(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 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 SmallLabel = () => ( +
+
+ Formik text area component with a small label (designed e.g. for login + forms) +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ + + +
Value: {values.name}
+
+ ) + }} +
+
+) diff --git a/src/forms/NewFormikNumberField.tsx b/src/forms/NewFormikNumberField.tsx new file mode 100644 index 00000000..718d5a3f --- /dev/null +++ b/src/forms/NewFormikNumberField.tsx @@ -0,0 +1,149 @@ +import { useField } from 'formik' +import React from 'react' +import { twMerge } from 'tailwind-merge' +import NumberField, { NumberFieldClassName } from './NumberField' + +interface FormikNumberFieldProps { + id?: string + label?: string + labelType?: 'small' | 'large' + placeholder?: string + precision?: number + min?: number + max?: number + tooltip?: string | React.ReactNode + required?: boolean + hideError?: boolean + error?: string + disabled?: boolean + onBlur?: () => void + data?: { + cy?: string + test?: string + } + className?: NumberFieldClassName & { root?: string } + [key: string]: any +} + +export interface FormikNumberFieldNameProps extends FormikNumberFieldProps { + name: string + value?: never + onChange?: never + isTouched?: never +} + +export interface FormikNumberFieldOnChangeProps extends FormikNumberFieldProps { + name?: never + value: string + onChange: (newValue: string) => void + isTouched?: boolean +} + +/** + * This function returns a text field component for use without formik + * + * @param id - The id 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). + * @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 precision - The optional precision defines the number of decimal places that are allowed. + * @param min - The optional min defines the minimum value that is allowed. + * @param max - The optional max defines the maximum value that is allowed. + * @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 error - The error message that is displayed below the input field. + * @param isTouched - Indicate whether the field has been touched or not (validation is not handled by this component). + * @param disabled - Indicate whether the field is disabled or not. + * @param onBlur - The onBlur function 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 className - The optional className object allows you to override the default styling. + */ +export function FormikNumberField({ + id, + name, + value, + onChange, + label, + labelType, + placeholder, + precision, + min, + max, + tooltip, + required, + hideError, + error, + isTouched, + disabled, + onBlur, + data, + className, + ...props +}: FormikNumberFieldNameProps | FormikNumberFieldOnChangeProps) { + const [field, meta, helpers] = useField(name || '') + + if (name) { + return ( +
+ helpers.setValue(newValue)} + label={label} + labelType={labelType} + placeholder={placeholder} + precision={precision} + min={min} + max={max} + tooltip={tooltip} + required={required} + hideError={hideError} + error={meta.error} + isTouched={meta.touched} + disabled={disabled} + onBlur={() => { + helpers.setTouched(true) + onBlur?.() + }} + data={data} + className={className} + {...props} + /> +
+ ) + } + + return ( +
+ { + helpers.setTouched(true) + onBlur?.() + }} + data={data} + className={className} + {...props} + /> +
+ ) +} + +export default FormikNumberField diff --git a/src/forms/NewFormikTextField.tsx b/src/forms/NewFormikTextField.tsx index e5171904..cc22341c 100644 --- a/src/forms/NewFormikTextField.tsx +++ b/src/forms/NewFormikTextField.tsx @@ -31,6 +31,7 @@ export interface FormikTextFieldWithNameProps extends FormikTextFieldProps { value?: never onChange?: never error?: never + isTouched?: never [key: string]: any } export interface FormikTextFieldWithOnChangeProps extends FormikTextFieldProps { @@ -38,6 +39,7 @@ export interface FormikTextFieldWithOnChangeProps extends FormikTextFieldProps { value: string onChange: (newValue: string) => void error?: string + isTouched?: boolean [key: string]: any } @@ -79,6 +81,7 @@ export function FormikTextField({ tooltip, required = false, hideError = false, + isTouched = false, disabled = false, onPaste, className, @@ -88,7 +91,7 @@ export function FormikTextField({ if (name) { return ( -
+
+
) } + +export function SmallLabel() { + const [value, setValue] = useState('') + return ( +
+ setValue(newValue)} + label="Nunber Field" + labelType="small" + tooltip="This is a tooltip for the number field" + required + /> +
Value: {value}
+
+ ) +} + +export function Error() { + const [value, setValue] = useState('') + const [touched, setTouched] = useState(false) + + return ( +
+
+ As soon as the field has been touched, an error will be displayed. +
+ setValue(newValue)} + onBlur={() => setTouched(true)} + label="Nunber Field" + labelType="small" + tooltip="This is a tooltip for the number field" + error="This is an error message" + isTouched={touched} + required + /> +
Value: {value}
+
+ ) +} diff --git a/src/forms/NumberField.tsx b/src/forms/NumberField.tsx index bd3998ea..21198124 100644 --- a/src/forms/NumberField.tsx +++ b/src/forms/NumberField.tsx @@ -1,32 +1,40 @@ +import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import React from 'react' import { twMerge } from 'tailwind-merge' +import { Tooltip } from '../Tooltip' import Label from './Label' export interface NumberFieldClassName { - root?: string - input?: string + field?: string label?: string + input?: string + error?: string tooltip?: string } export interface NumberFieldProps { id?: string - data?: { - cy?: string - test?: string - } value: string | number onChange: (newValue: string) => void label?: string - tooltip?: string | React.ReactNode - required?: boolean - onBlur?: () => void + labelType?: 'small' | 'large' placeholder?: string - disabled?: boolean - className?: NumberFieldClassName precision?: number min?: number max?: number + tooltip?: string | React.ReactNode + required?: boolean + hideError?: boolean + error?: string + isTouched?: boolean + disabled?: boolean + onBlur?: () => void + data?: { + cy?: string + test?: string + } + className?: NumberFieldClassName [key: string]: any } @@ -34,38 +42,46 @@ export interface NumberFieldProps { * 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 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 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 onBlur - The onBlur function of the input field. + * @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 disabled - Indicate whether the field is disabled or not. * @param precision - The optional precision defines the number of decimal places that are allowed. * @param min - The optional min defines the minimum value that is allowed. * @param max - The optional max defines the maximum value that is allowed. + * @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 error - The error message that is displayed below the input field. + * @param isTouched - Indicate whether the field has been touched or not (validation is not handled by this component). + * @param disabled - Indicate whether the field is disabled or not. + * @param onBlur - The onBlur function 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 className - The optional className object allows you to override the default styling. */ - export function NumberField({ id, - data, value, onChange, label, - tooltip, - required, - onBlur, + labelType, placeholder, - disabled, precision, min, max, + tooltip, + required, + hideError, + error, + isTouched, + disabled, + onBlur, + data, className, + ...props }: NumberFieldProps): React.ReactElement { - const regex = + const validInput = typeof precision === 'number' && !isNaN(precision) ? precision === 0 ? /^[-]?\d*$/ @@ -73,51 +89,77 @@ export function NumberField({ : /^[-]?\d*\.?\d*$/ return ( -
+
{label && (
) } diff --git a/src/forms/TextField.tsx b/src/forms/TextField.tsx index 68b5a7e0..4c5d4011 100644 --- a/src/forms/TextField.tsx +++ b/src/forms/TextField.tsx @@ -100,7 +100,7 @@ export function TextField({
@@ -176,7 +176,7 @@ export function TextField({ /> )}
- {error && !hideError && ( + {error && !hideError && isTouched && (