fix(ui): does not render row labels until form state returns (#10002)
Extension of #9933. Custom components are returned by form state, meaning that if we don't wait for form state to return before rendering row labels, the default row label component will render in briefly before being swapped by a custom component (if applicable). Using the new `isLoading` prop on array and block rows, we can conditionally render them just as we currently do for the row fields themselves.
This commit is contained in:
@@ -130,12 +130,14 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
|
||||
}
|
||||
header={
|
||||
<div className={`${baseClass}__row-header`}>
|
||||
<RowLabel
|
||||
CustomComponent={CustomRowLabel}
|
||||
label={fallbackLabel}
|
||||
path={path}
|
||||
rowNumber={rowIndex}
|
||||
/>
|
||||
{isLoading ? null : (
|
||||
<RowLabel
|
||||
CustomComponent={CustomRowLabel}
|
||||
label={fallbackLabel}
|
||||
path={path}
|
||||
rowNumber={rowIndex}
|
||||
/>
|
||||
)}
|
||||
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -140,21 +140,23 @@ export const BlockRow: React.FC<BlocksFieldProps> = ({
|
||||
: undefined
|
||||
}
|
||||
header={
|
||||
Label || (
|
||||
<div className={`${baseClass}__block-header`}>
|
||||
<span className={`${baseClass}__block-number`}>
|
||||
{String(rowIndex + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<Pill
|
||||
className={`${baseClass}__block-pill ${baseClass}__block-pill-${row.blockType}`}
|
||||
pillStyle="white"
|
||||
>
|
||||
{getTranslation(block.labels.singular, i18n)}
|
||||
</Pill>
|
||||
<SectionTitle path={`${path}.blockName`} readOnly={readOnly} />
|
||||
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
|
||||
</div>
|
||||
)
|
||||
isLoading
|
||||
? null
|
||||
: Label || (
|
||||
<div className={`${baseClass}__block-header`}>
|
||||
<span className={`${baseClass}__block-number`}>
|
||||
{String(rowIndex + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<Pill
|
||||
className={`${baseClass}__block-pill ${baseClass}__block-pill-${row.blockType}`}
|
||||
pillStyle="white"
|
||||
>
|
||||
{getTranslation(block.labels.singular, i18n)}
|
||||
</Pill>
|
||||
<SectionTitle path={`${path}.blockName`} readOnly={readOnly} />
|
||||
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
isCollapsed={row.collapsed}
|
||||
key={row.id}
|
||||
|
||||
@@ -25,6 +25,7 @@ export const RowLabelProvider: React.FC<Props<unknown>> = ({ children, path, row
|
||||
const { getDataByPath, getSiblingData } = useWatchForm()
|
||||
const collapsibleData = getSiblingData(path)
|
||||
const arrayData = getDataByPath(path)
|
||||
|
||||
const data = arrayData || collapsibleData
|
||||
|
||||
return <RowLabel.Provider value={{ data, path, rowNumber }}>{children}</RowLabel.Provider>
|
||||
|
||||
@@ -8,6 +8,8 @@ import React from 'react'
|
||||
export const ArrayRowLabel: PayloadClientReactComponent<RowLabelComponent> = () => {
|
||||
const { data } = useRowLabel<{ title: string }>()
|
||||
return (
|
||||
<div style={{ color: 'coral', textTransform: 'uppercase' }}>{data.title || 'Untitled'}</div>
|
||||
<div id="custom-array-row-label" style={{ color: 'coral', textTransform: 'uppercase' }}>
|
||||
{data.title || 'Untitled'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -84,11 +84,25 @@ describe('Array', () => {
|
||||
await page.goto(url.create)
|
||||
await page.locator('#field-rowLabelAsComponent >> .array-field__add-row').click()
|
||||
|
||||
// ensure the default label does not blink in before form state returns
|
||||
const defaultRowLabelWasAttached = await page
|
||||
.waitForSelector('#field-rowLabelAsComponent .array-field__row-header .row-label', {
|
||||
state: 'attached',
|
||||
timeout: 100, // A small timeout to catch any transient rendering
|
||||
})
|
||||
.catch(() => false) // If it doesn't appear, this resolves to `false`
|
||||
|
||||
expect(defaultRowLabelWasAttached).toBeFalsy()
|
||||
|
||||
await expect(page.locator('#field-rowLabelAsComponent #custom-array-row-label')).toBeVisible()
|
||||
|
||||
await page.locator('#field-rowLabelAsComponent__0__title').fill(label)
|
||||
await wait(100)
|
||||
|
||||
const customRowLabel = page.locator(
|
||||
'#rowLabelAsComponent-row-0 >> .array-field__row-header > :text("custom row label")',
|
||||
)
|
||||
|
||||
await expect(customRowLabel).toHaveCSS('text-transform', 'uppercase')
|
||||
})
|
||||
|
||||
|
||||
@@ -868,6 +868,25 @@ export interface ConditionalLogic {
|
||||
text: string;
|
||||
toggleField?: boolean | null;
|
||||
fieldWithCondition?: string | null;
|
||||
customFieldWithField?: string | null;
|
||||
customFieldWithHOC?: string | null;
|
||||
customClientFieldWithCondition?: string | null;
|
||||
customServerFieldWithCondition?: string | null;
|
||||
conditionalRichText?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
userConditional?: string | null;
|
||||
parentGroup?: {
|
||||
enableParentGroupFields?: boolean | null;
|
||||
@@ -2052,42 +2071,257 @@ export interface BlockFieldsSelect<T extends boolean = true> {
|
||||
blocks?:
|
||||
| T
|
||||
| {
|
||||
content?: T | ContentBlockSelect<T>;
|
||||
number?: T | NumberBlockSelect<T>;
|
||||
subBlocks?: T | SubBlocksBlockSelect<T>;
|
||||
tabs?: T | TabsBlockSelect<T>;
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
tabs?:
|
||||
| T
|
||||
| {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
duplicate?:
|
||||
| T
|
||||
| {
|
||||
content?: T | ContentBlockSelect<T>;
|
||||
number?: T | NumberBlockSelect<T>;
|
||||
subBlocks?: T | SubBlocksBlockSelect<T>;
|
||||
tabs?: T | TabsBlockSelect<T>;
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
tabs?:
|
||||
| T
|
||||
| {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
collapsedByDefaultBlocks?:
|
||||
| T
|
||||
| {
|
||||
localizedContent?: T | LocalizedContentBlockSelect<T>;
|
||||
localizedNumber?: T | LocalizedNumberBlockSelect<T>;
|
||||
localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect<T>;
|
||||
localizedTabs?: T | LocalizedTabsBlockSelect<T>;
|
||||
localizedContent?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedNumber?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedSubBlocks?:
|
||||
| T
|
||||
| {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedTabs?:
|
||||
| T
|
||||
| {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
disableSort?:
|
||||
| T
|
||||
| {
|
||||
localizedContent?: T | LocalizedContentBlockSelect<T>;
|
||||
localizedNumber?: T | LocalizedNumberBlockSelect<T>;
|
||||
localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect<T>;
|
||||
localizedTabs?: T | LocalizedTabsBlockSelect<T>;
|
||||
localizedContent?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedNumber?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedSubBlocks?:
|
||||
| T
|
||||
| {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedTabs?:
|
||||
| T
|
||||
| {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
localizedBlocks?:
|
||||
| T
|
||||
| {
|
||||
localizedContent?: T | LocalizedContentBlockSelect<T>;
|
||||
localizedNumber?: T | LocalizedNumberBlockSelect<T>;
|
||||
localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect<T>;
|
||||
localizedTabs?: T | LocalizedTabsBlockSelect<T>;
|
||||
localizedContent?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedNumber?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedSubBlocks?:
|
||||
| T
|
||||
| {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
localizedTabs?:
|
||||
| T
|
||||
| {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
i18nBlocks?:
|
||||
| T
|
||||
@@ -2225,116 +2459,6 @@ export interface BlockFieldsSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "ContentBlock_select".
|
||||
*/
|
||||
export interface ContentBlockSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "NumberBlock_select".
|
||||
*/
|
||||
export interface NumberBlockSelect<T extends boolean = true> {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "SubBlocksBlock_select".
|
||||
*/
|
||||
export interface SubBlocksBlockSelect<T extends boolean = true> {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "TabsBlock_select".
|
||||
*/
|
||||
export interface TabsBlockSelect<T extends boolean = true> {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "localizedContentBlock_select".
|
||||
*/
|
||||
export interface LocalizedContentBlockSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "localizedNumberBlock_select".
|
||||
*/
|
||||
export interface LocalizedNumberBlockSelect<T extends boolean = true> {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "localizedSubBlocksBlock_select".
|
||||
*/
|
||||
export interface LocalizedSubBlocksBlockSelect<T extends boolean = true> {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "localizedTabsBlock_select".
|
||||
*/
|
||||
export interface LocalizedTabsBlockSelect<T extends boolean = true> {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "checkbox-fields_select".
|
||||
@@ -2404,6 +2528,11 @@ export interface ConditionalLogicSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
toggleField?: T;
|
||||
fieldWithCondition?: T;
|
||||
customFieldWithField?: T;
|
||||
customFieldWithHOC?: T;
|
||||
customClientFieldWithCondition?: T;
|
||||
customServerFieldWithCondition?: T;
|
||||
conditionalRichText?: T;
|
||||
userConditional?: T;
|
||||
parentGroup?:
|
||||
| T
|
||||
@@ -2892,10 +3021,53 @@ export interface TabsFieldsSelect<T extends boolean = true> {
|
||||
blocks?:
|
||||
| T
|
||||
| {
|
||||
content?: T | ContentBlockSelect<T>;
|
||||
number?: T | NumberBlockSelect<T>;
|
||||
subBlocks?: T | SubBlocksBlockSelect<T>;
|
||||
tabs?: T | TabsBlockSelect<T>;
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
richText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
subBlocks?:
|
||||
| T
|
||||
| {
|
||||
text?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
number?:
|
||||
| T
|
||||
| {
|
||||
number?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
tabs?:
|
||||
| T
|
||||
| {
|
||||
textInCollapsible?: T;
|
||||
textInRow?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
group?:
|
||||
| T
|
||||
@@ -2905,7 +3077,24 @@ export interface TabsFieldsSelect<T extends boolean = true> {
|
||||
textInRow?: T;
|
||||
numberInRow?: T;
|
||||
json?: T;
|
||||
tab?: T | TabWithNameSelect<T>;
|
||||
tab?:
|
||||
| T
|
||||
| {
|
||||
array?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
};
|
||||
text?: T;
|
||||
defaultValue?: T;
|
||||
arrayInRow?:
|
||||
| T
|
||||
| {
|
||||
textInArrayInRow?: T;
|
||||
id?: T;
|
||||
};
|
||||
};
|
||||
namedTabWithDefaultValue?:
|
||||
| T
|
||||
| {
|
||||
@@ -2955,26 +3144,6 @@ export interface TabsFieldsSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "TabWithName_select".
|
||||
*/
|
||||
export interface TabWithNameSelect<T extends boolean = true> {
|
||||
array?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
};
|
||||
text?: T;
|
||||
defaultValue?: T;
|
||||
arrayInRow?:
|
||||
| T
|
||||
| {
|
||||
textInArrayInRow?: T;
|
||||
id?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "text-fields_select".
|
||||
|
||||
Reference in New Issue
Block a user