Skip to content

Commit

Permalink
feat(ui): split up Select component into Select and SelectInput (#6471)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlessioGr committed May 23, 2024
1 parent 72c0534 commit 661a4a0
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 78 deletions.
4 changes: 2 additions & 2 deletions packages/ui/src/fields/Checkbox/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { Check } from '../../icons/Check/index.js'
import { Line } from '../../icons/Line/index.js'

type Props = {
export type CheckboxInputProps = {
AfterInput?: React.ReactNode
BeforeInput?: React.ReactNode
CustomLabel?: React.ReactNode
Expand All @@ -26,7 +26,7 @@ type Props = {

export const inputBaseClass = 'checkbox-input'

export const CheckboxInput: React.FC<Props> = ({
export const CheckboxInput: React.FC<CheckboxInputProps> = ({
id,
name,
AfterInput,
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/fields/Checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ClientValidate } from 'payload/types'

import React, { useCallback } from 'react'

import type { CheckboxInputProps } from './Input.js'
import type { CheckboxFieldProps } from './types.js'

import { FieldDescription } from '../../forms/FieldDescription/index.js'
Expand All @@ -19,7 +20,7 @@ import './index.scss'

const baseClass = 'checkbox'

export { CheckboxFieldProps, CheckboxInput }
export { CheckboxFieldProps, CheckboxInput, type CheckboxInputProps }

const CheckboxField: React.FC<CheckboxFieldProps> = (props) => {
const {
Expand Down
131 changes: 131 additions & 0 deletions packages/ui/src/fields/Select/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
'use client'
import type { OptionObject } from 'payload/types'

import { getTranslation } from '@payloadcms/translations'
import React from 'react'

import type { SelectFieldProps } from './index.js'

import { ReactSelect } from '../../elements/ReactSelect/index.js'
import { FieldDescription } from '../../forms/FieldDescription/index.js'
import { FieldError } from '../../forms/FieldError/index.js'
import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { fieldBaseClass } from '../shared/index.js'
import './index.scss'

export type SelectInputProps = Omit<
SelectFieldProps,
| 'custom'
| 'disabled'
| 'docPreferences'
| 'locale'
| 'localized'
| 'options'
| 'rtl'
| 'type'
| 'user'
| 'validate'
| 'value'
> & {
options?: OptionObject[]
showError?: boolean
value?: string | string[]
}

export const SelectInput: React.FC<SelectInputProps> = (props) => {
const {
AfterInput,
BeforeInput,
CustomDescription,
CustomError,
CustomLabel,
className,
descriptionProps,
errorProps,
hasMany = false,
isClearable = true,
isSortable = true,
label,
labelProps,
onChange,
options,
path,
readOnly,
required,
showError,
style,
value,
width,
} = props

const { i18n } = useTranslation()

let valueToRender

if (hasMany && Array.isArray(value)) {
valueToRender = value.map((val) => {
const matchingOption = options.find((option) => option.value === val)
return {
label: matchingOption ? getTranslation(matchingOption.label, i18n) : val,
value: matchingOption?.value ?? val,
}
})
} else if (value) {
const matchingOption = options.find((option) => option.value === value)
valueToRender = {
label: matchingOption ? getTranslation(matchingOption.label, i18n) : value,
value: matchingOption?.value ?? value,
}
}

return (
<div
className={[
fieldBaseClass,
'select',
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
style={{
...style,
width,
}}
>
<FieldLabel
CustomLabel={CustomLabel}
label={label}
required={required}
{...(labelProps || {})}
/>
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
<div className={`${fieldBaseClass}__wrap`}>
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
{BeforeInput}
<ReactSelect
disabled={readOnly}
isClearable={isClearable}
isMulti={hasMany}
isSortable={isSortable}
onChange={onChange}
options={options.map((option) => ({
...option,
label: getTranslation(option.label, i18n),
}))}
showError={showError}
value={valueToRender as OptionObject}
/>
{AfterInput}
</div>
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}
104 changes: 29 additions & 75 deletions packages/ui/src/fields/Select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,15 @@
'use client'
import type { ClientValidate, Option, OptionObject } from 'payload/types'

import { getTranslation } from '@payloadcms/translations'
import React, { useCallback, useState } from 'react'

import type { FormFieldBase } from '../shared/index.js'
import type { SelectInputProps } from './Input.js'

import { ReactSelect } from '../../elements/ReactSelect/index.js'
import { FieldDescription } from '../../forms/FieldDescription/index.js'
import { FieldError } from '../../forms/FieldError/index.js'
import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { fieldBaseClass } from '../shared/index.js'
import './index.scss'
import { SelectInput } from './Input.js'

export type SelectFieldProps = FormFieldBase & {
hasMany?: boolean
Expand Down Expand Up @@ -68,8 +62,6 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
width,
} = props

const { i18n } = useTranslation()

const [options] = useState(formatOptions(optionsFromProps))

const memoizedValidate: ClientValidate = useCallback(
Expand All @@ -88,24 +80,6 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
validate: memoizedValidate,
})

let valueToRender

if (hasMany && Array.isArray(value)) {
valueToRender = value.map((val) => {
const matchingOption = options.find((option) => option.value === val)
return {
label: matchingOption ? getTranslation(matchingOption.label, i18n) : val,
value: matchingOption?.value ?? val,
}
})
} else if (value) {
const matchingOption = options.find((option) => option.value === value)
valueToRender = {
label: matchingOption ? getTranslation(matchingOption.label, i18n) : value,
value: matchingOption?.value ?? value,
}
}

const onChange = useCallback(
(selectedOption) => {
if (!readOnly) {
Expand Down Expand Up @@ -133,54 +107,34 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
)

return (
<div
className={[
fieldBaseClass,
'select',
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
style={{
...style,
width,
}}
>
<FieldLabel
CustomLabel={CustomLabel}
label={label}
required={required}
{...(labelProps || {})}
/>
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
<div className={`${fieldBaseClass}__wrap`}>
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
{BeforeInput}
<ReactSelect
disabled={readOnly}
isClearable={isClearable}
isMulti={hasMany}
isSortable={isSortable}
onChange={onChange}
options={options.map((option) => ({
...option,
label: getTranslation(option.label, i18n),
}))}
showError={showError}
value={valueToRender as OptionObject}
/>
{AfterInput}
</div>
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
<SelectInput
AfterInput={AfterInput}
BeforeInput={BeforeInput}
CustomDescription={CustomDescription}
CustomError={CustomError}
CustomLabel={CustomLabel}
className={className}
descriptionProps={descriptionProps}
errorProps={errorProps}
hasMany={hasMany}
isClearable={isClearable}
isSortable={isSortable}
label={label}
labelProps={labelProps}
name={name}
onChange={onChange}
options={options}
path={path}
readOnly={readOnly}
required={required}
showError={showError}
style={style}
value={value as string | string[]}
width={width}
/>
)
}

export const Select = withCondition(SelectField)

export { SelectInput, type SelectInputProps }

0 comments on commit 661a4a0

Please sign in to comment.