fix(ui): processing and initializing form does not disable standalone fields (#11714)
The form component's `initializing` and `processing` states do not disable fields that are rendered outside of `DocumentFields`. Fields currently rely on the `readOnly` prop provided by `DocumentFields` and do not subscribe to these states for themselves. This means that fields that are rendered outright, such as within the bulk edit drawer, they do not receive a `readOnly` prop and are therefore never disabled. The fix is add a `disabled` property to the `useField` hook. This subscribes to the `initializing` and `processing` states in the same way as `DocumentFields`, however, now each field can determine its own disabled state instead of relying solely on the `readOnly` prop. Adding this new prop has no overhead as `processing` and `initializing` is already being subscribed to within `useField`.
This commit is contained in:
@@ -68,8 +68,7 @@ const RichTextComponent: React.FC<
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
formInitializing,
|
disabled: disabledFromField,
|
||||||
formProcessing,
|
|
||||||
initialValue,
|
initialValue,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
@@ -79,7 +78,7 @@ const RichTextComponent: React.FC<
|
|||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
const disabled = readOnlyFromProps || formProcessing || formInitializing
|
const disabled = readOnlyFromProps || disabledFromField
|
||||||
|
|
||||||
const [isSmallWidthViewport, setIsSmallWidthViewport] = useState<boolean>(false)
|
const [isSmallWidthViewport, setIsSmallWidthViewport] = useState<boolean>(false)
|
||||||
const [rerenderProviderKey, setRerenderProviderKey] = useState<Date>()
|
const [rerenderProviderKey, setRerenderProviderKey] = useState<Date>()
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const RichTextField: React.FC<LoadedSlateFieldProps> = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { Description, Error, Label } = {},
|
customComponents: { Description, Error, Label } = {},
|
||||||
formInitializing,
|
disabled: disabledFromField,
|
||||||
initialValue,
|
initialValue,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
@@ -105,7 +105,7 @@ const RichTextField: React.FC<LoadedSlateFieldProps> = (props) => {
|
|||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
const disabled = readOnlyFromProps || formInitializing
|
const disabled = readOnlyFromProps || disabledFromField
|
||||||
|
|
||||||
const editor = useMemo(() => {
|
const editor = useMemo(() => {
|
||||||
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())))
|
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())))
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const DocumentFields: React.FC<Args> = ({
|
|||||||
docPermissions,
|
docPermissions,
|
||||||
fields,
|
fields,
|
||||||
forceSidebarWrap,
|
forceSidebarWrap,
|
||||||
readOnly: readOnlyProp,
|
readOnly,
|
||||||
schemaPathSegments,
|
schemaPathSegments,
|
||||||
}) => {
|
}) => {
|
||||||
const { hasSidebarFields, mainFields, sidebarFields } = useMemo(() => {
|
const { hasSidebarFields, mainFields, sidebarFields } = useMemo(() => {
|
||||||
@@ -53,11 +53,6 @@ export const DocumentFields: React.FC<Args> = ({
|
|||||||
)
|
)
|
||||||
}, [fields])
|
}, [fields])
|
||||||
|
|
||||||
const formInitializing = useFormInitializing()
|
|
||||||
const formProcessing = useFormProcessing()
|
|
||||||
|
|
||||||
const readOnly = readOnlyProp || formInitializing || formProcessing
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={[
|
className={[
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label, RowLabels } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label, RowLabels } = {},
|
||||||
|
disabled,
|
||||||
errorPaths,
|
errorPaths,
|
||||||
rows: rowsData = [],
|
rows: rowsData = [],
|
||||||
showError,
|
showError,
|
||||||
@@ -197,7 +198,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
|
|||||||
const fieldErrorCount = errorPaths.length
|
const fieldErrorCount = errorPaths.length
|
||||||
const fieldHasErrors = submitted && errorPaths.length > 0
|
const fieldHasErrors = submitted && errorPaths.length > 0
|
||||||
|
|
||||||
const showRequired = readOnly && rowsData.length === 0
|
const showRequired = (readOnly || disabled) && rowsData.length === 0
|
||||||
const showMinRows = rowsData.length < minRows || (required && rowsData.length === 0)
|
const showMinRows = rowsData.length < minRows || (required && rowsData.length === 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -285,7 +286,11 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
|
|||||||
).length
|
).length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableSortableItem disabled={readOnly || !isSortable} id={rowID} key={rowID}>
|
<DraggableSortableItem
|
||||||
|
disabled={readOnly || disabled || !isSortable}
|
||||||
|
id={rowID}
|
||||||
|
key={rowID}
|
||||||
|
>
|
||||||
{(draggableSortableItemProps) => (
|
{(draggableSortableItemProps) => (
|
||||||
<ArrayRow
|
<ArrayRow
|
||||||
{...draggableSortableItemProps}
|
{...draggableSortableItemProps}
|
||||||
@@ -303,7 +308,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
|
|||||||
parentPath={path}
|
parentPath={path}
|
||||||
path={rowPath}
|
path={rowPath}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
removeRow={removeRow}
|
removeRow={removeRow}
|
||||||
row={rowData}
|
row={rowData}
|
||||||
rowCount={rowsData?.length}
|
rowCount={rowsData?.length}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
|
|||||||
schemaPath: schemaPathFromProps,
|
schemaPath: schemaPathFromProps,
|
||||||
validate,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const schemaPath = schemaPathFromProps ?? name
|
const schemaPath = schemaPathFromProps ?? name
|
||||||
|
|
||||||
const minRows = (minRowsProp ?? required) ? 1 : 0
|
const minRows = (minRowsProp ?? required) ? 1 : 0
|
||||||
@@ -98,6 +99,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label, RowLabels } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label, RowLabels } = {},
|
||||||
|
disabled,
|
||||||
errorPaths,
|
errorPaths,
|
||||||
rows = [],
|
rows = [],
|
||||||
showError,
|
showError,
|
||||||
@@ -276,7 +278,11 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
|
|||||||
).length
|
).length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableSortableItem disabled={readOnly || !isSortable} id={row.id} key={row.id}>
|
<DraggableSortableItem
|
||||||
|
disabled={readOnly || disabled || !isSortable}
|
||||||
|
id={row.id}
|
||||||
|
key={row.id}
|
||||||
|
>
|
||||||
{(draggableSortableItemProps) => (
|
{(draggableSortableItemProps) => (
|
||||||
<BlockRow
|
<BlockRow
|
||||||
{...draggableSortableItemProps}
|
{...draggableSortableItemProps}
|
||||||
@@ -295,7 +301,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
|
|||||||
parentPath={path}
|
parentPath={path}
|
||||||
path={rowPath}
|
path={rowPath}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
removeRow={removeRow}
|
removeRow={removeRow}
|
||||||
row={row}
|
row={row}
|
||||||
rowCount={rows.length}
|
rowCount={rows.length}
|
||||||
@@ -335,12 +341,12 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<DrawerToggler
|
<DrawerToggler
|
||||||
className={`${baseClass}__drawer-toggler`}
|
className={`${baseClass}__drawer-toggler`}
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
slug={drawerSlug}
|
slug={drawerSlug}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
buttonStyle="icon-label"
|
buttonStyle="icon-label"
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
el="span"
|
el="span"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
iconPosition="left"
|
iconPosition="left"
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import { withCondition } from '../../forms/withCondition/index.js'
|
|||||||
import { useEditDepth } from '../../providers/EditDepth/index.js'
|
import { useEditDepth } from '../../providers/EditDepth/index.js'
|
||||||
import { generateFieldID } from '../../utilities/generateFieldID.js'
|
import { generateFieldID } from '../../utilities/generateFieldID.js'
|
||||||
import { mergeFieldStyles } from '../mergeFieldStyles.js'
|
import { mergeFieldStyles } from '../mergeFieldStyles.js'
|
||||||
import './index.scss'
|
|
||||||
import { fieldBaseClass } from '../shared/index.js'
|
import { fieldBaseClass } from '../shared/index.js'
|
||||||
import { CheckboxInput } from './Input.js'
|
import { CheckboxInput } from './Input.js'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'checkbox'
|
const baseClass = 'checkbox'
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ const CheckboxFieldComponent: CheckboxFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -91,7 +92,7 @@ const CheckboxFieldComponent: CheckboxFieldClientComponent = (props) => {
|
|||||||
showError && 'error',
|
showError && 'error',
|
||||||
className,
|
className,
|
||||||
value && `${baseClass}--checked`,
|
value && `${baseClass}--checked`,
|
||||||
readOnly && `${baseClass}--read-only`,
|
(readOnly || disabled) && `${baseClass}--read-only`,
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -112,7 +113,7 @@ const CheckboxFieldComponent: CheckboxFieldClientComponent = (props) => {
|
|||||||
name={path}
|
name={path}
|
||||||
onToggle={onToggle}
|
onToggle={onToggle}
|
||||||
partialChecked={partialChecked}
|
partialChecked={partialChecked}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
required={required}
|
required={required}
|
||||||
/>
|
/>
|
||||||
<RenderCustomComponent
|
<RenderCustomComponent
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -64,7 +65,7 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
|||||||
baseClass,
|
baseClass,
|
||||||
className,
|
className,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
readOnly && 'read-only',
|
(readOnly || disabled) && 'read-only',
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -84,10 +85,10 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
|||||||
{BeforeInput}
|
{BeforeInput}
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
defaultLanguage={prismToMonacoLanguageMap[language] || language}
|
defaultLanguage={prismToMonacoLanguageMap[language] || language}
|
||||||
onChange={readOnly ? () => null : (val) => setValue(val)}
|
onChange={readOnly || disabled ? () => null : (val) => setValue(val)}
|
||||||
onMount={onMount}
|
onMount={onMount}
|
||||||
options={editorOptions}
|
options={editorOptions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
value={(value as string) || ''}
|
value={(value as string) || ''}
|
||||||
/>
|
/>
|
||||||
{AfterInput}
|
{AfterInput}
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ const CollapsibleFieldComponent: CollapsibleFieldClientComponent = (props) => {
|
|||||||
const [errorCount, setErrorCount] = useState(0)
|
const [errorCount, setErrorCount] = useState(0)
|
||||||
const fieldHasErrors = errorCount > 0
|
const fieldHasErrors = errorCount > 0
|
||||||
|
|
||||||
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {} } = useField({
|
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, disabled } =
|
||||||
|
useField({
|
||||||
path,
|
path,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ const CollapsibleFieldComponent: CollapsibleFieldClientComponent = (props) => {
|
|||||||
parentPath={parentPath}
|
parentPath={parentPath}
|
||||||
parentSchemaPath={parentSchemaPath}
|
parentSchemaPath={parentSchemaPath}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
/>
|
/>
|
||||||
</CollapsibleElement>
|
</CollapsibleElement>
|
||||||
{AfterInput}
|
{AfterInput}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ export type ConfirmPasswordFieldProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ConfirmPasswordField: React.FC<ConfirmPasswordFieldProps> = (props) => {
|
export const ConfirmPasswordField: React.FC<ConfirmPasswordFieldProps> = (props) => {
|
||||||
const { disabled, path = 'confirm-password' } = props
|
const { disabled: disabledFromProps, path = 'confirm-password' } = props
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { setValue, showError, value } = useField({
|
const { disabled, setValue, showError, value } = useField({
|
||||||
path,
|
path,
|
||||||
validate: (value, options) => {
|
validate: (value, options) => {
|
||||||
return confirmPassword(value, {
|
return confirmPassword(value, {
|
||||||
@@ -47,7 +47,7 @@ export const ConfirmPasswordField: React.FC<ConfirmPasswordFieldProps> = (props)
|
|||||||
<input
|
<input
|
||||||
aria-label={t('authentication:confirmPassword')}
|
aria-label={t('authentication:confirmPassword')}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
disabled={!!disabled}
|
disabled={!!(disabled || disabledFromProps)}
|
||||||
id="field-confirm-password"
|
id="field-confirm-password"
|
||||||
name="confirm-password"
|
name="confirm-password"
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ const DateTimeFieldComponent: DateFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -102,7 +103,7 @@ const DateTimeFieldComponent: DateFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(incomingDate: Date) => {
|
(incomingDate: Date) => {
|
||||||
if (!readOnly) {
|
if (!(readOnly || disabled)) {
|
||||||
if (timezone && selectedTimezone && incomingDate) {
|
if (timezone && selectedTimezone && incomingDate) {
|
||||||
// Create TZDate instances for the selected timezone
|
// Create TZDate instances for the selected timezone
|
||||||
const TZDateWithSelectedTz = TZDate.tz(selectedTimezone)
|
const TZDateWithSelectedTz = TZDate.tz(selectedTimezone)
|
||||||
@@ -132,7 +133,7 @@ const DateTimeFieldComponent: DateFieldClientComponent = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[readOnly, setValue, timezone, selectedTimezone],
|
[readOnly, disabled, timezone, selectedTimezone, isDateOnly, setValue],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onChangeTimezone = useCallback(
|
const onChangeTimezone = useCallback(
|
||||||
@@ -157,7 +158,7 @@ const DateTimeFieldComponent: DateFieldClientComponent = (props) => {
|
|||||||
baseClass,
|
baseClass,
|
||||||
className,
|
className,
|
||||||
showError && `${baseClass}--has-error`,
|
showError && `${baseClass}--has-error`,
|
||||||
readOnly && 'read-only',
|
(readOnly || disabled) && 'read-only',
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -180,7 +181,7 @@ const DateTimeFieldComponent: DateFieldClientComponent = (props) => {
|
|||||||
...datePickerProps?.overrides,
|
...datePickerProps?.overrides,
|
||||||
}}
|
}}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
value={displayedValue}
|
value={displayedValue}
|
||||||
/>
|
/>
|
||||||
{timezone && supportedTimezones.length > 0 && (
|
{timezone && supportedTimezones.length > 0 && (
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const EmailFieldComponent: EmailFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -63,7 +64,13 @@ const EmailFieldComponent: EmailFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={[fieldBaseClass, 'email', className, showError && 'error', readOnly && 'read-only']
|
className={[
|
||||||
|
fieldBaseClass,
|
||||||
|
'email',
|
||||||
|
className,
|
||||||
|
showError && 'error',
|
||||||
|
(readOnly || disabled) && 'read-only',
|
||||||
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
style={styles}
|
style={styles}
|
||||||
@@ -84,7 +91,7 @@ const EmailFieldComponent: EmailFieldClientComponent = (props) => {
|
|||||||
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||||
<input
|
<input
|
||||||
autoComplete={autoComplete}
|
autoComplete={autoComplete}
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
name={path}
|
name={path}
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => {
|
|||||||
const isWithinGroup = useGroup()
|
const isWithinGroup = useGroup()
|
||||||
const isWithinRow = useRow()
|
const isWithinRow = useRow()
|
||||||
const isWithinTab = useTabs()
|
const isWithinTab = useTabs()
|
||||||
|
|
||||||
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, errorPaths } =
|
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, errorPaths } =
|
||||||
useField({ path })
|
useField({ path })
|
||||||
|
|
||||||
const submitted = useFormSubmitted()
|
const submitted = useFormSubmitted()
|
||||||
const errorCount = errorPaths.length
|
const errorCount = errorPaths.length
|
||||||
const fieldHasErrors = submitted && errorCount > 0
|
const fieldHasErrors = submitted && errorCount > 0
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
|||||||
readOnly,
|
readOnly,
|
||||||
validate,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const [jsonError, setJsonError] = useState<string>()
|
const [jsonError, setJsonError] = useState<string>()
|
||||||
const inputChangeFromRef = React.useRef<'system' | 'user'>('system')
|
const inputChangeFromRef = React.useRef<'system' | 'user'>('system')
|
||||||
const [editorKey, setEditorKey] = useState<string>('')
|
const [editorKey, setEditorKey] = useState<string>('')
|
||||||
@@ -46,6 +47,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
initialValue,
|
initialValue,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
@@ -90,7 +92,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(val) => {
|
(val) => {
|
||||||
if (readOnly) {
|
if (readOnly || disabled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inputChangeFromRef.current = 'user'
|
inputChangeFromRef.current = 'user'
|
||||||
@@ -103,7 +105,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
|||||||
setJsonError(e)
|
setJsonError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[readOnly, setValue],
|
[readOnly, disabled, setValue],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -128,7 +130,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
|||||||
baseClass,
|
baseClass,
|
||||||
className,
|
className,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
readOnly && 'read-only',
|
(readOnly || disabled) && 'read-only',
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -153,7 +155,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onMount={handleMount}
|
onMount={handleMount}
|
||||||
options={editorOptions}
|
options={editorOptions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
value={initialStringValue}
|
value={initialStringValue}
|
||||||
wrapperProps={{
|
wrapperProps={{
|
||||||
id: `field-${path?.replace(/\./g, '__')}`,
|
id: `field-${path?.replace(/\./g, '__')}`,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -88,7 +89,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const handleHasManyChange = useCallback(
|
const handleHasManyChange = useCallback(
|
||||||
(selectedOption) => {
|
(selectedOption) => {
|
||||||
if (!readOnly) {
|
if (!(readOnly || disabled)) {
|
||||||
let newValue
|
let newValue
|
||||||
if (!selectedOption) {
|
if (!selectedOption) {
|
||||||
newValue = []
|
newValue = []
|
||||||
@@ -101,7 +102,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
|||||||
setValue(newValue)
|
setValue(newValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[readOnly, setValue],
|
[readOnly, disabled, setValue],
|
||||||
)
|
)
|
||||||
|
|
||||||
// useEffect update valueToRender:
|
// useEffect update valueToRender:
|
||||||
@@ -131,7 +132,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
|||||||
'number',
|
'number',
|
||||||
className,
|
className,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
readOnly && 'read-only',
|
(readOnly || disabled) && 'read-only',
|
||||||
hasMany && 'has-many',
|
hasMany && 'has-many',
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -153,7 +154,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
|||||||
{hasMany ? (
|
{hasMany ? (
|
||||||
<ReactSelect
|
<ReactSelect
|
||||||
className={`field-${path.replace(/\./g, '__')}`}
|
className={`field-${path.replace(/\./g, '__')}`}
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
filterOption={(_, rawInput) => {
|
filterOption={(_, rawInput) => {
|
||||||
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
const isOverHasMany = Array.isArray(value) && value.length >= maxRows
|
||||||
return isNumber(rawInput) && !isOverHasMany
|
return isNumber(rawInput) && !isOverHasMany
|
||||||
@@ -179,7 +180,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
|||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
max={max}
|
max={max}
|
||||||
min={min}
|
min={min}
|
||||||
|
|||||||
@@ -71,8 +71,7 @@ const PasswordFieldComponent: React.FC<PasswordFieldProps> = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
formInitializing,
|
disabled,
|
||||||
formProcessing,
|
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -81,8 +80,6 @@ const PasswordFieldComponent: React.FC<PasswordFieldProps> = (props) => {
|
|||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
const disabled = disabledFromProps || formInitializing || formProcessing
|
|
||||||
|
|
||||||
const renderRTL = isFieldRTL({
|
const renderRTL = isFieldRTL({
|
||||||
fieldLocalized: false,
|
fieldLocalized: false,
|
||||||
fieldRTL: rtl,
|
fieldRTL: rtl,
|
||||||
@@ -109,7 +106,7 @@ const PasswordFieldComponent: React.FC<PasswordFieldProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
path={path}
|
path={path}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
readOnly={disabled}
|
readOnly={disabled || disabledFromProps}
|
||||||
required={required}
|
required={required}
|
||||||
rtl={renderRTL}
|
rtl={renderRTL}
|
||||||
showError={showError}
|
showError={showError}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export const PointFieldComponent: PointFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value = [null, null],
|
value = [null, null],
|
||||||
@@ -81,7 +82,7 @@ export const PointFieldComponent: PointFieldClientComponent = (props) => {
|
|||||||
baseClass,
|
baseClass,
|
||||||
className,
|
className,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
readOnly && 'read-only',
|
(readOnly || disabled) && 'read-only',
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -105,7 +106,7 @@ export const PointFieldComponent: PointFieldClientComponent = (props) => {
|
|||||||
{/* disable eslint rule because the label is dynamic */}
|
{/* disable eslint rule because the label is dynamic */}
|
||||||
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||||
<input
|
<input
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
id={`field-longitude-${path?.replace(/\./g, '__')}`}
|
id={`field-longitude-${path?.replace(/\./g, '__')}`}
|
||||||
name={`${path}.longitude`}
|
name={`${path}.longitude`}
|
||||||
onChange={(e) => handleChange(e, 0)}
|
onChange={(e) => handleChange(e, 0)}
|
||||||
@@ -138,7 +139,7 @@ export const PointFieldComponent: PointFieldClientComponent = (props) => {
|
|||||||
{/* disable eslint rule because the label is dynamic */}
|
{/* disable eslint rule because the label is dynamic */}
|
||||||
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||||
<input
|
<input
|
||||||
disabled={readOnly}
|
disabled={readOnly || disabled}
|
||||||
id={`field-latitude-${path?.replace(/\./g, '__')}`}
|
id={`field-latitude-${path?.replace(/\./g, '__')}`}
|
||||||
name={`${path}.latitude`}
|
name={`${path}.latitude`}
|
||||||
onChange={(e) => handleChange(e, 1)}
|
onChange={(e) => handleChange(e, 1)}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value: valueFromContext,
|
value: valueFromContext,
|
||||||
@@ -73,7 +74,7 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => {
|
|||||||
className,
|
className,
|
||||||
`${baseClass}--layout-${layout}`,
|
`${baseClass}--layout-${layout}`,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
readOnly && `${baseClass}--read-only`,
|
(readOnly || disabled) && `${baseClass}--read-only`,
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -115,13 +116,13 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => {
|
|||||||
onChangeFromProps(optionValue)
|
onChangeFromProps(optionValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!(readOnly || disabled)) {
|
||||||
setValue(optionValue, !!disableModifyingFormFromProps)
|
setValue(optionValue, !!disableModifyingFormFromProps)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
option={optionIsObject(option) ? option : { label: option, value: option }}
|
option={optionIsObject(option) ? option : { label: option, value: option }}
|
||||||
path={path}
|
path={path}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
uuid={uuid}
|
uuid={uuid}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
filterOptions,
|
filterOptions,
|
||||||
initialValue,
|
initialValue,
|
||||||
setValue,
|
setValue,
|
||||||
@@ -577,8 +578,8 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
className,
|
className,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
errorLoading && 'error-loading',
|
errorLoading && 'error-loading',
|
||||||
readOnly && `${baseClass}--read-only`,
|
(readOnly || disabled) && `${baseClass}--read-only`,
|
||||||
!readOnly && allowCreate && `${baseClass}--allow-create`,
|
!(readOnly || disabled) && allowCreate && `${baseClass}--allow-create`,
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
@@ -611,7 +612,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
onDocumentDrawerOpen,
|
onDocumentDrawerOpen,
|
||||||
onSave,
|
onSave,
|
||||||
}}
|
}}
|
||||||
disabled={readOnly || isDrawerOpen}
|
disabled={readOnly || disabled || isDrawerOpen}
|
||||||
filterOption={enableWordBoundarySearch ? filterOption : undefined}
|
filterOption={enableWordBoundarySearch ? filterOption : undefined}
|
||||||
getOptionValue={(option) => {
|
getOptionValue={(option) => {
|
||||||
if (!option) {
|
if (!option) {
|
||||||
@@ -625,7 +626,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
isMulti={hasMany}
|
isMulti={hasMany}
|
||||||
isSortable={isSortable}
|
isSortable={isSortable}
|
||||||
onChange={
|
onChange={
|
||||||
!readOnly
|
!(readOnly || disabled)
|
||||||
? (selected) => {
|
? (selected) => {
|
||||||
if (selected === null) {
|
if (selected === null) {
|
||||||
setValue(hasMany ? [] : null)
|
setValue(hasMany ? [] : null)
|
||||||
@@ -689,7 +690,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
showError={showError}
|
showError={showError}
|
||||||
value={valueToRender ?? null}
|
value={valueToRender ?? null}
|
||||||
/>
|
/>
|
||||||
{!readOnly && allowCreate && (
|
{!(readOnly || disabled) && allowCreate && (
|
||||||
<AddNewRelation
|
<AddNewRelation
|
||||||
hasMany={hasMany}
|
hasMany={hasMany}
|
||||||
path={path}
|
path={path}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -74,7 +75,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const onChange: ReactSelectAdapterProps['onChange'] = useCallback(
|
const onChange: ReactSelectAdapterProps['onChange'] = useCallback(
|
||||||
(selectedOption: OptionObject | OptionObject[]) => {
|
(selectedOption: OptionObject | OptionObject[]) => {
|
||||||
if (!readOnly) {
|
if (!readOnly || disabled) {
|
||||||
let newValue: string | string[] = null
|
let newValue: string | string[] = null
|
||||||
if (selectedOption && hasMany) {
|
if (selectedOption && hasMany) {
|
||||||
if (Array.isArray(selectedOption)) {
|
if (Array.isArray(selectedOption)) {
|
||||||
@@ -93,7 +94,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
|||||||
setValue(newValue)
|
setValue(newValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[readOnly, hasMany, setValue, onChangeFromProps],
|
[readOnly, disabled, hasMany, setValue, onChangeFromProps],
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = useMemo(() => mergeFieldStyles(field), [field])
|
const styles = useMemo(() => mergeFieldStyles(field), [field])
|
||||||
@@ -116,7 +117,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
options={options}
|
options={options}
|
||||||
path={path}
|
path={path}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
required={required}
|
required={required}
|
||||||
showError={showError}
|
showError={showError}
|
||||||
style={styles}
|
style={styles}
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ type ActiveTabProps = {
|
|||||||
readonly permissions: SanitizedFieldPermissions
|
readonly permissions: SanitizedFieldPermissions
|
||||||
readonly readOnly: boolean
|
readonly readOnly: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabContent({
|
function TabContent({
|
||||||
description,
|
description,
|
||||||
fields,
|
fields,
|
||||||
@@ -273,6 +274,7 @@ function TabContent({
|
|||||||
readOnly,
|
readOnly,
|
||||||
}: ActiveTabProps) {
|
}: ActiveTabProps) {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
const { customComponents: { AfterInput, BeforeInput, Description, Field } = {} } = useField({
|
const { customComponents: { AfterInput, BeforeInput, Description, Field } = {} } = useField({
|
||||||
path,
|
path,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const TextFieldComponent: TextFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -75,7 +76,7 @@ const TextFieldComponent: TextFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const handleHasManyChange = useCallback(
|
const handleHasManyChange = useCallback(
|
||||||
(selectedOption) => {
|
(selectedOption) => {
|
||||||
if (!readOnly) {
|
if (!(readOnly || disabled)) {
|
||||||
let newValue
|
let newValue
|
||||||
if (!selectedOption) {
|
if (!selectedOption) {
|
||||||
newValue = []
|
newValue = []
|
||||||
@@ -88,7 +89,7 @@ const TextFieldComponent: TextFieldClientComponent = (props) => {
|
|||||||
setValue(newValue)
|
setValue(newValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[readOnly, setValue],
|
[readOnly, setValue, disabled],
|
||||||
)
|
)
|
||||||
|
|
||||||
// useEffect update valueToRender:
|
// useEffect update valueToRender:
|
||||||
@@ -136,7 +137,7 @@ const TextFieldComponent: TextFieldClientComponent = (props) => {
|
|||||||
}
|
}
|
||||||
path={path}
|
path={path}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
required={required}
|
required={required}
|
||||||
rtl={renderRTL}
|
rtl={renderRTL}
|
||||||
showError={showError}
|
showError={showError}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ const TextareaFieldComponent: TextareaFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
value,
|
value,
|
||||||
@@ -86,7 +87,7 @@ const TextareaFieldComponent: TextareaFieldClientComponent = (props) => {
|
|||||||
}}
|
}}
|
||||||
path={path}
|
path={path}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
required={required}
|
required={required}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
rtl={isRTL}
|
rtl={isRTL}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export function UploadComponent(props: UploadFieldClientProps) {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||||
|
disabled,
|
||||||
filterOptions,
|
filterOptions,
|
||||||
setValue,
|
setValue,
|
||||||
showError,
|
showError,
|
||||||
@@ -79,7 +80,7 @@ export function UploadComponent(props: UploadFieldClientProps) {
|
|||||||
maxRows={maxRows}
|
maxRows={maxRows}
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
path={path}
|
path={path}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || disabled}
|
||||||
relationTo={relationTo}
|
relationTo={relationTo}
|
||||||
required={required}
|
required={required}
|
||||||
serverURL={config.serverURL}
|
serverURL={config.serverURL}
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export const useField = <TValue,>(options: Options): FieldType<TValue> => {
|
|||||||
const result: FieldType<TValue> = useMemo(
|
const result: FieldType<TValue> = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
customComponents: field?.customComponents,
|
customComponents: field?.customComponents,
|
||||||
|
disabled: processing || initializing,
|
||||||
errorMessage: field?.errorMessage,
|
errorMessage: field?.errorMessage,
|
||||||
errorPaths: field?.errorPaths || [],
|
errorPaths: field?.errorPaths || [],
|
||||||
filterOptions,
|
filterOptions,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export type Options = {
|
|||||||
|
|
||||||
export type FieldType<T> = {
|
export type FieldType<T> = {
|
||||||
customComponents?: FieldState['customComponents']
|
customComponents?: FieldState['customComponents']
|
||||||
|
disabled: boolean
|
||||||
errorMessage?: string
|
errorMessage?: string
|
||||||
errorPaths?: string[]
|
errorPaths?: string[]
|
||||||
filterOptions?: FilterOptionsResult
|
filterOptions?: FilterOptionsResult
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import type { BrowserContext, Page } from '@playwright/test'
|
import type { BrowserContext, Page } from '@playwright/test'
|
||||||
|
import type { PayloadTestSDK } from 'helpers/sdk/index.js'
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test'
|
import { expect, test } from '@playwright/test'
|
||||||
import { addBlock } from 'helpers/e2e/addBlock.js'
|
import { addBlock } from 'helpers/e2e/addBlock.js'
|
||||||
import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js'
|
import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import React from 'react'
|
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
import type { Config, Post } from './payload-types.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ensureCompilationIsDone,
|
ensureCompilationIsDone,
|
||||||
initPageConsoleErrorCatch,
|
initPageConsoleErrorCatch,
|
||||||
@@ -22,6 +24,8 @@ const dirname = path.dirname(filename)
|
|||||||
|
|
||||||
const title = 'Title'
|
const title = 'Title'
|
||||||
let context: BrowserContext
|
let context: BrowserContext
|
||||||
|
let payload: PayloadTestSDK<Config>
|
||||||
|
let serverURL: string
|
||||||
|
|
||||||
test.describe('Form State', () => {
|
test.describe('Form State', () => {
|
||||||
let page: Page
|
let page: Page
|
||||||
@@ -29,8 +33,7 @@ test.describe('Form State', () => {
|
|||||||
|
|
||||||
test.beforeAll(async ({ browser }, testInfo) => {
|
test.beforeAll(async ({ browser }, testInfo) => {
|
||||||
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
||||||
|
;({ payload, serverURL } = await initPayloadE2ENoConfig({ dirname }))
|
||||||
const { payload, serverURL } = await initPayloadE2ENoConfig({ dirname })
|
|
||||||
postsUrl = new AdminUrlUtil(serverURL, 'posts')
|
postsUrl = new AdminUrlUtil(serverURL, 'posts')
|
||||||
|
|
||||||
context = await browser.newContext()
|
context = await browser.newContext()
|
||||||
@@ -38,15 +41,31 @@ test.describe('Form State', () => {
|
|||||||
initPageConsoleErrorCatch(page)
|
initPageConsoleErrorCatch(page)
|
||||||
await ensureCompilationIsDone({ page, serverURL })
|
await ensureCompilationIsDone({ page, serverURL })
|
||||||
})
|
})
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
// await throttleTest({ page, context, delay: 'Fast 3G' })
|
||||||
|
})
|
||||||
|
|
||||||
test('collection — should re-enable fields after save', async () => {
|
test('should disable fields during initialization', async () => {
|
||||||
|
await page.goto(postsUrl.create, { waitUntil: 'commit' })
|
||||||
|
await expect(page.locator('#field-title')).toBeDisabled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should disable fields while processing', async () => {
|
||||||
|
const doc = await createPost()
|
||||||
|
await page.goto(postsUrl.edit(doc.id))
|
||||||
|
await page.locator('#field-title').fill(title)
|
||||||
|
await page.click('#action-save', { delay: 100 })
|
||||||
|
await expect(page.locator('#field-title')).toBeDisabled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should re-enable fields after save', async () => {
|
||||||
await page.goto(postsUrl.create)
|
await page.goto(postsUrl.create)
|
||||||
await page.locator('#field-title').fill(title)
|
await page.locator('#field-title').fill(title)
|
||||||
await saveDocAndAssert(page)
|
await saveDocAndAssert(page)
|
||||||
await expect(page.locator('#field-title')).toBeEnabled()
|
await expect(page.locator('#field-title')).toBeEnabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should thread proper event argument to validation functions', async () => {
|
test('should only validate on submit via the `event` argument', async () => {
|
||||||
await page.goto(postsUrl.create)
|
await page.goto(postsUrl.create)
|
||||||
await page.locator('#field-title').fill(title)
|
await page.locator('#field-title').fill(title)
|
||||||
await page.locator('#field-validateUsingEvent').fill('Not allowed')
|
await page.locator('#field-validateUsingEvent').fill('Not allowed')
|
||||||
@@ -161,3 +180,13 @@ test.describe('Form State', () => {
|
|||||||
await cdpSession.detach()
|
await cdpSession.detach()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function createPost(overrides?: Partial<Post>): Promise<Post> {
|
||||||
|
return payload.create({
|
||||||
|
collection: 'posts',
|
||||||
|
data: {
|
||||||
|
title: 'Post Title',
|
||||||
|
...overrides,
|
||||||
|
},
|
||||||
|
}) as unknown as Promise<Post>
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@payload-config": ["./test/_community/config.ts"],
|
"@payload-config": ["./test/form-state/config.ts"],
|
||||||
"@payloadcms/admin-bar": ["./packages/admin-bar/src"],
|
"@payloadcms/admin-bar": ["./packages/admin-bar/src"],
|
||||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
||||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
||||||
|
|||||||
Reference in New Issue
Block a user