feat: specifies component names for arrays/collapsibles, simplifies threaded data

This commit is contained in:
Jarrod Flesch
2022-11-15 11:34:36 -05:00
parent 2878b4b1be
commit b74ea218ca
12 changed files with 124 additions and 74 deletions

View File

@@ -1,23 +0,0 @@
import React from 'react';
import { Props } from './types';
import { useWatchForm } from '../Form/context';
export const CollapsibleLabel: React.FC<Props> = (props) => {
const {
path,
fallback,
label,
rowNumber,
} = props;
const { getDataByPath, getSiblingData, getData } = useWatchForm();
const formData = getData();
const collapsibleData = getDataByPath(path) || getSiblingData(path);
return (
<React.Fragment>
{(typeof label === 'function')
? label({ collapsibleData, index: rowNumber, formData, fallback })
: (label || fallback)}
</React.Fragment>
);
};

View File

@@ -1,16 +0,0 @@
import React from 'react';
import { Data } from '../Form/types';
export type CollapsibleLabel = (props: {
formData: Data,
collapsibleData: Data,
index?: number,
fallback: string,
}) => React.ReactNode;
export type Props = {
fallback: string;
path: string;
label?: CollapsibleLabel;
rowNumber?: number;
}

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { isComponent, Props } from './types';
import { useWatchForm } from '../Form/context';
const baseClass = 'row-label';
export const RowLabel: React.FC<Props> = (props) => {
const {
path,
fallback,
label,
rowNumber,
className,
} = 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}
fallback={fallback}
index={rowNumber}
/>
);
}
if (label) {
return (
<span
className={[
baseClass,
className,
].filter(Boolean).join(' ')}
>
{typeof label === 'function' ? label({
data,
path,
fallback,
index: rowNumber,
}) : label}
</span>
);
}
if (fallback) {
return (
<React.Fragment>
{fallback}
</React.Fragment>
);
}
return null;
};

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { Data } from '../Form/types';
export type Props = {
fallback: string;
path: string;
label?: RowLabel;
rowNumber?: number;
className?: string,
}
export type RowLabelArgs = {
data: Data,
path: string,
index: number,
fallback: string
}
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);
}

View File

@@ -22,7 +22,7 @@ import { usePreferences } from '../../../utilities/Preferences';
import { ArrayAction } from '../../../elements/ArrayAction';
import { scrollToID } from '../../../../utilities/scrollToID';
import HiddenInput from '../HiddenInput';
import { CollapsibleLabel } from '../../CollapsibleLabel';
import { RowLabel } from '../../RowLabel';
import './index.scss';
@@ -49,7 +49,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
},
} = props;
const CollapsibleLabelFromProps = components?.CollapsibleLabel || undefined;
const RowLabelFromProps = components?.RowLabel || undefined;
const path = pathFromProps || name;
@@ -287,9 +287,9 @@ const ArrayFieldType: React.FC<Props> = (props) => {
key={row.id}
dragHandleProps={providedDrag.dragHandleProps}
header={(
<CollapsibleLabel
<RowLabel
path={`${path}.${i}`}
label={CollapsibleLabelFromProps}
label={RowLabelFromProps}
fallback={`${labels.singular} ${String(rowNumber).padStart(2, '0')}`}
rowNumber={rowNumber}
/>

View File

@@ -9,7 +9,7 @@ import { DocumentPreferences } from '../../../../../preferences/types';
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
import FieldDescription from '../../FieldDescription';
import { getFieldPath } from '../getFieldPath';
import { CollapsibleLabel } from '../../CollapsibleLabel';
import { RowLabel } from '../../RowLabel';
import './index.scss';
@@ -74,7 +74,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
className,
].filter(Boolean).join(' ')}
header={(
<CollapsibleLabel
<RowLabel
path={path}
fallback={label}
label={CollapsibleLabelFromProps}

View File

@@ -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({
@@ -185,7 +187,7 @@ export const collapsible = baseField.keys({
type: joi.string().valid('collapsible').required(),
fields: joi.array().items(joi.link('#field')),
admin: baseAdminFields.keys({
components: joi.object().keys({
components: baseAdminComponentFields.keys({
CollapsibleLabel: componentSchema,
}).default({}),
}).default({}),
@@ -241,8 +243,8 @@ export const array = baseField.keys({
joi.func(),
),
admin: baseAdminFields.keys({
components: joi.object().keys({
CollapsibleLabel: componentSchema,
components: baseAdminComponentFields.keys({
RowLabel: componentSchema,
}).default({}),
}).default({}),
});

View File

@@ -8,7 +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 { CollapsibleLabel } from '../../admin/components/forms/CollapsibleLabel/types';
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. */
@@ -193,8 +193,8 @@ export type CollapsibleField = Omit<FieldBase, 'name'> & {
admin?: Admin & {
initCollapsed?: boolean | false;
components?: {
CollapsibleLabel?: CollapsibleLabel
}
CollapsibleLabel?: RowLabel
} & Admin['components']
}
}
@@ -342,8 +342,8 @@ export type ArrayField = FieldBase & {
admin?: Admin & {
initCollapsed?: boolean | false;
components?: {
CollapsibleLabel?: CollapsibleLabel
}
RowLabel?: RowLabel
} & Admin['components']
};
};

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { CollapsibleLabel } from '../../../../src/admin/components/forms/CollapsibleLabel/types';
import { RowLabelComponent } from '../../../../src/admin/components/forms/RowLabel/types';
export const ArrayCollapsibleLabel: CollapsibleLabel = (props) => {
const { collapsibleData, fallback } = props;
return <span style={{ color: 'hotpink' }}>{collapsibleData.title || fallback}</span>;
export const ArrayRowLabel: RowLabelComponent = (props) => {
const { data, fallback } = props;
return <span style={{ color: 'hotpink' }}>{data.title || fallback}</span>;
};

View File

@@ -1,5 +1,5 @@
import type { CollectionConfig } from '../../../../src/collections/config/types';
import { ArrayCollapsibleLabel } from './HeaderComponent';
import { ArrayRowLabel } from './HeaderComponent';
export const arrayDefaultValue = [
{ text: 'row one' },
@@ -85,7 +85,7 @@ const ArrayFields: CollectionConfig = {
},
{
type: 'array',
name: 'collapsibleLabelAsFunction',
name: 'rowLabelAsFunction',
fields: [
{
name: 'title',
@@ -93,15 +93,15 @@ const ArrayFields: CollectionConfig = {
},
],
admin: {
description: 'Collapsible labels rendered from a function.',
description: 'Row labels rendered from a function.',
components: {
CollapsibleLabel: ({ collapsibleData, fallback }) => collapsibleData.title || fallback,
RowLabel: ({ data, fallback }) => data.title || fallback,
},
},
},
{
type: 'array',
name: 'rowHeaderAsComponent',
name: 'rowLabelAsComponent',
fields: [
{
name: 'title',
@@ -109,9 +109,9 @@ const ArrayFields: CollectionConfig = {
},
],
admin: {
description: 'Collapsible labels rendered as components.',
description: 'Row labels rendered as react components.',
components: {
CollapsibleLabel: ArrayCollapsibleLabel,
RowLabel: ArrayRowLabel,
},
},
},

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { CollapsibleLabel } from '../../../../src/admin/components/forms/CollapsibleLabel/types';
import { RowLabel } from '../../../../src/admin/components/forms/RowLabel/types';
export const CollapsibleLabelComponent: CollapsibleLabel = (props) => {
const { collapsibleData, fallback } = props;
return <span style={{ color: 'coral' }}>{collapsibleData.componentTitleField || fallback}</span>;
export const CollapsibleLabelComponent: RowLabel = (props) => {
const { data, fallback } = props;
return <span style={{ color: 'coral' }}>{data.componentTitleField || fallback}</span>;
};

View File

@@ -81,7 +81,7 @@ const CollapsibleFields: CollectionConfig = {
description: 'Collapsible label rendered from a function.',
initCollapsed: true,
components: {
CollapsibleLabel: ({ collapsibleData }) => collapsibleData.functionTitleField || 'Untitled',
CollapsibleLabel: ({ data }) => data.functionTitleField || 'Untitled',
},
},
fields: [