Skip to content

Commit

Permalink
feat(Switch): introduce enhanced switch and new formik switch field c…
Browse files Browse the repository at this point in the history
…omponents (#102)

* enhance(Switch): add error and required symbols to Switch component

* feat(NewFormikSwitchField): add new formik switch field with enhanced error parsing

* chore: simplify classname structure on formik switch field
  • Loading branch information
sjschlapbach authored Jul 17, 2024
1 parent 48643bc commit 426b78d
Show file tree
Hide file tree
Showing 6 changed files with 449 additions and 11 deletions.
62 changes: 58 additions & 4 deletions src/Switch.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,55 @@ export const Default = () => {
return (
<Switch
checked={isChecked}
label="Unchecked"
label={isChecked ? 'Checked' : 'Unchecked'}
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
)
}

export const Required = () => {
const [isChecked, setIsChecked] = useState(false)
return (
<Switch
required
checked={isChecked}
label={isChecked ? 'Checked' : 'Unchecked'}
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
)
}

export const Tooltip = () => {
const [isChecked, setIsChecked] = useState(false)
return (
<>
<Switch
required
checked={isChecked}
label={isChecked ? 'Checked' : 'Unchecked'}
tooltip="This is a tooltip"
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
<Switch
required
checked={isChecked}
label={isChecked ? 'Checked' : 'Unchecked'}
tooltip="This is a tooltip"
error="Error message"
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
</>
)
}

export const Error = () => {
const [isChecked, setIsChecked] = useState(false)
return (
<Switch
required
error={isChecked ? undefined : 'This field is required'}
checked={isChecked}
label={isChecked ? 'Checked' : 'Unchecked'}
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
)
Expand All @@ -18,7 +66,13 @@ export const Undefined = () => {
return (
<Switch
checked={isChecked}
label="Unchecked"
label={
typeof isChecked === 'undefined'
? 'Undefined'
: isChecked
? 'Checked'
: 'Unchecked'
}
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
)
Expand All @@ -30,7 +84,7 @@ export const Disabled = () => {
<Switch
disabled
checked={isChecked}
label="Unchecked"
label={isChecked ? 'Checked' : 'Unchecked'}
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
)
Expand All @@ -42,7 +96,7 @@ export const DisabledActive = () => {
<Switch
disabled
checked={isChecked}
label="Unchecked"
label={isChecked ? 'Checked' : 'Unchecked'}
onCheckedChange={(newValue) => setIsChecked(newValue)}
/>
)
Expand Down
36 changes: 31 additions & 5 deletions src/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import * as RadixSwitch from '@radix-ui/react-switch'
import React from 'react'
import { twMerge } from 'tailwind-merge'
import { Tooltip } from './Tooltip'
import Label from './forms/Label'

export interface SwitchClassName {
override?: string
thumbOverride?: string
root?: string
element?: string
thumb?: string
Expand All @@ -23,7 +24,11 @@ export interface SwitchProps {
onBlur?: () => void
disabled?: boolean
label?: string
tooltip?: string | React.ReactNode
fluid?: boolean
error?: string
hideError?: boolean
required?: boolean
labelLeft?: boolean
size?: 'sm' | 'md' | 'lg'
className?: SwitchClassName
Expand All @@ -36,11 +41,15 @@ export interface SwitchProps {
* @param id - The id of the switch.
* @param data - The object of data attributes that can be used for testing (e.g. data-test or data-cy)
* @param label - The label that is displayed next to the switch.
* @param tooltip - The tooltip that is displayed when hovering over the label.
* @param checked - Indicator whether the switch is checked or not (or indefinite if undefined value). State is managed by the parent component.
* @param onCheckedChange - The function that is called when the switch is checked or unchecked. The new value is passed as a parameter.
* @param onBlur - The function that is called when the switch loses focus.
* @param disabled - Indicator whether the switch is disabled or not.
* @param fluid - Indicator whether the switch should be fluid or not.
* @param error - The error message that is shown next the switch.
* @param hideError - Indicator whether the error message should be hidden or not.
* @param required - Indicator whether the switch is required or not.
* @param labelLeft - Indicator whether the label should be displayed on the left or right side of the switch.
* @param size - The size of the switch. The size can be small, medium or large.
* @param className - The optional className object allows you to override the default styling.
Expand All @@ -51,10 +60,14 @@ export function Switch({
data,
disabled = false,
label,
tooltip,
checked,
onCheckedChange,
onBlur,
fluid = false,
error,
hideError = false,
required = false,
labelLeft = false,
size = 'md',
className,
Expand Down Expand Up @@ -97,11 +110,11 @@ export function Switch({
data-test={data?.test}
checked={checked}
className={twMerge(
className?.override,
'relative rounded-full border-0 bg-uzh-grey-80',
disabled && 'cursor-not-allowed bg-uzh-grey-40',
checked && 'bg-primary-60',
checked && disabled && 'bg-primary-20',
!!error && !hideError && 'outline outline-2 outline-red-600',
rootSize[size || 'md'],
className?.element
)}
Expand All @@ -111,7 +124,6 @@ export function Switch({
>
<RadixSwitch.Thumb
className={twMerge(
className?.thumbOverride,
'block rounded-full bg-white transition-transform',
typeof checked === 'undefined' &&
transitionSizeUndefined[size || 'md'],
Expand All @@ -122,7 +134,21 @@ export function Switch({
/>
</RadixSwitch.Root>
{!labelLeft && label && (
<Label className={{ root: className?.label }} label={label} />
<Label
className={{ root: twMerge('mr-2', className?.label) }}
label={label}
required={required}
tooltip={tooltip}
showTooltipSymbol={typeof tooltip !== 'undefined'}
/>
)}
{error && !hideError && (
<Tooltip tooltip={error} delay={0} className={{ tooltip: 'text-sm' }}>
<FontAwesomeIcon
icon={faCircleExclamation}
className="-mx-2 text-red-600"
/>
</Tooltip>
)}
</div>
)
Expand Down
Loading

0 comments on commit 426b78d

Please sign in to comment.