fix(ui): awaits form state before rendering conditional fields (#9933)

When a condition exists on a field and it resolves to `false`, it
currently "blinks" in and out when rendered within an array or block
row. This is because when add rows to form state, we iterate over the
_fields_ of that row and render their respective components. Then when
conditions are checked for that field, we're expecting `passesCondition`
to be explicitly `false`, ultimately _rendering_ the field for a brief
moment before form state returns with evaluated conditions. The fix is
to set these fields into local form state with a new `isLoading: true`
prop, then display a loader within the row until form state returns with
its proper conditions.
This commit is contained in:
Jacob Fletcher
2024-12-13 11:42:52 -05:00
committed by GitHub
parent 9c8cdea4b3
commit 796df37461
14 changed files with 157 additions and 63 deletions

View File

@@ -9,11 +9,13 @@ import type { UseDraggableSortableReturn } from '../../elements/DraggableSortabl
import { ArrayAction } from '../../elements/ArrayAction/index.js'
import { Collapsible } from '../../elements/Collapsible/index.js'
import { ErrorPill } from '../../elements/ErrorPill/index.js'
import { ShimmerEffect } from '../../elements/ShimmerEffect/index.js'
import { useFormSubmitted } from '../../forms/Form/context.js'
import { RenderFields } from '../../forms/RenderFields/index.js'
import { RowLabel } from '../../forms/RowLabel/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { useThrottledValue } from '../../hooks/useThrottledValue.js'
import './index.scss'
import { useTranslation } from '../../providers/Translation/index.js'
const baseClass = 'array-field'
@@ -25,6 +27,7 @@ type ArrayRowProps = {
readonly fields: ClientField[]
readonly forceRender?: boolean
readonly hasMaxRows?: boolean
readonly isLoading?: boolean
readonly isSortable?: boolean
readonly labels: Partial<ArrayField['labels']>
readonly moveRow: (fromIndex: number, toIndex: number) => void
@@ -50,6 +53,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
forceRender = false,
hasMaxRows,
isDragging,
isLoading: isLoadingFromProps,
isSortable,
labels,
listeners,
@@ -68,6 +72,8 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
transform,
transition,
}) => {
const isLoading = useThrottledValue(isLoadingFromProps, 500)
const { i18n } = useTranslation()
const hasSubmitted = useFormSubmitted()
@@ -136,17 +142,21 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
isCollapsed={row.collapsed}
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
>
<RenderFields
className={`${baseClass}__fields`}
fields={fields}
forceRender={forceRender}
margins="small"
parentIndexPath=""
parentPath={path}
parentSchemaPath={schemaPath}
permissions={permissions === true ? permissions : permissions?.fields}
readOnly={readOnly}
/>
{isLoading ? (
<ShimmerEffect />
) : (
<RenderFields
className={`${baseClass}__fields`}
fields={fields}
forceRender={forceRender}
margins="small"
parentIndexPath=""
parentPath={path}
parentSchemaPath={schemaPath}
permissions={permissions === true ? permissions : permissions?.fields}
readOnly={readOnly}
/>
)}
</Collapsible>
</div>
)

View File

@@ -276,7 +276,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
onDragEnd={({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex)}
>
{rowsData.map((rowData, i) => {
const { id: rowID } = rowData
const { id: rowID, isLoading } = rowData
const rowPath = `${path}.${i}`
@@ -296,6 +296,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
fields={fields}
forceRender={forceRender}
hasMaxRows={hasMaxRows}
isLoading={isLoading}
isSortable={isSortable}
labels={labels}
moveRow={moveRow}