feat: simplifies collapsible label API, adds e2e tests
This commit is contained in:
@@ -4,24 +4,27 @@ import { useWatchForm } from '../Form/context';
|
||||
|
||||
const baseClass = 'row-label';
|
||||
|
||||
export const RowLabel: React.FC<Props> = (props) => {
|
||||
export const RowLabel: React.FC<Props> = ({ className, ...rest }) => {
|
||||
return (
|
||||
<span style={{
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
<span
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
className={[
|
||||
baseClass,
|
||||
className,
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
<RowLabelContent {...props} />
|
||||
<RowLabelContent {...rest} />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const RowLabelContent: React.FC<Props> = (props) => {
|
||||
const RowLabelContent: React.FC<Omit<Props, 'className'>> = (props) => {
|
||||
const {
|
||||
path,
|
||||
fallback,
|
||||
label,
|
||||
rowNumber,
|
||||
className,
|
||||
} = props;
|
||||
|
||||
const { getDataByPath, getSiblingData } = useWatchForm();
|
||||
@@ -35,37 +38,18 @@ const RowLabelContent: React.FC<Props> = (props) => {
|
||||
<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;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{typeof label === 'function' ? label({
|
||||
data,
|
||||
path,
|
||||
index: rowNumber,
|
||||
}) : label}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { Data } from '../Form/types';
|
||||
|
||||
export type Props = {
|
||||
fallback: string;
|
||||
path: string;
|
||||
label?: RowLabel;
|
||||
rowNumber?: number;
|
||||
@@ -12,8 +11,7 @@ export type Props = {
|
||||
export type RowLabelArgs = {
|
||||
data: Data,
|
||||
path: string,
|
||||
index: number,
|
||||
fallback: string
|
||||
index?: number,
|
||||
}
|
||||
|
||||
export type RowLabelFunction = (args: RowLabelArgs) => string
|
||||
|
||||
@@ -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';
|
||||
@@ -50,8 +50,6 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
},
|
||||
} = props;
|
||||
|
||||
const RowLabelFromProps = components?.RowLabel || undefined;
|
||||
|
||||
const path = pathFromProps || name;
|
||||
|
||||
// Handle labeling for Arrays, Global Arrays, and Blocks
|
||||
@@ -65,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();
|
||||
@@ -255,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
|
||||
@@ -278,8 +279,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
header={(
|
||||
<RowLabel
|
||||
path={`${path}.${i}`}
|
||||
label={RowLabelFromProps}
|
||||
fallback={`${labels.singular} ${String(rowNumber).padStart(2, '0')}`}
|
||||
label={CustomRowLabel || fallbackLabel}
|
||||
rowNumber={rowNumber}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.collapsible-field {
|
||||
margin: 0 0 base(2);
|
||||
|
||||
&__label {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ 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';
|
||||
@@ -28,45 +27,57 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
||||
className,
|
||||
initCollapsed,
|
||||
description,
|
||||
components,
|
||||
},
|
||||
} = props;
|
||||
|
||||
const CollapsibleLabelFromProps = components?.CollapsibleLabel || undefined;
|
||||
|
||||
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,
|
||||
fields: {
|
||||
...existingPreferences?.fields || {},
|
||||
[fieldPreferencesKey]: {
|
||||
...existingPreferences?.fields?.[fieldPreferencesKey],
|
||||
collapsed: newCollapsedState,
|
||||
...path ? {
|
||||
fields: {
|
||||
...existingPreferences?.fields || {},
|
||||
[path]: {
|
||||
...existingPreferences?.fields?.[path],
|
||||
collapsed: newCollapsedState,
|
||||
},
|
||||
},
|
||||
} : {
|
||||
fields: {
|
||||
...existingPreferences?.fields || {},
|
||||
[fieldPreferencesKey]: {
|
||||
...existingPreferences?.fields?.[fieldPreferencesKey],
|
||||
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 (
|
||||
<div id={fieldPreferencesKey}>
|
||||
<div id={`field-${fieldPreferencesKey}${path ? `-${path.replace(/\./gi, '__')}` : ''}`}>
|
||||
<Collapsible
|
||||
initCollapsed={collapsedOnMount}
|
||||
className={[
|
||||
@@ -77,8 +88,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
||||
header={(
|
||||
<RowLabel
|
||||
path={path}
|
||||
fallback={label}
|
||||
label={CollapsibleLabelFromProps}
|
||||
label={label}
|
||||
/>
|
||||
)}
|
||||
onToggle={onToggle}
|
||||
|
||||
@@ -183,14 +183,13 @@ 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.keys({
|
||||
components: baseAdminComponentFields.keys({
|
||||
CollapsibleLabel: componentSchema,
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
admin: baseAdminFields.default(),
|
||||
});
|
||||
|
||||
const tab = baseField.keys({
|
||||
|
||||
@@ -186,15 +186,12 @@ 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;
|
||||
components?: {
|
||||
CollapsibleLabel?: RowLabel
|
||||
} & Admin['components']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user