Skip to content

Commit

Permalink
feat: add forwarded ref to all components
Browse files Browse the repository at this point in the history
  • Loading branch information
clementprevot committed Jul 19, 2023
1 parent f572046 commit 4309088
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 102 deletions.
35 changes: 22 additions & 13 deletions packages/fractal/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { button, typography } from '@snowball-tech/fractal-panda/recipes'
import isEmpty from 'lodash/fp/isEmpty'
import isFunction from 'lodash/fp/isFunction'
import omit from 'lodash/fp/omit'
import { type ForwardedRef, forwardRef } from 'react'

import { PREFIX } from '@/constants'

Expand All @@ -14,18 +15,21 @@ import type { ButtonProps } from './Button.types'
/**
* `Button` component is used to allow a user to make an interaction.
*/
export default function Button({
disabled = false,
fullWidth = false,
icon,
iconPosition = 'right',
label,
onClick,
onLongClick,
type = 'button',
variant = DEFAULT_VARIANT,
...props
}: ButtonProps) {
function Button(
{
disabled = false,
fullWidth = false,
icon,
iconPosition = 'right',
label,
onClick,
onLongClick,
type = 'button',
variant = DEFAULT_VARIANT,
...props
}: ButtonProps,
ref: ForwardedRef<HTMLButtonElement>,
) {
const buttonClassNames = cx(
`${PREFIX}-${GROUP_NAME}`,
button({ variant }),
Expand All @@ -37,15 +41,18 @@ export default function Button({

return (
<Pressable
{...(props.id !== undefined ? { id: props.id } : {})}
ref={ref}
className={buttonClassNames}
{...(props.dir !== undefined ? { dir: props.dir } : {})}
disabled={disabled}
preventFocusOnPress
type={type}
{...(isFunction(onClick) ? { onPress: (event) => onClick(event) } : {})}
{...(isFunction(onLongClick)
? { onLongPress: (event) => onLongClick(event) }
: {})}
{...omit(['className', 'dir'], props)}
{...omit(['className', 'dir', 'id'], props)}
>
{icon && iconPosition === 'left' && icon}

Expand All @@ -55,3 +62,5 @@ export default function Button({
</Pressable>
)
}

export default forwardRef(Button)
37 changes: 22 additions & 15 deletions packages/fractal/src/components/InputCheckbox/InputCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import isFunction from 'lodash/fp/isFunction'
import omit from 'lodash/fp/omit'
import uniqueId from 'lodash/fp/uniqueId'
import { type ForwardedRef, forwardRef } from 'react'

import { PREFIX } from '@/constants'

Expand All @@ -22,21 +23,24 @@ import type { InputCheckboxProps } from './InputCheckbox.types'
/**
* `Checkbox` component is used to allow a user to make a binary choice.
*/
export default function InputCheckbox({
checked,
color = DEFAULT_COLOR,
defaultChecked,
disabled = false,
fullWidth = false,
id = uniqueId('fractal-input-checkbox-'),
label,
name,
onCheckedChange,
required = false,
value = 'on',
variant = DEFAULT_VARIANT,
...props
}: InputCheckboxProps) {
function InputCheckbox(
{
checked,
color = DEFAULT_COLOR,
defaultChecked,
disabled = false,
fullWidth = false,
id = uniqueId('fractal-input-checkbox-'),
label,
name,
onCheckedChange,
required = false,
value = 'on',
variant = DEFAULT_VARIANT,
...props
}: InputCheckboxProps,
ref: ForwardedRef<HTMLButtonElement>,
) {
const groupClassNames = cx(
`${PREFIX}-${GROUP_NAME}`,
inputCheckboxContainer({ color, variant }),
Expand All @@ -50,6 +54,7 @@ export default function InputCheckbox({
<div className={groupClassNames}>
<RxCheckbox.Root
id={id}
ref={ref}
{...(checked !== undefined ? { checked } : {})}
className={inputCheckbox({ color, variant })}
{...(defaultChecked !== undefined ? { defaultChecked } : {})}
Expand Down Expand Up @@ -81,3 +86,5 @@ export default function InputCheckbox({
</div>
)
}

export default forwardRef(InputCheckbox)
76 changes: 56 additions & 20 deletions packages/fractal/src/components/InputDate/InputDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import isEmpty from 'lodash/fp/isEmpty'
import isFunction from 'lodash/fp/isFunction'
import isNumber from 'lodash/fp/isNumber'
import uniqueId from 'lodash/fp/uniqueId'
import { useState } from 'react'
import {
type ForwardedRef,
forwardRef,
useImperativeHandle,
useRef,
useState,
} from 'react'

import { InputText } from '@/components/InputText'
import { PREFIX } from '@/constants'
Expand Down Expand Up @@ -49,25 +55,50 @@ function isValid(type: keyof DateFormat, value: number) {
* `InputDate` component is used to allow the user to enter a date using 3
* separate day/month/year fields.
*/
export default function InputDate({
autoFocus = false,
defaultValue,
descriptions,
disabled = false,
error,
id = uniqueId('fractal-input-date-'),
label,
maxYear = 2099,
name,
onChange,
onFieldChange,
placeholders,
readOnly = false,
required = false,
success,
value,
...props
}: InputDateProps) {
function InputDate(
{
autoFocus = false,
defaultValue,
descriptions,
disabled = false,
error,
id = uniqueId('fractal-input-date-'),
label,
maxYear = 2099,
name,
onChange,
onFieldChange,
placeholders,
readOnly = false,
required = false,
success,
value,
...props
}: InputDateProps,
ref: ForwardedRef<{
day: HTMLInputElement | null
month: HTMLInputElement | null
year: HTMLInputElement | null
}>,
) {
const dayRef = useRef<HTMLInputElement | null>(null)
const monthRef = useRef<HTMLInputElement | null>(null)
const yearRef = useRef<HTMLInputElement | null>(null)

useImperativeHandle(ref, () => ({
get day() {
return dayRef.current
},

get month() {
return monthRef.current
},

get year() {
return yearRef.current
},
}))

const [errors, setErrors] = useState<{
day: boolean
month: boolean
Expand Down Expand Up @@ -128,6 +159,7 @@ export default function InputDate({
<div className={cx('fields', inputDateFields())}>
<InputText
id={`${id}-day`}
ref={dayRef}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus={autoFocus}
className={cx(inputDateField(), inputDateDay())}
Expand All @@ -153,6 +185,7 @@ export default function InputDate({

<InputText
id={`${id}-month`}
ref={monthRef}
// eslint-disable-next-line jsx-a11y/no-autofocus
className={cx(inputDateField(), inputDateMonth())}
{...(defaultValue?.month !== undefined
Expand All @@ -177,6 +210,7 @@ export default function InputDate({

<InputText
id={`${id}-year`}
ref={yearRef}
// eslint-disable-next-line jsx-a11y/no-autofocus
className={cx(inputDateField(), inputDateYear())}
{...(defaultValue?.year !== undefined
Expand Down Expand Up @@ -215,3 +249,5 @@ export default function InputDate({
</div>
)
}

export default forwardRef(InputDate)
73 changes: 52 additions & 21 deletions packages/fractal/src/components/InputPhone/InputPhone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ import examples from 'libphonenumber-js/mobile/examples'
import isEmpty from 'lodash/fp/isEmpty'
import isFunction from 'lodash/fp/isFunction'
import uniqueId from 'lodash/fp/uniqueId'
import { useCallback, useEffect, useRef, useState } from 'react'
import {
type ForwardedRef,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react'

import { InputText } from '@/components/InputText'
import { PREFIX } from '@/constants'
Expand All @@ -47,26 +55,45 @@ import type {
* `InputPhone` component is used to allow the user to enter a phone number
* (with or without a prefix).
*/
export default function InputPhone({
autoFocus = false,
defaultValue,
description,
disabled = false,
error,
id = uniqueId('fractal-input-phone-'),
label,
name,
onChange,
placeholder,
readOnly = false,
required = false,
searchPlaceholder,
success,
updateOnInvalid = true,
value,
withPrefix = true,
...props
}: InputPhoneProps) {
function InputPhone(
{
autoFocus = false,
defaultValue,
description,
disabled = false,
error,
id = uniqueId('fractal-input-phone-'),
label,
name,
onChange,
placeholder,
readOnly = false,
required = false,
searchPlaceholder,
success,
updateOnInvalid = true,
value,
withPrefix = true,
...props
}: InputPhoneProps,
ref: ForwardedRef<{
phone: HTMLInputElement | null
prefix: HTMLButtonElement | null
}>,
) {
const phoneRef = useRef<HTMLInputElement>(null)
const prefixRef = useRef<HTMLButtonElement>(null)

useImperativeHandle(ref, () => ({
get phone() {
return phoneRef.current
},

get prefix() {
return prefixRef.current
},
}))

const searchInput = useRef<HTMLInputElement>(null)
const [keepFocus, setKeepFocus] = useState(false)

Expand Down Expand Up @@ -268,6 +295,7 @@ export default function InputPhone({
{withPrefix ? (
<Select
id={`${id}-prefix`}
ref={prefixRef}
className={inputPhonePrefix()}
disabled={disabled}
displayedValue={
Expand Down Expand Up @@ -326,6 +354,7 @@ export default function InputPhone({

<InputText
id={`${id}-number`}
ref={phoneRef}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus={autoFocus && !withPrefix}
className={cx(
Expand Down Expand Up @@ -388,3 +417,5 @@ export default function InputPhone({
</div>
)
}

export default forwardRef(InputPhone)
24 changes: 15 additions & 9 deletions packages/fractal/src/components/InputRadio/InputRadio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '@snowball-tech/fractal-panda/recipes'
import omit from 'lodash/fp/omit'
import uniqueId from 'lodash/fp/uniqueId'
import { useContext } from 'react'
import { type ForwardedRef, forwardRef, useContext } from 'react'

import { PREFIX } from '@/constants'

Expand All @@ -25,14 +25,17 @@ import { InputRadioVariantContext } from './InputRadioVariantContext'
*
* You must use this component with the `InputRadioGroup` component.
*/
export default function InputRadio({
disabled = false,
fullWidth = false,
id = uniqueId('fractal-input-checkbox-'),
label,
value,
...props
}: InputRadioProps) {
function InputRadio(
{
disabled = false,
fullWidth = false,
id = uniqueId('fractal-input-checkbox-'),
label,
value,
...props
}: InputRadioProps,
ref: ForwardedRef<HTMLButtonElement>,
) {
const variant = useContext(InputRadioVariantContext)

const groupClassNames = cx(
Expand All @@ -48,6 +51,7 @@ export default function InputRadio({
<div className={groupClassNames}>
<RxRadio.Item
id={id}
ref={ref}
className={inputRadio()}
disabled={disabled}
value={value}
Expand All @@ -67,3 +71,5 @@ export default function InputRadio({
</div>
)
}

export default forwardRef(InputRadio)
Loading

0 comments on commit 4309088

Please sign in to comment.