progress to typing Blocks and Arrays

This commit is contained in:
James
2020-11-30 20:12:15 -05:00
parent 6bf60ff5d6
commit bb7c829c5e
19 changed files with 155 additions and 307 deletions

View File

@@ -3,11 +3,11 @@ import React, {
} from 'react';
import { Link } from 'react-router-dom';
import Chevron from '../../icons/Chevron';
import { Context } from './types';
import { Context as ContextType } from './types';
import './index.scss';
const Context = createContext({} as Context);
const Context = createContext({} as ContextType);
const StepNavProvider: React.FC = ({ children }) => {
const [stepNav, setStepNav] = useState([]);
@@ -23,7 +23,7 @@ const StepNavProvider: React.FC = ({ children }) => {
);
};
const useStepNav = (): Context => useContext(Context);
const useStepNav = (): ContextType => useContext(Context);
const StepNav: React.FC = () => {
const dashboardLabel = <span>Dashboard</span>;

View File

@@ -1,5 +1,4 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import AnimateHeight from 'react-animate-height';
import { Draggable } from 'react-beautiful-dnd';
@@ -10,12 +9,13 @@ import Button from '../../elements/Button';
import { NegativeFieldGutterProvider } from '../FieldTypeGutter/context';
import FieldTypeGutter from '../FieldTypeGutter';
import RenderFields from '../RenderFields';
import { Props } from './types';
import './index.scss';
const baseClass = 'draggable-section';
const DraggableSection = (props) => {
const DraggableSection: React.FC<Props> = (props) => {
const {
moveRow,
addRow,
@@ -29,8 +29,8 @@ const DraggableSection = (props) => {
fieldTypes,
toggleRowCollapse,
id,
positionPanelVerticalAlignment,
actionPanelVerticalAlignment,
positionPanelVerticalAlignment = 'sticky',
actionPanelVerticalAlignment = 'sticky',
permissions,
isOpen,
readOnly,
@@ -102,7 +102,7 @@ const DraggableSection = (props) => {
readOnly={readOnly}
fieldTypes={fieldTypes}
key={rowIndex}
permissions={permissions}
permissions={permissions?.fields}
fieldSchema={fieldSchema.map((field) => ({
...field,
path: `${parentPath}.${rowIndex}${field.name ? `.${field.name}` : ''}`,
@@ -136,38 +136,4 @@ const DraggableSection = (props) => {
);
};
DraggableSection.defaultProps = {
toggleRowCollapse: undefined,
rowCount: null,
initialData: undefined,
label: '',
blockType: '',
isOpen: true,
positionPanelVerticalAlignment: 'sticky',
actionPanelVerticalAlignment: 'sticky',
permissions: {},
readOnly: false,
};
DraggableSection.propTypes = {
moveRow: PropTypes.func.isRequired,
addRow: PropTypes.func.isRequired,
removeRow: PropTypes.func.isRequired,
toggleRowCollapse: PropTypes.func,
rowIndex: PropTypes.number.isRequired,
parentPath: PropTypes.string.isRequired,
label: PropTypes.string,
fieldSchema: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
rowCount: PropTypes.number,
initialData: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.shape({})]),
isOpen: PropTypes.bool,
blockType: PropTypes.string,
fieldTypes: PropTypes.shape({}).isRequired,
id: PropTypes.string.isRequired,
positionPanelVerticalAlignment: PropTypes.oneOf(['top', 'center', 'sticky']),
actionPanelVerticalAlignment: PropTypes.oneOf(['top', 'center', 'sticky']),
permissions: PropTypes.shape({}),
readOnly: PropTypes.bool,
};
export default DraggableSection;

View File

@@ -0,0 +1,23 @@
import { Field } from '../../../../fields/config/types';
import { FieldTypes } from '../field-types';
import { FieldPermissions } from '../../../../auth/types';
export type Props = {
moveRow: (fromIndex: number, toIndex: number) => void
addRow: (index: number, blockType?: string) => void
removeRow: (index: number) => void
rowIndex: number
rowCount: number
parentPath: string
fieldSchema: Field[],
label?: string
blockType?: string
fieldTypes: FieldTypes
toggleRowCollapse?: (index: number) => void
id: string
positionPanelVerticalAlignment?: 'top' | 'center' | 'sticky'
actionPanelVerticalAlignment?: 'top' | 'center' | 'sticky'
permissions: FieldPermissions
isOpen?: boolean
readOnly: boolean
}

View File

@@ -1,11 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Props } from './types';
import './index.scss';
const Label = (props) => {
const Label: React.FC<Props> = (props) => {
const {
label, required, htmlFor,
label, required = false, htmlFor,
} = props;
if (label) {
@@ -23,18 +22,4 @@ const Label = (props) => {
return null;
};
Label.defaultProps = {
required: false,
label: '',
};
Label.propTypes = {
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
]),
htmlFor: PropTypes.string.isRequired,
required: PropTypes.bool,
};
export default Label;

View File

@@ -0,0 +1,5 @@
export type Props = {
label?: string
required?: boolean
htmlFor?: string
}

View File

@@ -1,5 +1,5 @@
import { CollectionFieldPermissions, GlobalFieldPermissions } from '../../../../auth/types';
import { FieldWithPath } from '../../../../fields/config/types';
import { FieldPermissions } from '../../../../auth/types';
import { FieldWithPath, Field } from '../../../../fields/config/types';
import { FieldTypes } from '../field-types';
export type Operation = 'create' | 'update'
@@ -12,8 +12,10 @@ export type Props = {
className?: string
operation: Operation
readOnly: boolean
permissions: CollectionFieldPermissions | GlobalFieldPermissions
filter: (field: Field) => boolean
permissions: {
[field: string]: FieldPermissions
}
filter?: (field: Field) => boolean
fieldSchema: FieldWithPath[]
fieldTypes: FieldTypes
}

View File

@@ -1,5 +1,4 @@
import React, { useEffect, useReducer, useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import withCondition from '../../withCondition';
@@ -13,23 +12,27 @@ import Error from '../../Error';
import { array } from '../../../../../fields/validations';
import getDataByPath from '../../Form/getDataByPath';
import Banner from '../../../elements/Banner';
import { Props, RenderArrayProps } from './types';
import './index.scss';
const baseClass = 'field-type array';
const ArrayFieldType = (props) => {
const ArrayFieldType: React.FC<Props> = (props) => {
const {
label,
name,
path: pathFromProps,
fields,
fieldTypes,
validate,
validate = array,
required,
maxRows,
minRows,
labels,
labels = {
singular: 'Row',
plural: 'Rows',
},
permissions,
admin: {
readOnly,
@@ -125,45 +128,7 @@ const ArrayFieldType = (props) => {
);
};
ArrayFieldType.defaultProps = {
label: undefined,
validate: array,
required: false,
maxRows: undefined,
minRows: undefined,
labels: {
singular: 'Row',
plural: 'Rows',
},
permissions: {},
admin: {},
};
ArrayFieldType.propTypes = {
fields: PropTypes.arrayOf(
PropTypes.shape({}),
).isRequired,
label: PropTypes.string,
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
name: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
fieldTypes: PropTypes.shape({}).isRequired,
validate: PropTypes.func,
required: PropTypes.bool,
maxRows: PropTypes.number,
minRows: PropTypes.number,
permissions: PropTypes.shape({
fields: PropTypes.shape({}),
}),
admin: PropTypes.shape({
readOnly: PropTypes.bool,
}),
};
const RenderArray = React.memo((props) => {
const RenderArray = React.memo((props: RenderArrayProps) => {
const {
onDragEnd,
label,
@@ -180,8 +145,6 @@ const RenderArray = React.memo((props) => {
permissions,
value,
readOnly,
style,
width,
minRows,
maxRows,
required,
@@ -191,10 +154,6 @@ const RenderArray = React.memo((props) => {
<DragDropContext onDragEnd={onDragEnd}>
<div
className={baseClass}
style={{
...style,
width,
}}
>
<header className={`${baseClass}__header`}>
<h3>{label}</h3>
@@ -223,7 +182,6 @@ const RenderArray = React.memo((props) => {
removeRow={() => removeRow(i)}
moveRow={moveRow}
parentPath={path}
initNull={row.initNull}
fieldTypes={fieldTypes}
fieldSchema={fields}
permissions={permissions.fields}
@@ -268,56 +226,4 @@ const RenderArray = React.memo((props) => {
);
});
RenderArray.defaultProps = {
label: undefined,
showError: false,
errorMessage: undefined,
rows: [],
labels: {
singular: 'Row',
plural: 'Rows',
},
path: '',
value: undefined,
readOnly: false,
style: {},
width: undefined,
maxRows: undefined,
minRows: undefined,
required: false,
};
RenderArray.propTypes = {
label: PropTypes.string,
showError: PropTypes.bool,
errorMessage: PropTypes.string,
rows: PropTypes.arrayOf(
PropTypes.shape({}),
),
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
path: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.number,
onDragEnd: PropTypes.func.isRequired,
addRow: PropTypes.func.isRequired,
removeRow: PropTypes.func.isRequired,
moveRow: PropTypes.func.isRequired,
fieldTypes: PropTypes.shape({}).isRequired,
fields: PropTypes.arrayOf(
PropTypes.shape({}),
).isRequired,
permissions: PropTypes.shape({
fields: PropTypes.shape({}),
}).isRequired,
readOnly: PropTypes.bool,
style: PropTypes.shape({}),
width: PropTypes.string,
maxRows: PropTypes.number,
minRows: PropTypes.number,
required: PropTypes.bool,
};
export default withCondition(ArrayFieldType);

View File

@@ -3,7 +3,7 @@ import Loading from '../../../elements/Loading';
const ArrayField = lazy(() => import('./Array'));
export default (props) => (
export default (props: unknown): React.ReactNode => (
<Suspense fallback={<Loading />}>
<ArrayField {...props} />
</Suspense>

View File

@@ -0,0 +1,32 @@
import { Data } from '../../Form/types';
import { ArrayField, Labels, Field } from '../../../../../fields/config/types';
import { FieldTypes } from '..';
import { FieldPermissions } from '../../../../../auth/types';
export type Props = ArrayField & {
path?: string
fieldTypes: FieldTypes
permissions: FieldPermissions
}
export type RenderArrayProps = {
path: string
name: string
fieldTypes: FieldTypes
fields: Field[]
permissions: FieldPermissions
onDragEnd: (result: any) => void
label: string
value: number
readOnly: boolean
minRows: number
maxRows: number
required: boolean
labels: Labels
addRow: (index: number) => Promise<void>
removeRow: (index: number) => void
moveRow: (fromIndex: number, toIndex: number) => void
showError: boolean
errorMessage: string
rows: Data[]
}

View File

@@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
import BlockSearch from './BlockSearch';
import BlocksContainer from './BlocksContainer';
import { Props } from './types';
const baseClass = 'block-selector';
const BlockSelector = (props) => {
const BlockSelector: React.FC<Props> = (props) => {
const {
blocks, close, parentIsHovered, watchParentHover, ...remainingProps
} = props;

View File

@@ -0,0 +1,3 @@
export type Props = {
}

View File

@@ -17,28 +17,30 @@ import BlockSelector from './BlockSelector';
import { blocks as blocksValidator } from '../../../../../fields/validations';
import getDataByPath from '../../Form/getDataByPath';
import Banner from '../../../elements/Banner';
import { Props, RenderBlockProps } from './types';
import './index.scss';
const baseClass = 'field-type blocks';
const Blocks = (props) => {
const Blocks: React.FC<Props> = (props) => {
const {
label,
name,
path: pathFromProps,
blocks,
labels,
labels = {
singular: 'Block',
plural: 'Blocks',
},
fieldTypes,
maxRows,
minRows,
required,
validate,
validate = blocksValidator,
permissions,
admin: {
readOnly,
style,
width,
},
} = props;
@@ -138,55 +140,14 @@ const Blocks = (props) => {
value={value}
blocks={blocks}
readOnly={readOnly}
style={style}
width={width}
minRows={minRows}
maxRows={maxRows}
required={required}
/>
);
};
Blocks.defaultProps = {
label: '',
labels: {
singular: 'Block',
plural: 'Blocks',
},
validate: blocksValidator,
required: false,
maxRows: undefined,
minRows: undefined,
permissions: {},
admin: {},
};
Blocks.propTypes = {
blocks: PropTypes.arrayOf(
PropTypes.shape({}),
).isRequired,
label: PropTypes.string,
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
name: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
fieldTypes: PropTypes.shape({}).isRequired,
validate: PropTypes.func,
required: PropTypes.bool,
maxRows: PropTypes.number,
minRows: PropTypes.number,
permissions: PropTypes.shape({
fields: PropTypes.shape({}),
}),
admin: PropTypes.shape({
readOnly: PropTypes.bool,
style: PropTypes.shape({}),
width: PropTypes.string,
}),
};
const RenderBlocks = React.memo((props) => {
const RenderBlocks = React.memo((props: RenderBlockProps) => {
const {
onDragEnd,
label,
@@ -204,8 +165,6 @@ const RenderBlocks = React.memo((props) => {
toggleCollapse,
blocks,
readOnly,
style,
width,
minRows,
maxRows,
required,
@@ -215,10 +174,6 @@ const RenderBlocks = React.memo((props) => {
<DragDropContext onDragEnd={onDragEnd}>
<div
className={baseClass}
style={{
...style,
width,
}}
>
<header className={`${baseClass}__header`}>
<h3>{label}</h3>
@@ -331,57 +286,4 @@ const RenderBlocks = React.memo((props) => {
);
});
RenderBlocks.defaultProps = {
label: undefined,
showError: false,
errorMessage: undefined,
rows: [],
labels: {
singular: 'Block',
plural: 'Blocks',
},
path: '',
value: undefined,
readOnly: false,
style: {},
width: undefined,
maxRows: undefined,
minRows: undefined,
required: false,
};
RenderBlocks.propTypes = {
label: PropTypes.string,
showError: PropTypes.bool,
errorMessage: PropTypes.string,
rows: PropTypes.arrayOf(
PropTypes.shape({}),
),
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
path: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.number,
onDragEnd: PropTypes.func.isRequired,
addRow: PropTypes.func.isRequired,
removeRow: PropTypes.func.isRequired,
moveRow: PropTypes.func.isRequired,
fieldTypes: PropTypes.shape({}).isRequired,
permissions: PropTypes.shape({
fields: PropTypes.shape({}),
}).isRequired,
blocks: PropTypes.arrayOf(
PropTypes.shape({}),
).isRequired,
toggleCollapse: PropTypes.func.isRequired,
readOnly: PropTypes.bool,
style: PropTypes.shape({}),
width: PropTypes.string,
maxRows: PropTypes.number,
minRows: PropTypes.number,
required: PropTypes.bool,
};
export default withCondition(Blocks);

View File

@@ -3,7 +3,7 @@ import Loading from '../../../elements/Loading';
const Blocks = lazy(() => import('./Blocks'));
export default (props) => (
export default (props: unknown): React.ReactNode => (
<Suspense fallback={<Loading />}>
<Blocks {...props} />
</Suspense>

View File

@@ -0,0 +1,33 @@
import { Data } from '../../Form/types';
import { BlockField, Labels, Block } from '../../../../../fields/config/types';
import { FieldTypes } from '..';
import { FieldPermissions } from '../../../../../auth/types';
export type Props = BlockField & {
path?: string
fieldTypes: FieldTypes
permissions: FieldPermissions
}
export type RenderBlockProps = {
path: string
name: string
fieldTypes: FieldTypes
permissions: FieldPermissions
onDragEnd: (result: any) => void
label: string
value: number
readOnly: boolean
minRows: number
maxRows: number
required: boolean
labels: Labels
addRow: (index: number, blockType: string) => Promise<void>
removeRow: (index: number) => void
moveRow: (fromIndex: number, toIndex: number) => void
showError: boolean
errorMessage: string
rows: Data[]
blocks: Block[],
toggleCollapse: (row: number) => void
}

View File

@@ -169,7 +169,6 @@ const DefaultEditView = (props) => {
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={(field) => field?.admin?.position === 'sidebar'}
position="sidebar"
fieldTypes={fieldTypes}
fieldSchema={fields}
/>

View File

@@ -27,7 +27,7 @@ const EditView = (props) => {
Edit: CustomEdit,
} = {},
} = {},
},
} = {},
fields,
} = collection;

View File

@@ -6,21 +6,17 @@ export type Permission = {
where?: Record<string, unknown>
}
export type CollectionFieldPermissions = {
[field: string]: {
create: {
permission: boolean
}
read: {
permission: boolean
}
update: {
permission: boolean
}
delete: {
permission: boolean
}
export type FieldPermissions = {
create: {
permission: boolean
}
read: {
permission: boolean
}
update: {
permission: boolean
}
fields?: FieldPermissions
}
export type CollectionPermission = {
@@ -28,24 +24,17 @@ export type CollectionPermission = {
read: Permission
update: Permission
delete: Permission
fields: CollectionFieldPermissions
}
export type GlobalFieldPermissions = {
[field: string]: {
read: {
permission: boolean
}
update: {
permission: boolean
}
fields: {
[field: string]: FieldPermissions
}
}
export type GlobalPermission = {
read: Permission
update: Permission
fields: GlobalFieldPermissions
fields: {
[field: string]: FieldPermissions
}
}
export type Permissions = {

View File

@@ -25,11 +25,13 @@ type Admin = {
hidden?: boolean
}
type Labels = {
export type Labels = {
singular: string;
plural: string;
};
export type Validate = (value: unknown, options: unknown) => string | boolean;
interface FieldBase {
name?: string;
label?: string;
@@ -41,7 +43,7 @@ interface FieldBase {
hidden?: boolean;
saveToJWT?: boolean
localized?: boolean;
validate?: (value: any, field: Field) => any;
validate?: Validate;
hooks?: {
beforeValidate?: FieldHook[];
beforeChange?: FieldHook[];