Merge pull request #1223 from bigmistqke/feat/customizable-header-labels
WIP: customizable RowHeaders
This commit is contained in:
@@ -46,6 +46,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------- |
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
| **`components.RowLabel`** | Function or React component to be rendered as the label on the array row. Recieves `({ data, index, path })` as args |
|
||||
|
||||
|
||||
### Example
|
||||
@@ -68,6 +69,10 @@ const ExampleCollection: CollectionConfig = {
|
||||
plural: 'Slides',
|
||||
},
|
||||
fields: [ // required
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
}
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
@@ -78,7 +83,14 @@ const ExampleCollection: CollectionConfig = {
|
||||
name: 'caption',
|
||||
type: 'text',
|
||||
}
|
||||
]
|
||||
],
|
||||
admin: {
|
||||
components: {
|
||||
RowLabel: ({ data, index }) => {
|
||||
return data?.title || `Slide ${String(index).padStart(2, '0')}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | ------------------------------------------------------------------------- |
|
||||
| **`label`** * | A label to render within the header of the collapsible component. |
|
||||
| **`label`** * | A label to render within the header of the collapsible component. This can be a string, function or react component. Function/components receive `({ data, path })` as args. |
|
||||
| **`fields`** * | Array of field types to nest within this Collapsible. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
|
||||
@@ -38,9 +38,14 @@ const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
label: 'Header of collapsible goes here',
|
||||
label: ({ data }) => data?.title || 'Untitled',
|
||||
type: 'collapsible', // required
|
||||
fields: [ // required
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'someTextField',
|
||||
type: 'text',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import AnimateHeight from 'react-animate-height';
|
||||
import { fieldAffectsData } from '../../../../fields/config/types';
|
||||
import { FieldAffectingData, fieldAffectsData } from '../../../../fields/config/types';
|
||||
import SearchFilter from '../SearchFilter';
|
||||
import ColumnSelector from '../ColumnSelector';
|
||||
import WhereBuilder from '../WhereBuilder';
|
||||
@@ -48,7 +48,7 @@ const ListControls: React.FC<Props> = (props) => {
|
||||
fieldName={titleField && fieldAffectsData(titleField) ? titleField.name : undefined}
|
||||
handleChange={handleWhereChange}
|
||||
modifySearchQuery={modifySearchQuery}
|
||||
fieldLabel={titleField && titleField.label ? titleField.label : undefined}
|
||||
fieldLabel={titleField && fieldAffectsData(titleField) && titleField.label ? titleField.label : undefined}
|
||||
listSearchableFields={textFieldsToBeSearched}
|
||||
/>
|
||||
<div className={`${baseClass}__buttons`}>
|
||||
|
||||
@@ -20,7 +20,7 @@ const RenderFields: React.FC<Props> = (props) => {
|
||||
readOnly: readOnlyOverride,
|
||||
className,
|
||||
forceRender,
|
||||
indexPath: incomingIndexPath
|
||||
indexPath: incomingIndexPath,
|
||||
} = props;
|
||||
|
||||
const [hasRendered, setHasRendered] = useState(Boolean(forceRender));
|
||||
@@ -107,11 +107,7 @@ const RenderFields: React.FC<Props> = (props) => {
|
||||
className="missing-field"
|
||||
key={fieldIndex}
|
||||
>
|
||||
No matched field found for
|
||||
{' '}
|
||||
"
|
||||
{field.label}
|
||||
"
|
||||
{`No matched field found for "${field.label}"`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
55
src/admin/components/forms/RowLabel/index.tsx
Normal file
55
src/admin/components/forms/RowLabel/index.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { isComponent, Props } from './types';
|
||||
import { useWatchForm } from '../Form/context';
|
||||
|
||||
const baseClass = 'row-label';
|
||||
|
||||
export const RowLabel: React.FC<Props> = ({ className, ...rest }) => {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
className={[
|
||||
baseClass,
|
||||
className,
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
<RowLabelContent {...rest} />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const RowLabelContent: React.FC<Omit<Props, 'className'>> = (props) => {
|
||||
const {
|
||||
path,
|
||||
label,
|
||||
rowNumber,
|
||||
} = props;
|
||||
|
||||
const { getDataByPath, getSiblingData } = useWatchForm();
|
||||
const collapsibleData = getSiblingData(path);
|
||||
const arrayData = getDataByPath(path);
|
||||
const data = arrayData || collapsibleData;
|
||||
|
||||
if (isComponent(label)) {
|
||||
const Label = label;
|
||||
return (
|
||||
<Label
|
||||
data={data}
|
||||
path={path}
|
||||
index={rowNumber}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{typeof label === 'function' ? label({
|
||||
data,
|
||||
path,
|
||||
index: rowNumber,
|
||||
}) : label}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
25
src/admin/components/forms/RowLabel/types.ts
Normal file
25
src/admin/components/forms/RowLabel/types.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Data } from '../Form/types';
|
||||
|
||||
export type Props = {
|
||||
path: string;
|
||||
label?: RowLabel;
|
||||
rowNumber?: number;
|
||||
className?: string,
|
||||
}
|
||||
|
||||
export type RowLabelArgs = {
|
||||
data: Data,
|
||||
path: string,
|
||||
index?: number,
|
||||
}
|
||||
|
||||
export type RowLabelFunction = (args: RowLabelArgs) => string
|
||||
|
||||
export type RowLabelComponent = React.ComponentType<RowLabelArgs>
|
||||
|
||||
export type RowLabel = string | RowLabelFunction | RowLabelComponent
|
||||
|
||||
export function isComponent(label: RowLabel): label is RowLabelComponent {
|
||||
return React.isValidElement(label);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useReducer, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useReducer } from 'react';
|
||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||
import { useAuth } from '../../../utilities/Auth';
|
||||
import withCondition from '../../withCondition';
|
||||
@@ -22,6 +22,7 @@ import { usePreferences } from '../../../utilities/Preferences';
|
||||
import { ArrayAction } from '../../../elements/ArrayAction';
|
||||
import { scrollToID } from '../../../../utilities/scrollToID';
|
||||
import HiddenInput from '../HiddenInput';
|
||||
import { RowLabel } from '../../RowLabel';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -45,6 +46,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
condition,
|
||||
initCollapsed,
|
||||
className,
|
||||
components,
|
||||
},
|
||||
} = props;
|
||||
|
||||
@@ -61,6 +63,8 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
// eslint-disable-next-line react/destructuring-assignment
|
||||
const label = props?.label ?? props?.labels?.singular;
|
||||
|
||||
const CustomRowLabel = components?.RowLabel || undefined;
|
||||
|
||||
const { preferencesKey } = useDocumentInfo();
|
||||
const { getPreference } = usePreferences();
|
||||
const { setPreference } = usePreferences();
|
||||
@@ -251,6 +255,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
>
|
||||
{rows.length > 0 && rows.map((row, i) => {
|
||||
const rowNumber = i + 1;
|
||||
const fallbackLabel = `${labels.singular} ${String(rowNumber).padStart(2, '0')}`;
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
@@ -271,7 +276,13 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
className={`${baseClass}__row`}
|
||||
key={row.id}
|
||||
dragHandleProps={providedDrag.dragHandleProps}
|
||||
header={`${labels.singular} ${rowNumber >= 10 ? rowNumber : `0${rowNumber}`}`}
|
||||
header={(
|
||||
<RowLabel
|
||||
path={`${path}.${i}`}
|
||||
label={CustomRowLabel || fallbackLabel}
|
||||
rowNumber={rowNumber}
|
||||
/>
|
||||
)}
|
||||
actions={!readOnly ? (
|
||||
<ArrayAction
|
||||
rowCount={rows.length}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.collapsible-field {
|
||||
margin: 0 0 base(2);
|
||||
|
||||
&__label {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import RenderFields from '../../RenderFields';
|
||||
import withCondition from '../../withCondition';
|
||||
import { Props } from './types';
|
||||
import { Collapsible } from '../../../elements/Collapsible';
|
||||
import toKebabCase from '../../../../../utilities/toKebabCase';
|
||||
import { usePreferences } from '../../../utilities/Preferences';
|
||||
import { DocumentPreferences } from '../../../../../preferences/types';
|
||||
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
|
||||
import FieldDescription from '../../FieldDescription';
|
||||
import { getFieldPath } from '../getFieldPath';
|
||||
import { RowLabel } from '../../RowLabel';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -33,13 +33,22 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
||||
const { getPreference, setPreference } = usePreferences();
|
||||
const { preferencesKey } = useDocumentInfo();
|
||||
const [collapsedOnMount, setCollapsedOnMount] = useState<boolean>();
|
||||
const [fieldPreferencesKey] = useState(() => `collapsible-${toKebabCase(label)}`);
|
||||
const fieldPreferencesKey = `collapsible-${indexPath.replace(/\./gi, '__')}`;
|
||||
|
||||
const onToggle = useCallback(async (newCollapsedState: boolean) => {
|
||||
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey);
|
||||
|
||||
setPreference(preferencesKey, {
|
||||
...existingPreferences,
|
||||
...path ? {
|
||||
fields: {
|
||||
...existingPreferences?.fields || {},
|
||||
[path]: {
|
||||
...existingPreferences?.fields?.[path],
|
||||
collapsed: newCollapsedState,
|
||||
},
|
||||
},
|
||||
} : {
|
||||
fields: {
|
||||
...existingPreferences?.fields || {},
|
||||
[fieldPreferencesKey]: {
|
||||
@@ -47,22 +56,28 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
||||
collapsed: newCollapsedState,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [preferencesKey, fieldPreferencesKey, getPreference, setPreference]);
|
||||
}, [preferencesKey, fieldPreferencesKey, getPreference, setPreference, path]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchInitialState = async () => {
|
||||
const preferences = await getPreference(preferencesKey);
|
||||
setCollapsedOnMount(Boolean(preferences?.fields?.[fieldPreferencesKey]?.collapsed ?? initCollapsed));
|
||||
if (preferences) {
|
||||
const initCollapsedFromPref = path ? preferences?.fields?.[path]?.collapsed : preferences?.fields?.[fieldPreferencesKey]?.collapsed;
|
||||
setCollapsedOnMount(Boolean(initCollapsedFromPref));
|
||||
} else {
|
||||
setCollapsedOnMount(typeof initCollapsed === 'boolean' ? initCollapsed : false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchInitialState();
|
||||
}, [getPreference, preferencesKey, fieldPreferencesKey, initCollapsed]);
|
||||
}, [getPreference, preferencesKey, fieldPreferencesKey, initCollapsed, path]);
|
||||
|
||||
if (typeof collapsedOnMount !== 'boolean') return null;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div id={`field-${fieldPreferencesKey}${path ? `-${path.replace(/\./gi, '__')}` : ''}`}>
|
||||
<Collapsible
|
||||
initCollapsed={collapsedOnMount}
|
||||
className={[
|
||||
@@ -70,7 +85,12 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
||||
baseClass,
|
||||
className,
|
||||
].filter(Boolean).join(' ')}
|
||||
header={<div className={`${baseClass}__label`}>{label}</div>}
|
||||
header={(
|
||||
<RowLabel
|
||||
path={path}
|
||||
label={label}
|
||||
/>
|
||||
)}
|
||||
onToggle={onToggle}
|
||||
>
|
||||
<RenderFields
|
||||
@@ -88,7 +108,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
||||
<FieldDescription
|
||||
description={description}
|
||||
/>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import joi from 'joi';
|
||||
import { componentSchema } from '../../utilities/componentSchema';
|
||||
|
||||
export const baseAdminComponentFields = joi.object().keys({
|
||||
Cell: componentSchema,
|
||||
Field: componentSchema,
|
||||
Filter: componentSchema,
|
||||
}).default({});
|
||||
|
||||
export const baseAdminFields = joi.object().keys({
|
||||
description: joi.alternatives().try(
|
||||
joi.string(),
|
||||
@@ -15,11 +21,7 @@ export const baseAdminFields = joi.object().keys({
|
||||
hidden: joi.boolean().default(false),
|
||||
disabled: joi.boolean().default(false),
|
||||
condition: joi.func(),
|
||||
components: joi.object().keys({
|
||||
Cell: componentSchema,
|
||||
Field: componentSchema,
|
||||
Filter: componentSchema,
|
||||
}).default({}),
|
||||
components: baseAdminComponentFields,
|
||||
});
|
||||
|
||||
export const baseField = joi.object().keys({
|
||||
@@ -181,7 +183,10 @@ export const row = baseField.keys({
|
||||
});
|
||||
|
||||
export const collapsible = baseField.keys({
|
||||
label: joi.string().required(),
|
||||
label: joi.alternatives().try(
|
||||
joi.string(),
|
||||
componentSchema,
|
||||
),
|
||||
type: joi.string().valid('collapsible').required(),
|
||||
fields: joi.array().items(joi.link('#field')),
|
||||
admin: baseAdminFields.default(),
|
||||
@@ -236,6 +241,11 @@ export const array = baseField.keys({
|
||||
joi.array().items(joi.object()),
|
||||
joi.func(),
|
||||
),
|
||||
admin: baseAdminFields.keys({
|
||||
components: baseAdminComponentFields.keys({
|
||||
RowLabel: componentSchema,
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
export const upload = baseField.keys({
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ConditionalDateProps } from '../../admin/components/elements/DatePicker
|
||||
import { Description } from '../../admin/components/forms/FieldDescription/types';
|
||||
import { User } from '../../auth';
|
||||
import { Payload } from '../..';
|
||||
import { RowLabel } from '../../admin/components/forms/RowLabel/types';
|
||||
|
||||
export type FieldHookArgs<T extends TypeWithID = any, P = any, S = any> = {
|
||||
/** The data passed to update the document within create and update operations, and the full document itself in the afterRead hook. */
|
||||
@@ -185,9 +186,9 @@ export type RowField = Omit<FieldBase, 'admin' | 'name'> & {
|
||||
fields: Field[];
|
||||
}
|
||||
|
||||
export type CollapsibleField = Omit<FieldBase, 'name'> & {
|
||||
export type CollapsibleField = Omit<FieldBase, 'name' | 'label'> & {
|
||||
type: 'collapsible';
|
||||
label: string
|
||||
label: RowLabel
|
||||
fields: Field[];
|
||||
admin?: Admin & {
|
||||
initCollapsed?: boolean | false;
|
||||
@@ -337,8 +338,11 @@ export type ArrayField = FieldBase & {
|
||||
fields: Field[];
|
||||
admin?: Admin & {
|
||||
initCollapsed?: boolean | false;
|
||||
}
|
||||
}
|
||||
components?: {
|
||||
RowLabel?: RowLabel
|
||||
} & Admin['components']
|
||||
};
|
||||
};
|
||||
|
||||
export type RadioField = FieldBase & {
|
||||
type: 'radio';
|
||||
|
||||
6
test/fields/collections/Array/LabelComponent.tsx
Normal file
6
test/fields/collections/Array/LabelComponent.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import { RowLabelComponent } from '../../../../src/admin/components/forms/RowLabel/types';
|
||||
|
||||
export const ArrayRowLabel: RowLabelComponent = ({ data }) => {
|
||||
return <div style={{ textTransform: 'uppercase', color: 'coral' }}>{data.title || 'Untitled'}</div>;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CollectionConfig } from '../../../../src/collections/config/types';
|
||||
import { ArrayRowLabel } from './LabelComponent';
|
||||
|
||||
export const arrayDefaultValue = [
|
||||
{ text: 'row one' },
|
||||
@@ -82,6 +83,38 @@ const ArrayFields: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'rowLabelAsFunction',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
description: 'Row labels rendered from a function.',
|
||||
components: {
|
||||
RowLabel: ({ data, fallback }) => data.title || fallback,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'rowLabelAsComponent',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
description: 'Row labels rendered as react components.',
|
||||
components: {
|
||||
RowLabel: ArrayRowLabel,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
6
test/fields/collections/Collapsible/LabelComponent.tsx
Normal file
6
test/fields/collections/Collapsible/LabelComponent.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import { RowLabelComponent } from '../../../../src/admin/components/forms/RowLabel/types';
|
||||
|
||||
export const CollapsibleLabelComponent: RowLabelComponent = ({ data }) => {
|
||||
return <div style={{ textTransform: 'uppercase', color: 'hotpink' }}>{data.innerCollapsible || 'Untitled'}</div>;
|
||||
};
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { CollectionConfig } from '../../../../src/collections/config/types';
|
||||
import { CollapsibleLabelComponent } from './LabelComponent';
|
||||
|
||||
export const collapsibleFieldsSlug = 'collapsible-fields';
|
||||
|
||||
const CollapsibleFields: CollectionConfig = {
|
||||
slug: 'collapsible-fields',
|
||||
slug: collapsibleFieldsSlug,
|
||||
versions: true,
|
||||
fields: [
|
||||
{
|
||||
@@ -73,6 +76,59 @@ const CollapsibleFields: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: ({ data }) => data.functionTitleField || 'Custom Collapsible Label',
|
||||
type: 'collapsible',
|
||||
admin: {
|
||||
description: 'Collapsible label rendered from a function.',
|
||||
initCollapsed: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'functionTitleField',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: ({ data }) => data?.componentTitleField || 'Untitled',
|
||||
type: 'collapsible',
|
||||
admin: {
|
||||
description: 'Collapsible label rendered as a react component.',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'componentTitleField',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'collapsible',
|
||||
label: ({ data }) => data?.nestedTitle || 'Nested Collapsible',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'nestedTitle',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'arrayWithCollapsibles',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
label: CollapsibleLabelComponent,
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
name: 'innerCollapsible',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -84,6 +140,11 @@ export const collapsibleDoc = {
|
||||
textWithinSubGroup: 'hello, get out',
|
||||
},
|
||||
},
|
||||
arrayWithCollapsibles: [
|
||||
{
|
||||
innerCollapsible: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default CollapsibleFields;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { textDoc } from './collections/Text';
|
||||
import { arrayFieldsSlug } from './collections/Array';
|
||||
import { pointFieldsSlug } from './collections/Point';
|
||||
import { tabsSlug } from './collections/Tabs';
|
||||
import { collapsibleFieldsSlug } from './collections/Collapsible';
|
||||
import wait from '../../src/utilities/wait';
|
||||
import { relationshipFieldsSlug } from './collections/Relationship';
|
||||
|
||||
@@ -66,6 +67,33 @@ describe('fields', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('fields - collapsible', () => {
|
||||
let url: AdminUrlUtil;
|
||||
beforeAll(() => {
|
||||
url = new AdminUrlUtil(serverURL, collapsibleFieldsSlug);
|
||||
});
|
||||
|
||||
test('should render CollapsibleLabel using a function', async () => {
|
||||
const label = 'custom row label';
|
||||
await page.goto(url.create);
|
||||
await page.locator('#field-collapsible-3__1 >> #field-nestedTitle').fill(label);
|
||||
await wait(100);
|
||||
const customCollapsibleLabel = await page.locator('#field-collapsible-3__1 >> .row-label');
|
||||
await expect(customCollapsibleLabel).toContainText(label);
|
||||
});
|
||||
|
||||
test('should render CollapsibleLabel using a component', async () => {
|
||||
const label = 'custom row label as component';
|
||||
await page.goto(url.create);
|
||||
await page.locator('#field-arrayWithCollapsibles >> .array-field__add-button-wrap >> button').click();
|
||||
|
||||
await page.locator('#field-collapsible-4__0-arrayWithCollapsibles__0 >> #field-arrayWithCollapsibles__0__innerCollapsible').fill(label);
|
||||
await wait(100);
|
||||
const customCollapsibleLabel = await page.locator(`#field-collapsible-4__0-arrayWithCollapsibles__0 >> .row-label :text("${label}")`);
|
||||
await expect(customCollapsibleLabel).toHaveCSS('text-transform', 'uppercase');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fields - array', () => {
|
||||
let url: AdminUrlUtil;
|
||||
beforeAll(() => {
|
||||
@@ -85,6 +113,28 @@ describe('fields', () => {
|
||||
await expect(field)
|
||||
.toHaveValue('defaultValue');
|
||||
});
|
||||
|
||||
test('should render RowLabel using a function', async () => {
|
||||
const label = 'custom row label as function';
|
||||
await page.goto(url.create);
|
||||
await page.locator('#field-rowLabelAsFunction >> .array-field__add-button-wrap >> button').click();
|
||||
|
||||
await page.locator('#field-rowLabelAsFunction__0__title').fill(label);
|
||||
await wait(100);
|
||||
const customRowLabel = await page.locator('#rowLabelAsFunction-row-0 >> .row-label');
|
||||
await expect(customRowLabel).toContainText(label);
|
||||
});
|
||||
|
||||
test('should render RowLabel using a component', async () => {
|
||||
const label = 'custom row label as component';
|
||||
await page.goto(url.create);
|
||||
await page.locator('#field-rowLabelAsComponent >> .array-field__add-button-wrap >> button').click();
|
||||
|
||||
await page.locator('#field-rowLabelAsComponent__0__title').fill(label);
|
||||
await wait(100);
|
||||
const customRowLabel = await page.locator('#rowLabelAsComponent-row-0 >> .row-label :text("custom row label")');
|
||||
await expect(customRowLabel).toHaveCSS('text-transform', 'uppercase');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fields - tabs', () => {
|
||||
|
||||
Reference in New Issue
Block a user