Skip to content

Commit

Permalink
enhance(*NumberField): add optional unit attribute to all number fiel…
Browse files Browse the repository at this point in the history
…d components
  • Loading branch information
sjschlapbach committed Jan 30, 2025
1 parent f619757 commit da1946e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 33 deletions.
38 changes: 38 additions & 0 deletions packages/design-system/src/forms/FormikNumberField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,44 @@ export const Default = () => (
</div>
)

export const Unit = () => (
<div>
<div>
The formik number field supports the same attributes as the standard
number field (including e.g. units)
</div>
<Formik
initialValues={{
name: '',
}}
isInitialValid={false}
onSubmit={async (values, { resetForm }) => {
alert(`Form submitted with value: ${values.name}`)
resetForm()
}}
>
{({ values }) => {
return (
<div>
<Form>
<FormikNumberField
name="name"
label="Label"
tooltip="Tooltip for this input"
unit="kg"
className={{ root: 'mb-1 w-80' }}
placeholder="Placeholder"
/>
<Button type="submit">Submit</Button>
</Form>
<div>Value: {values.name}</div>
</div>
)
}}
</Formik>
</div>
)

export const MinMax = () => (
<div>
<div>
Expand Down
5 changes: 5 additions & 0 deletions packages/design-system/src/forms/FormikNumberField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface FormikNumberFieldProps {
precision?: number
min?: number
max?: number
unit?: string
tooltip?: string | React.ReactNode
required?: boolean
hideError?: boolean
Expand Down Expand Up @@ -51,6 +52,7 @@ export interface FormikNumberFieldOnChangeProps extends FormikNumberFieldProps {
* @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 unit - The optional unit is shown next to 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.
Expand All @@ -72,6 +74,7 @@ export function FormikNumberField({
precision,
min,
max,
unit,
tooltip,
required,
hideError,
Expand All @@ -98,6 +101,7 @@ export function FormikNumberField({
precision={precision}
min={min}
max={max}
unit={unit}
tooltip={tooltip}
required={required}
hideError={hideError}
Expand Down Expand Up @@ -128,6 +132,7 @@ export function FormikNumberField({
precision={precision}
min={min}
max={max}
unit={unit}
tooltip={tooltip}
required={required}
hideError={hideError}
Expand Down
23 changes: 23 additions & 0 deletions packages/design-system/src/forms/NumberField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ export function Placeholder() {
)
}

export function Unit() {
const [value, setValue] = useState('')
return (
<div className="flex flex-col gap-3">
<NumberField
value={value}
onChange={(newValue) => setValue(newValue)}
className={{ field: 'w-80' }}
unit="kg"
/>
<NumberField
isTouched
value={value}
onChange={(newValue) => setValue(newValue)}
error="This is an error message"
className={{ field: 'w-80' }}
unit="kg"
/>
<div>Value: {value}</div>
</div>
)
}

export function Precision() {
const [value, setValue] = useState('')
return (
Expand Down
84 changes: 51 additions & 33 deletions packages/design-system/src/forms/NumberField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface NumberFieldClassName {
field?: string
label?: string
input?: string
unit?: string
error?: string
tooltip?: string
}
Expand All @@ -23,6 +24,7 @@ export interface NumberFieldProps {
precision?: number
min?: number
max?: number
unit?: string
tooltip?: string | React.ReactNode
required?: boolean
hideError?: boolean
Expand Down Expand Up @@ -50,6 +52,7 @@ export interface NumberFieldProps {
* @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 unit - The optional unit is shown next to 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.
Expand All @@ -70,6 +73,7 @@ export function NumberField({
precision,
min,
max,
unit,
tooltip,
required = false,
hideError,
Expand Down Expand Up @@ -108,40 +112,54 @@ export function NumberField({
)}

<div className="flex w-full flex-row items-center gap-2">
<input
id={id}
data-cy={data?.cy}
data-test={data?.test}
type="text"
value={value}
onChange={(e) => {
if (
e.target.value.match(validInput) !== null &&
(e.target.value === '' ||
typeof min === 'undefined' ||
parseFloat(e.target.value) >= min) &&
(e.target.value === '' ||
typeof max === 'undefined' ||
parseFloat(e.target.value) <= max)
) {
onChange(e.target.value)
} else {
console.log(
`input ${e.target.value} does not match regex ${validInput}`
)
}
}}
onBlur={onBlur}
placeholder={placeholder}
disabled={disabled}
className={twMerge(
'focus:border-uzh-blue-50 h-9 w-full rounded border border-uzh-grey-60 pl-2 placeholder-slate-400',
disabled && 'cursor-not-allowed',
!!error && isTouched && 'border-red-400 bg-red-50',
className?.input
<div className="flex w-full flex-row items-center">
<input
id={id}
data-cy={data?.cy}
data-test={data?.test}
type="text"
value={value}
onChange={(e) => {
if (
e.target.value.match(validInput) !== null &&
(e.target.value === '' ||
typeof min === 'undefined' ||
parseFloat(e.target.value) >= min) &&
(e.target.value === '' ||
typeof max === 'undefined' ||
parseFloat(e.target.value) <= max)
) {
onChange(e.target.value)
} else {
console.log(
`input ${e.target.value} does not match regex ${validInput}`
)
}
}}
onBlur={onBlur}
placeholder={placeholder}
disabled={disabled}
className={twMerge(
'focus:border-uzh-blue-50 h-9 w-full rounded border border-uzh-grey-60 pl-2 placeholder-slate-400',
disabled && 'cursor-not-allowed',
!!error && isTouched && 'border-red-400 bg-red-50',
!!unit && 'rounded-r-none',
className?.input
)}
{...props}
/>
{unit && (
<div
className={twMerge(
'flex h-9 min-w-max flex-col items-center justify-center rounded-r bg-slate-600 px-4 text-white',
className?.unit
)}
data-cy="input-numerical-unit"
>
{unit}
</div>
)}
{...props}
/>
</div>
{error && !hideError && isTouched && (
<Tooltip
tooltip={error}
Expand Down

0 comments on commit da1946e

Please sign in to comment.