From fd9d457ed4e83f5e576c4acf8b3b796ec38be019 Mon Sep 17 00:00:00 2001 From: sjschlapbach Date: Mon, 15 Jul 2024 12:28:43 +0200 Subject: [PATCH 001/115] fix(forms/FormikNumberField): add missing example for number field validation --- src/forms/FormikNumberField.stories.tsx | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/forms/FormikNumberField.stories.tsx b/src/forms/FormikNumberField.stories.tsx index 9e3f9b64..6263f707 100644 --- a/src/forms/FormikNumberField.stories.tsx +++ b/src/forms/FormikNumberField.stories.tsx @@ -38,6 +38,45 @@ export const Default = () => ( ) +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 = () => (
From 714742f1e99a5be4cc6fb685ea79f9cb16897227 Mon Sep 17 00:00:00 2001 From: sjschlapbach Date: Mon, 15 Jul 2024 12:42:53 +0200 Subject: [PATCH 002/115] fix(Switch): pass disabled prop correctly to radix switch component --- src/Switch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Switch.tsx b/src/Switch.tsx index 6e66ddad..b5316b33 100644 --- a/src/Switch.tsx +++ b/src/Switch.tsx @@ -107,6 +107,7 @@ export function Switch({ )} onCheckedChange={!disabled ? onCheckedChange : () => null} onClick={() => (onBlur ? onBlur() : null)} + disabled={disabled} > Date: Mon, 15 Jul 2024 12:44:48 +0200 Subject: [PATCH 003/115] chore(main): release 2.7.1 (#96) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61098207..fe1192e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.7.1](https://github.com/uzh-bf/design-system/compare/v2.7.0...v2.7.1) (2024-07-15) + + +### Bug Fixes + +* **forms/FormikNumberField:** add missing example for number field validation ([fd9d457](https://github.com/uzh-bf/design-system/commit/fd9d457ed4e83f5e576c4acf8b3b796ec38be019)) +* **Switch:** pass disabled prop correctly to radix switch component ([714742f](https://github.com/uzh-bf/design-system/commit/714742f1e99a5be4cc6fb685ea79f9cb16897227)) + ## [2.7.0](https://github.com/uzh-bf/design-system/compare/v2.6.0...v2.7.0) (2024-05-16) diff --git a/package-lock.json b/package-lock.json index a0aef7a2..976ad447 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@uzh-bf/design-system", - "version": "2.7.0", + "version": "2.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@uzh-bf/design-system", - "version": "2.7.0", + "version": "2.7.1", "license": "MIT", "dependencies": { "@radix-ui/react-checkbox": "1.0.4", diff --git a/package.json b/package.json index 68927fb3..4c056e3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@uzh-bf/design-system", - "version": "2.7.0", + "version": "2.7.1", "license": "MIT", "repository": { "url": "https://github.com/uzh-bf/design-system.git" From e9573dcacc51f9319d7d13d7d85cba90e9e46330 Mon Sep 17 00:00:00 2001 From: sjschlapbach Date: Mon, 15 Jul 2024 13:46:35 +0200 Subject: [PATCH 004/115] feat: ensure that hover styles are not applied on mobile devices --- src/Dropdown.tsx | 8 ++++---- src/Navigation.tsx | 6 +++--- src/Prose.tsx | 2 +- src/Select.tsx | 4 ++-- tailwind.config.js | 3 +++ 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Dropdown.tsx b/src/Dropdown.tsx index 7c871369..4aede9ed 100644 --- a/src/Dropdown.tsx +++ b/src/Dropdown.tsx @@ -81,9 +81,9 @@ export function Dropdown({ data-test={data?.test} className={twMerge( 'inline-flex h-7 items-center justify-between gap-3 rounded-md border', - 'bg-white py-1.5 pl-2 pr-2 shadow-sm hover:bg-primary-20 sm:hover:text-primary', + 'bg-white py-1.5 pl-2 pr-2 shadow-sm hover:bg-primary-20 hover:text-primary', disabled && - 'hover:bg-none, sm:hover:text-none cursor-not-allowed bg-uzh-grey-20 opacity-70 shadow-sm', + 'hover:bg-none, hover:text-none cursor-not-allowed bg-uzh-grey-20 opacity-70 shadow-sm', className?.trigger )} disabled={disabled} @@ -110,7 +110,7 @@ export function Dropdown({ @@ -203,7 +203,7 @@ const DropdownItem = ({ data-test={data?.test} className={twMerge( className?.override, - `flex flex-row rounded px-2 py-0.5 hover:cursor-pointer hover:bg-primary-20 sm:hover:text-primary`, + `flex flex-row rounded px-2 py-0.5 hover:cursor-pointer hover:bg-primary-20 hover:text-primary`, active && twMerge('font-bold', className?.active), className?.root )} diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 37681045..ecf9800d 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -260,7 +260,7 @@ Navigation.DropdownItem = function DropdownItem({ className={twMerge( className?.override, 'w-full rounded-md px-4 py-3 text-black hover:bg-primary-60', - 'focus:outline-none focus-visible:ring focus-visible:ring-opacity-75 sm:hover:text-white', + 'hover:text-white focus:outline-none focus-visible:ring focus-visible:ring-opacity-75', className?.root )} style={style?.root} @@ -367,7 +367,7 @@ Navigation.ButtonItem = function ButtonItem({ onClick={!disabled ? onClick : undefined} className={twMerge( className?.override, - 'cursor-pointer rounded-md px-3 py-2 text-sm font-medium text-black sm:hover:text-white', + 'cursor-pointer rounded-md px-3 py-2 text-sm font-medium text-black hover:text-white', !disabled && 'hover:bg-primary-60', disabled && 'hover:text-none cursor-not-allowed text-gray-400', className?.root, @@ -455,7 +455,7 @@ Navigation.IconItem = function IconItem({ onClick={!disabled ? onClick : undefined} className={twMerge( className?.override, - 'flex h-9 w-9 items-center justify-center rounded-md text-black sm:hover:text-white', + 'flex h-9 w-9 items-center justify-center rounded-md text-black hover:text-white', !disabled && 'hover:bg-primary-60', disabled && 'hover:text-none cursor-not-allowed text-gray-400', className?.root, diff --git a/src/Prose.tsx b/src/Prose.tsx index b9a8b775..603bc8cd 100644 --- a/src/Prose.tsx +++ b/src/Prose.tsx @@ -29,7 +29,7 @@ export function Prose({ id, data, className, children }: ProseProps) { data-cy={data?.cy} data-test={data?.test} className={twMerge( - 'prose-h4:text-md prose prose-headings:font-sans prose-headings:font-bold prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg sm:hover:prose-a:text-primary', + 'prose-h4:text-md prose prose-headings:font-sans prose-headings:font-bold prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg hover:prose-a:text-primary', className?.root )} > diff --git a/src/Select.tsx b/src/Select.tsx index 3dc81396..5ab63d31 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -188,7 +188,7 @@ export function Select({ className?.triggerOverride, 'rounded-md px-2 py-1', !basic && - 'inline-flex h-7 items-center justify-between gap-2 border bg-white p-4 shadow-sm hover:bg-primary-20 sm:hover:text-primary', + 'inline-flex h-7 items-center justify-between gap-2 border bg-white p-4 shadow-sm hover:bg-primary-20 hover:text-primary', disabled && 'hover:bg-none, hover:text-none cursor-not-allowed bg-uzh-grey-20 opacity-70 shadow-sm', size === 'sm' && '!text-sm', @@ -261,7 +261,7 @@ const SelectItem = React.forwardRef( className={twMerge( className?.itemOverride, 'relative flex select-none items-center rounded-md px-8 py-2 font-medium text-gray-700', - 'hover:cursor-pointer hover:bg-primary-20 hover:outline-none focus:border-primary-40 sm:hover:text-primary', + 'hover:cursor-pointer hover:bg-primary-20 hover:text-primary hover:outline-none focus:border-primary-40', disabled && 'cursor-not-allowed opacity-50 hover:bg-white hover:text-gray-700', size === 'sm' && 'px-7 text-sm', diff --git a/tailwind.config.js b/tailwind.config.js index 496238a4..2dc64fb6 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,6 +6,9 @@ const { module.exports = { content: ['src/**/**.tsx'], + future: { + hoverOnlyWhenSupported: true, + }, theme: { extend: { ...TailwindAnimations, From f7cb3acdb0562826775ee4f4821b01bf2c3f5f9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:50:10 +0200 Subject: [PATCH 005/115] chore(main): release 2.8.0 (#97) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe1192e5..89b7a8e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.8.0](https://github.com/uzh-bf/design-system/compare/v2.7.1...v2.8.0) (2024-07-15) + + +### Features + +* ensure that hover styles are not applied on mobile devices ([e9573dc](https://github.com/uzh-bf/design-system/commit/e9573dcacc51f9319d7d13d7d85cba90e9e46330)) + ## [2.7.1](https://github.com/uzh-bf/design-system/compare/v2.7.0...v2.7.1) (2024-07-15) diff --git a/package-lock.json b/package-lock.json index 976ad447..0a00b306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@uzh-bf/design-system", - "version": "2.7.1", + "version": "2.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@uzh-bf/design-system", - "version": "2.7.1", + "version": "2.8.0", "license": "MIT", "dependencies": { "@radix-ui/react-checkbox": "1.0.4", diff --git a/package.json b/package.json index 4c056e3e..34e1a1d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@uzh-bf/design-system", - "version": "2.7.1", + "version": "2.8.0", "license": "MIT", "repository": { "url": "https://github.com/uzh-bf/design-system.git" From 6ab8a6dbec8b2734ac85eebce45da8e76bd68f78 Mon Sep 17 00:00:00 2001 From: sjschlapbach Date: Tue, 16 Jul 2024 18:53:23 +0200 Subject: [PATCH 006/115] fix(Tooltip): ensure that tooltip also accepts zero delay --- src/Tooltip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx index d79ec21d..c1e6bac0 100644 --- a/src/Tooltip.tsx +++ b/src/Tooltip.tsx @@ -53,7 +53,7 @@ export function Tooltip({ }: TooltipProps): React.ReactElement { return ( - + Date: Wed, 17 Jul 2024 10:39:01 +0200 Subject: [PATCH 007/115] chore(main): release 2.8.1 (#98) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b7a8e5..36c8a47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.8.1](https://github.com/uzh-bf/design-system/compare/v2.8.0...v2.8.1) (2024-07-16) + + +### Bug Fixes + +* **Tooltip:** ensure that tooltip also accepts zero delay ([6ab8a6d](https://github.com/uzh-bf/design-system/commit/6ab8a6dbec8b2734ac85eebce45da8e76bd68f78)) + ## [2.8.0](https://github.com/uzh-bf/design-system/compare/v2.7.1...v2.8.0) (2024-07-15) diff --git a/package-lock.json b/package-lock.json index 0a00b306..a40ea135 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@uzh-bf/design-system", - "version": "2.8.0", + "version": "2.8.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@uzh-bf/design-system", - "version": "2.8.0", + "version": "2.8.1", "license": "MIT", "dependencies": { "@radix-ui/react-checkbox": "1.0.4", diff --git a/package.json b/package.json index 34e1a1d0..82e937d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@uzh-bf/design-system", - "version": "2.8.0", + "version": "2.8.1", "license": "MIT", "repository": { "url": "https://github.com/uzh-bf/design-system.git" From 56f95a4e525b1c014adbddc193c18ae60016b242 Mon Sep 17 00:00:00 2001 From: Julius Schlapbach <80708107+sjschlapbach@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:43:08 +0200 Subject: [PATCH 008/115] feat(forms/NewFormikTextField): add simplified formik text field with enhanced error illustration (#99) --- src/forms/NewFormikTextField.stories.tsx | 355 +++++++++++++++++++++++ src/forms/NewFormikTextField.tsx | 145 +++++++++ src/forms/TextField.stories.tsx | 49 +++- src/forms/TextField.tsx | 129 ++++++-- src/index.ts | 1 + 5 files changed, 643 insertions(+), 36 deletions(-) create mode 100644 src/forms/NewFormikTextField.stories.tsx create mode 100644 src/forms/NewFormikTextField.tsx diff --git a/src/forms/NewFormikTextField.stories.tsx b/src/forms/NewFormikTextField.stories.tsx new file mode 100644 index 00000000..305d614a --- /dev/null +++ b/src/forms/NewFormikTextField.stories.tsx @@ -0,0 +1,355 @@ +import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons' +import { Form, Formik } from 'formik' +import React, { useState } from 'react' +import * as yup from 'yup' +import Button from '../Button' +import FormikTextField from './NewFormikTextField' + +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 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.replace(/\s/g, '')) + }} + 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}
+
+ ) + }} +
+
+) + +export const Icon = () => { + const [textHidden, setTextHidden] = useState(true) + + return ( +
+
+ The icon prop allows to pass a fontawesom icon to the component, which + will then be displayed on the right side of the input field +
+ { + alert(`Form submitted with value: ${values.name}`) + resetForm() + }} + > + {({ values }) => { + return ( +
+
+ setTextHidden(!textHidden)} + type={textHidden ? 'password' : 'text'} + /> + + +
Value: {values.name}
+
+ ) + }} +
+
+ ) +} diff --git a/src/forms/NewFormikTextField.tsx b/src/forms/NewFormikTextField.tsx new file mode 100644 index 00000000..05f0411a --- /dev/null +++ b/src/forms/NewFormikTextField.tsx @@ -0,0 +1,145 @@ +import { IconDefinition } from '@fortawesome/free-solid-svg-icons' +import { useField } from 'formik' +import React from 'react' +import { twMerge } from 'tailwind-merge' +import TextField from './TextField' + +interface FormikTextFieldProps { + id?: string + data?: { + cy?: string + test?: string + } + label?: string + labelType?: 'small' | 'large' + icon?: IconDefinition + onIconClick?: () => void + placeholder?: string + tooltip?: string | React.ReactNode + 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 FormikTextFieldWithNameProps extends FormikTextFieldProps { + name: string + value?: never + onChange?: never + error?: never + [key: string]: any +} +export interface FormikTextFieldWithOnChangeProps extends FormikTextFieldProps { + 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 icon - An optional icon (FontAwesomeIcon IconDefinition) that is shown on the right side of the text input component + * @param onIconClick - An optional function that is called when the icon (previous prop) is clicked + * @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 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 FormikTextField({ + id, + data, + name, + value, + onChange, + error, + label, + labelType = 'small', + icon, + onIconClick, + placeholder, + tooltip, + required = false, + hideError = false, + disabled = false, + className, + ...props +}: FormikTextFieldWithNameProps | FormikTextFieldWithOnChangeProps) { + const [field, meta] = useField(name || '') + + if (name) { + return ( +
+ +
+ ) + } else { + return ( +
+ +
+ ) + } +} + +export default FormikTextField diff --git a/src/forms/TextField.stories.tsx b/src/forms/TextField.stories.tsx index f1a8d1ad..0e79dd59 100644 --- a/src/forms/TextField.stories.tsx +++ b/src/forms/TextField.stories.tsx @@ -7,9 +7,48 @@ export const Default = () => { return ( <> -
The default TextField works with a name input
+
The default TextField
+ + ) +} + +export const SmallLabel = () => { + const [value, setValue] = React.useState('') + + return ( + <> +
The TextField with a small label
+ + + {
The TextField can be disabled
{
{ be included at the beginning of the field
+ value?: never + onChange?: never + [key: string]: any +} + +export interface TextFieldOnChangeProps extends TextFieldProps { + name?: never + field?: never value: string onChange: (newValue: string) => void + [key: string]: any } /** @@ -38,6 +56,7 @@ export interface 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 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. @@ -55,62 +74,110 @@ export interface TextFieldProps { export function TextField({ id, data, + name, + field, value, onChange, label, + labelType = 'large', placeholder, tooltip, required, - hasError, isTouched, hideError, + error, disabled, className, icon, ...props -}: TextFieldProps) { +}: TextFieldNameProps | TextFieldOnChangeProps) { return ( -
+
{label && (