Files
payloadcms/packages/ui/src/fields/Code/index.tsx
Jacob Fletcher a6f13f7330 fix(ui): properly extracts label from field in FieldLabel component (#8190)
Although the `<FieldLabel />` component receives a `field` prop, it does
not use this prop to extract the `label` from the field. This is
currently only an issue when rendering this component directly, such as
within `admin.components.Label`. The label simply won't appear unless
explicitly provided, despite it being passed as `field.label`. This is
not an issue when rendering field components themselves, because they
properly thread this value through as a top-level prop.

Here's an example of the issue:

```tsx
import type { TextFieldLabelServerComponent } from 'payload'

import { FieldLabel } from '@payloadcms/ui'
import React from 'react'

export const MyCustomLabelComponent: TextFieldLabelServerComponent = ({ clientField }) => {
  return (
    <FieldLabel
      field={clientField}
      label={clientField.label} // this should not be needed!
    />
  )
}
```

Here is the end result:

```tsx
import type { TextFieldLabelServerComponent } from 'payload'

import { FieldLabel } from '@payloadcms/ui'
import React from 'react'

export const MyCustomLabelComponent: TextFieldLabelServerComponent = ({ clientField }) => {
  return <FieldLabel field={clientField} />
}
```
2024-09-12 14:45:17 -04:00

115 lines
3.2 KiB
TypeScript

'use client'
import type { CodeFieldClientComponent } from 'payload'
import React, { useCallback } from 'react'
import { CodeEditor } from '../../elements/CodeEditor/index.js'
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { RenderComponent } from '../../providers/Config/RenderComponent.js'
import { FieldDescription } from '../FieldDescription/index.js'
import { FieldError } from '../FieldError/index.js'
import { FieldLabel } from '../FieldLabel/index.js'
import { fieldBaseClass } from '../shared/index.js'
import './index.scss'
const prismToMonacoLanguageMap = {
js: 'javascript',
ts: 'typescript',
}
const baseClass = 'code-field'
const CodeFieldComponent: CodeFieldClientComponent = (props) => {
const {
descriptionProps,
errorProps,
field,
field: {
name,
_path: pathFromProps,
admin: {
className,
description,
editorOptions = {},
language = 'javascript',
readOnly: readOnlyFromAdmin,
style,
width,
} = {},
label,
required,
},
labelProps,
readOnly: readOnlyFromTopLevelProps,
validate,
} = props
const readOnlyFromProps = readOnlyFromTopLevelProps || readOnlyFromAdmin
const memoizedValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
return validate(value, { ...options, required })
}
},
[validate, required],
)
const { path: pathFromContext, readOnly: readOnlyFromContext } = useFieldProps()
const { formInitializing, formProcessing, path, setValue, showError, value } = useField({
path: pathFromContext ?? pathFromProps ?? name,
validate: memoizedValidate,
})
const disabled = readOnlyFromProps || readOnlyFromContext || formProcessing || formInitializing
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
showError && 'error',
disabled && 'read-only',
]
.filter(Boolean)
.join(' ')}
style={{
...style,
width,
}}
>
<FieldLabel field={field} Label={field?.admin?.components?.Label} {...(labelProps || {})} />
<div className={`${fieldBaseClass}__wrap`}>
<FieldError
CustomError={field?.admin?.components?.Error}
field={field}
path={path}
{...(errorProps || {})}
/>
<RenderComponent mappedComponent={field?.admin?.components?.beforeInput} />
<CodeEditor
defaultLanguage={prismToMonacoLanguageMap[language] || language}
onChange={disabled ? () => null : (val) => setValue(val)}
options={editorOptions}
readOnly={disabled}
value={(value as string) || ''}
/>
<RenderComponent mappedComponent={field?.admin?.components?.afterInput} />
</div>
<FieldDescription
Description={field?.admin?.components?.Description}
description={description}
field={field}
{...(descriptionProps || {})}
/>
</div>
)
}
export const CodeField = withCondition(CodeFieldComponent)