From ba82d363b84b780a2b58037665b7aca78b3a7b4c Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 25 Aug 2021 04:16:31 +0300 Subject: [PATCH] [fix] GH-7 ViewKind's Coll extension with ViewDescr did not work --- .vscode/launch.json | 2 +- src/DispatchCell.tsx | 20 +- src/Form.tsx | 145 ++++++++------ src/cells/AntdCellCardLayout.tsx | 6 +- src/cells/AntdCellHorizontalLayout.tsx | 23 ++- src/data-controls/DataControl.tsx | 8 +- src/data-controls/GridRenderer.tsx | 6 +- src/data-controls/TableRenderer.tsx | 5 +- src/layouts/AntdFormLayout.tsx | 18 +- src/layouts/AntdHorizontalLayout.tsx | 16 +- src/layouts/AntdVerticalLayout.tsx | 16 +- src/layouts/LayoutComponent.ts | 4 +- src/layouts/SplitPaneLayout.tsx | 10 +- src/layouts/TabsLayout.tsx | 4 +- .../reactSplitPane.d.ts => types.d.ts} | 1 + src/util/AntdModal.tsx | 8 +- src/util/ContextToProps.tsx | 183 +++++++++++------- src/util/layout.tsx | 4 +- stories/FormOverride.stories.tsx | 170 ++++++++++++++++ 19 files changed, 468 insertions(+), 181 deletions(-) rename src/{layouts/reactSplitPane.d.ts => types.d.ts} (95%) create mode 100644 stories/FormOverride.stories.tsx diff --git a/.vscode/launch.json b/.vscode/launch.json index f5ed755..e14c07b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,7 @@ "request": "launch", "name": "Launch Storybook in Chrome", "breakOnLoad": true, - "url": "http://localhost:6006/?path=/story/several-controls-treeandform-cards--empty", + "url": "http://localhost:6006/?path=/story/form-artifactformoverride--remote-data", "sourceMaps": true, "webRoot": "${workspaceFolder}", "sourceMapPathOverrides": { diff --git a/src/DispatchCell.tsx b/src/DispatchCell.tsx index bd1d477..405a63b 100644 --- a/src/DispatchCell.tsx +++ b/src/DispatchCell.tsx @@ -19,7 +19,21 @@ import { MstContext } from './MstContext'; * Dispatch renderer component for cells. */ export const DispatchCell: React.FC = React.memo( - ({ data, onMeasureChange, uri, schema, viewKindElement, viewKind, enabled, id, CKey, rowData, ...rest }) => { + ({ + data, + onMeasureChange, + uri, + schema, + viewKind, + viewKindElement, + viewDescr, + viewDescrElement, + enabled, + id, + CKey, + rowData, + ...rest + }) => { const { cells } = useContext(MstContext); const renderer = maxBy(cells, (r) => r.tester(viewKindElement, schema)); if (renderer === undefined || renderer.tester(viewKindElement, schema) === -1) { @@ -42,10 +56,12 @@ export const DispatchCell: React.FC = React.memo( rowData={rowData} onMeasureChange={onMeasureChange} schema={schema} + viewKind={viewKind} viewKindElement={viewKindElement} + viewDescr={viewDescr} + viewDescrElement={viewDescrElement} uri={uri} enabled={enabled} - viewKind={viewKind} id={id} {...rest} /> diff --git a/src/Form.tsx b/src/Form.tsx index 406c76d..d506cdb 100644 --- a/src/Form.tsx +++ b/src/Form.tsx @@ -7,19 +7,20 @@ * * SPDX-License-Identifier: GPL-3.0-only ********************************************************************************/ +import uuid62 from 'uuid62'; import { maxBy } from 'lodash-es'; import React, { useContext } from 'react'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { Spin } from 'antd'; import { observer } from 'mobx-react-lite'; -import { applySnapshot, getSnapshot } from 'mobx-state-tree'; +import { getSnapshot } from 'mobx-state-tree'; import { JsonSchema7 } from './models/jsonSchema7'; //import ModalAntd from './antd/util/AntdModal'; import { MstContext } from './MstContext'; import { UnknownRenderer } from './UnknownRenderer'; import { RankedTester } from './testers'; -import { IViewDescr, IViewKind, IViewKindElement } from './models/uischema'; +import { IViewDescr, IViewDescrElement, IViewKind, IViewKindElement } from './models/uischema'; export interface ControlComponent { data: any; @@ -58,18 +59,16 @@ export interface FormsInitStateProps { export interface FormsDispatchProps { viewKind: IViewKind; viewKindElement: IViewKindElement; - viewDescr?: IViewDescr; - viewDescrElement?: IViewKindElement; + viewDescr: IViewDescr; + enabled?: boolean; form?: string; } -export interface FormDispatchProps extends FormsDispatchProps { - schema?: any; - uri?: string; -} export interface RenderProps extends FormsDispatchProps { - schema: JsonSchema7; + viewDescrElement?: IViewDescrElement; + id: string; + schema: JsonSchema7; } export interface RenderCellProps extends RenderProps { data: any; @@ -84,64 +83,82 @@ export interface DispatchCellProps extends RenderProps { [key: string]: any; } -export const FormsDispatch: React.FC = observer( - ({ viewKind, viewKindElement, viewDescr, viewDescrElement, form, uri, enabled }) => { - const { store, renderers } = useContext(MstContext); +export function createViewDescrElementIri(viewKindElementIri: string): string { + return viewKindElementIri + '_' + uuid62.v4(); +} - // if ViewElement extend-override exists - if (!viewDescrElement && viewDescr) { - viewDescrElement = viewDescr.elements?.find((el) => el['@parent'] === viewKindElement['@id']); - } +export const processViewKindOverride = ( + props: { viewKindElement: IViewKindElement; viewDescr: IViewDescr }, + store: any, +): [string, string, string, string, IViewKindElement, IViewDescrElement | undefined] => { + const { viewKindElement, viewDescr } = props; + // if ViewElement extend-override exists + const viewDescrElement = viewDescr.elements?.find((el) => el['@parent'] === viewKindElement['@id']); + const id = viewDescrElement ? viewDescrElement['@id'] : createViewDescrElementIri(viewKindElement['@id']); + + const [collIri, inCollPath] = viewKindElement.resultsScope?.split('/') || []; + let collIriOverride: string = collIri; - const shapes = viewKindElement.resultsScope ? viewKindElement.resultsScope.split('/') : []; - let collIri = shapes.length === 2 ? shapes[0] : viewKindElement.resultsScope; - let schema: any; - if (collIri) { - // if CollConstr extend-override exists switch to extCollConstr - if (viewDescr && viewDescr.collsConstrs) { - const extCollConstr = viewDescr.collsConstrs?.find((el) => el['@parent'] === viewKindElement['@id']); - if (extCollConstr) collIri = extCollConstr['@id']; + if (collIriOverride) { + // if CollConstr extend-override exists switch to extCollConstr + if (viewDescr.collsConstrs) { + const extCollConstr = viewDescr.collsConstrs?.find((el) => el['@parent'] === collIri); + if (extCollConstr) { + collIriOverride = extCollConstr['@id'] || ''; } - const coll = store.getColl(collIri); - schema = coll?.collConstr.entConstrs[0]?.schemaJs; - //if (store.schemas[iri]) { - // schema = store.schemas[iri]; - //} else { - // if (iri !== 'client:views' && iri !== 'rm:viewPick' && iri !== 'data:Tabs') { - //store.getSchemaByUri(iri); - // return ; - // } - //} - if (!schema) return ; } - schema = shapes.length === 2 ? schema.properties[shapes[1]] : schema; - - const id = uri ? /*createId(uri)*/ uri : ''; - const renderer = maxBy(renderers, (r) => r.tester(viewKindElement, schema)); - //const isModal = viewKindElement.options && viewKindElement.options.modal; - if (renderer === undefined || renderer.tester(viewKindElement, schema) === -1) { - return ( - - ); - } else { - const Render: React.FC = renderer.renderer; - return ( - {}}> - - - ); - } - }, -); + } + + return [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement]; +}; + +export const FormsDispatch = observer((props) => { + const { store, renderers } = useContext(MstContext); + const { viewKind, viewDescr, form, enabled } = props; + + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); + + let schema: any; + if (collIri !== collIriOverride) { + const parentColl = collIri ? store.getColl(collIri) : undefined; + const parentSchema = parentColl?.collConstr.entConstrs[0]?.schemaJs; + const childColl = collIriOverride ? store.getColl(collIriOverride) : undefined; + const childSchema = childColl?.collConstr.entConstrs[0]?.schemaJs; + schema = childSchema ? childSchema : parentSchema; + } else { + const coll = collIri ? store.getColl(collIri) : undefined; + schema = coll?.collConstr.entConstrs[0]?.schemaJs; + } + if ((collIri || collIriOverride) && !schema) return ; + if (inCollPath && inCollPath.length > 0) schema = schema.properties[inCollPath]; + + const renderer = maxBy(renderers, (r) => r.tester(viewKindElement, schema)); + //const isModal = viewKindElement.options && viewKindElement.options.modal; + if (renderer === undefined || renderer.tester(viewKindElement, schema) === -1) { + return ( + + ); + } else { + const Render: React.FC = renderer.renderer; + return ( + {}}> + + + ); + } +}); export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { return ( @@ -153,7 +170,7 @@ export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { ); } -export const Form: React.FC = observer((props) => { +export const Form = observer((props) => { const { store } = useContext(MstContext); if (!store) { console.log('!store', store); diff --git a/src/cells/AntdCellCardLayout.tsx b/src/cells/AntdCellCardLayout.tsx index 29ba978..76bbf73 100644 --- a/src/cells/AntdCellCardLayout.tsx +++ b/src/cells/AntdCellCardLayout.tsx @@ -18,7 +18,7 @@ import { DispatchCell } from '../DispatchCell'; import './cell.css'; export const AntdCellCardLayout = (props: any) => { - const { viewKindElement, viewKind, schema, data, id } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement, schema, data, id } = props; const createCardChilds = () => viewKindElement.elements ? viewKindElement.elements.map((e: IViewKindElement, idx: number) => { @@ -28,10 +28,12 @@ export const AntdCellCardLayout = (props: any) => { id={id + String(idx)} key={id + String(idx)} viewKind={viewKind} + viewKindElement={e} + viewDescr={viewDescr} + viewDescrElement={viewDescrElement} data={data} rowData={data} schema={newSchema || schema} - viewKindElement={e} /> ); }) diff --git a/src/cells/AntdCellHorizontalLayout.tsx b/src/cells/AntdCellHorizontalLayout.tsx index 3bfcfc5..624feea 100644 --- a/src/cells/AntdCellHorizontalLayout.tsx +++ b/src/cells/AntdCellHorizontalLayout.tsx @@ -19,8 +19,10 @@ import { get } from 'lodash-es'; import { Idx } from '../util/layout'; export const AntdCellHorizontalLayoutRenderer: React.FC = ({ - viewKindElement, viewKind, + viewKindElement, + viewDescr, + viewDescrElement, data, schema, }) => { @@ -28,8 +30,10 @@ export const AntdCellHorizontalLayoutRenderer: React.FC = ({ const Render: React.FC = ({ idx, schema, - viewKindElement, viewKind, + viewKindElement, + viewDescr, + viewDescrElement, data, enabled, form, @@ -46,8 +50,10 @@ export const AntdCellHorizontalLayoutRenderer: React.FC = ({ = ({ return ( {(viewKindElement.elements || []).map((e: IViewKindElement, idx: number) => ( - + ))} ); diff --git a/src/data-controls/DataControl.tsx b/src/data-controls/DataControl.tsx index 93e54b6..cf3b7f0 100644 --- a/src/data-controls/DataControl.tsx +++ b/src/data-controls/DataControl.tsx @@ -23,11 +23,13 @@ const renderType: any = { export const AntdDataLayout: React.FC = (props) => { const { + viewKind, viewKindElement, + viewDescr, + viewDescrElement, enabled, handleChange = () => {}, dataSource, - viewKind, schema, editing, getData, @@ -50,8 +52,10 @@ export const AntdDataLayout: React.FC = (props) => { child={data} editing={editing} onDnD={onDnD} - viewKindElement={viewKindElement} viewKind={viewKind} + viewKindElement={viewKindElement} + viewDescr={viewDescr} + viewDescrElement={viewDescrElement} onCreateFolder={onCreateFolder} onDeleteFolder={onDeleteFolder} onRename={onRename} diff --git a/src/data-controls/GridRenderer.tsx b/src/data-controls/GridRenderer.tsx index 89d7480..e6c73e6 100644 --- a/src/data-controls/GridRenderer.tsx +++ b/src/data-controls/GridRenderer.tsx @@ -16,7 +16,7 @@ import { DispatchCell } from '../DispatchCell'; import './styles.css'; export const GridRenderer: React.FC = (props) => { - const { child, viewKindElement, viewKind, schema } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement, child, schema } = props; const grid = viewKindElement?.options?.grid || { gutter: 16, column: 4 }; const template = viewKindElement?.options?.elementTemplate || null; const createCell = (data: any, id: string | number) => @@ -25,8 +25,10 @@ export const GridRenderer: React.FC = (props) => { = React.memo( (props) => { - const { schema, enabled, child, onSelect, viewKindElement, viewKind, editing } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement, schema, enabled, child, onSelect, editing } = props; const [selected, setSelected] = useState(child[0]); const [cacheSelect, setCacheSelect] = useState(); const [dataSource, setDataSource] = useState(child); @@ -80,9 +80,10 @@ export const TableRenderer: React.FC = React.memo( {selected.viewKindElement || viewKindElement.elements ? (
) : null} diff --git a/src/layouts/AntdFormLayout.tsx b/src/layouts/AntdFormLayout.tsx index 686b67f..5315555 100644 --- a/src/layouts/AntdFormLayout.tsx +++ b/src/layouts/AntdFormLayout.tsx @@ -74,12 +74,14 @@ export const LogicalButton: React.FC = observer(({ form, onCancel, onS }); export const AntdFormLayout: React.FC = ({ - viewKindElement, viewKind, + viewKindElement, + viewDescr, + viewDescrElement, enabled, title, visible, - formId, + id, validation, editable, onSave, @@ -92,15 +94,17 @@ export const AntdFormLayout: React.FC = ({ {({ width, height }: any) => (
onEdit()}> {title} - +
diff --git a/src/layouts/AntdHorizontalLayout.tsx b/src/layouts/AntdHorizontalLayout.tsx index 7606683..3ce4430 100644 --- a/src/layouts/AntdHorizontalLayout.tsx +++ b/src/layouts/AntdHorizontalLayout.tsx @@ -19,20 +19,28 @@ import { Idx } from '../util/layout'; import { LayoutComponent } from './LayoutComponent'; export const AntdHorizontalLayoutRenderer: React.FC = ({ - viewKindElement, viewKind, + viewKindElement, + viewDescr, + viewDescrElement, enabled, visible, }) => { //const layout = viewKindElement as Layout; - const Render: React.FC = ({ idx, viewKindElement, viewKind, enabled, form }) => { + const Render: React.FC = ({ idx, viewKind, viewKindElement, viewDescr, enabled, form }) => { const options = viewKindElement.options || {}; const style: any = options.style; const span = options.contentSize || !viewKindElement.elements ? undefined : Math.ceil(24 / viewKindElement.elements.length); return ( - + ); }; @@ -41,7 +49,7 @@ export const AntdHorizontalLayoutRenderer: React.FC = ({ if (viewKindElement.options && viewKindElement.options.width === 'all-empty-space') rowStyle.width = '100%'; return ( - {renderLayoutElements({ viewKindElement, viewKind, enabled, Render })} + {renderLayoutElements({ viewKind, viewKindElement, viewDescr, enabled, Render })} ); }; diff --git a/src/layouts/AntdVerticalLayout.tsx b/src/layouts/AntdVerticalLayout.tsx index 5d4a345..3693571 100644 --- a/src/layouts/AntdVerticalLayout.tsx +++ b/src/layouts/AntdVerticalLayout.tsx @@ -19,13 +19,15 @@ import { Idx } from '../util/layout'; import { LayoutComponent } from './LayoutComponent'; export const AntdVerticalLayoutRenderer: React.FC = ({ - viewKindElement, viewKind, + viewKindElement, + viewDescr, + viewDescrElement, enabled, visible, form, }) => { - const Render: React.FC = ({ idx, viewKindElement, viewKind, enabled }) => { + const Render: React.FC = ({ idx, viewKind, viewKindElement, viewDescr, enabled }) => { const options = viewKindElement.options || {}; const style: any = options.style; return ( @@ -35,7 +37,13 @@ export const AntdVerticalLayoutRenderer: React.FC = ({ flex: viewKindElement.options && viewKindElement.options.height === 'all-empty-space' ? '1 1 auto' : '', }}> - + ); @@ -43,7 +51,7 @@ export const AntdVerticalLayoutRenderer: React.FC = ({ return (
- {renderLayoutElements({ viewKindElement, viewKind, enabled, Render })} + {renderLayoutElements({ viewKind, viewKindElement, viewDescr, enabled, Render })}
); diff --git a/src/layouts/LayoutComponent.ts b/src/layouts/LayoutComponent.ts index 1e1dd18..732bbe5 100644 --- a/src/layouts/LayoutComponent.ts +++ b/src/layouts/LayoutComponent.ts @@ -7,8 +7,8 @@ * * SPDX-License-Identifier: GPL-3.0-only ********************************************************************************/ -import { FormsDispatchProps } from '../Form'; +import { RenderProps } from '../Form'; -export interface LayoutComponent extends FormsDispatchProps { +export interface LayoutComponent extends RenderProps { visible: boolean; } diff --git a/src/layouts/SplitPaneLayout.tsx b/src/layouts/SplitPaneLayout.tsx index c95e262..6af40d1 100644 --- a/src/layouts/SplitPaneLayout.tsx +++ b/src/layouts/SplitPaneLayout.tsx @@ -26,7 +26,7 @@ const divStyle: React.CSSProperties = { margin: '1px', }; -const renderSplitElements = ({ viewDescr, viewKindElement, viewKind, enabled, Render, form }: RenderLayoutProps) => { +const renderSplitElements = ({ viewKind, viewKindElement, viewDescr, enabled, Render, form }: RenderLayoutProps) => { const elements = viewKindElement.elements; const defaultSize = viewKindElement.options && viewKindElement.options.defaultSize; return elements ? ( @@ -36,7 +36,7 @@ const renderSplitElements = ({ viewDescr, viewKindElement, viewKind, enabled, Re return (
- +
); @@ -55,17 +55,17 @@ export const SplitPaneLayoutRenderer: React.FC = ({ visible, }) => { //const layout = viewKindElement as Layout; - const Render: React.FC = ({ idx, viewKindElement, viewKind, enabled }) => { + const Render: React.FC = ({ idx, viewKind, viewKindElement, viewDescr, enabled }) => { return (
- +
); }; return ( - {renderSplitElements({ viewDescr, viewKindElement, viewKind, enabled, Render })} + {renderSplitElements({ viewKind, viewKindElement, viewDescr, enabled, Render })} ); diff --git a/src/layouts/TabsLayout.tsx b/src/layouts/TabsLayout.tsx index c69c29c..1d0bbcf 100644 --- a/src/layouts/TabsLayout.tsx +++ b/src/layouts/TabsLayout.tsx @@ -15,14 +15,14 @@ import { rankWith, RankedTester, uiTypeIs } from '../testers'; import { withLayoutProps } from '../util/ContextToProps'; export const TabsLayout: React.FC = (props) => { - const { enabled, onSelect = () => {}, viewKindElement, viewKind } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement, enabled, onSelect = () => {} } = props; const elements = viewKindElement.elements; const viewTabs = elements ? elements.map((e: any, index: number) => { const title = e.options && e.options.title; return ( - + ); }) diff --git a/src/layouts/reactSplitPane.d.ts b/src/types.d.ts similarity index 95% rename from src/layouts/reactSplitPane.d.ts rename to src/types.d.ts index 2244c29..b519f59 100644 --- a/src/layouts/reactSplitPane.d.ts +++ b/src/types.d.ts @@ -7,4 +7,5 @@ * * SPDX-License-Identifier: GPL-3.0-only ********************************************************************************/ +declare module 'uuid62'; declare module 'react-split-pane/lib/Pane'; diff --git a/src/util/AntdModal.tsx b/src/util/AntdModal.tsx index f48e4a5..52b67e6 100644 --- a/src/util/AntdModal.tsx +++ b/src/util/AntdModal.tsx @@ -15,7 +15,7 @@ import { SaveReqDialog } from './OnSaveDialog'; import { MstContext } from '../MstContext'; export const AntdModal: React.FC = observer( - ({ id, schema, viewKindElement, enabled, viewKind, cells, childrenId, Render }) => { + ({ viewKind, viewKindElement, viewDescr, viewDescrElement, id, schema, enabled, cells, childrenId, Render }) => { const [visible, setVisible] = useState(false); const { store } = useContext(MstContext); @@ -42,10 +42,12 @@ export const AntdModal: React.FC = observer( width={1200} okText='Сохранить'> diff --git a/src/util/ContextToProps.tsx b/src/util/ContextToProps.tsx index 3e93e03..62ecb36 100644 --- a/src/util/ContextToProps.tsx +++ b/src/util/ContextToProps.tsx @@ -17,7 +17,7 @@ import { observer } from 'mobx-react-lite'; import { createLabelDescriptionFrom } from './label'; import { LayoutComponent } from '../layouts/LayoutComponent'; import { IViewKindElement, IViewKind } from '../models/uischema'; -import { ControlComponent, RenderProps } from '../Form'; +import { ControlComponent, processViewKindOverride, RenderProps } from '../Form'; //import { FilterType } from '../complex/Query'; import { validators } from '../validation'; import { MstContext } from '../MstContext'; @@ -49,28 +49,32 @@ export interface ButtonComponent { export const withStoreToControlProps = (Component: React.FC): React.FC => observer((props) => { + const { store } = useContext(MstContext); const successValidation = { validateStatus: 'success', }; - const { form, viewKindElement } = props; - const id = viewKindElement.resultsScope; const [validateObj, setValidateObj] = useState<{ validateStatus: string; help?: string; }>(successValidation); - const [req] = id?.split('/') || []; - const [testReq, testUri] = viewKindElement.resultsScope?.split('/') || []; - const { store } = useContext(MstContext); + + const { form } = props; const controlProps = mapStateToControlProps(props); - //const custom = viewKind.properties && viewKind.properties[req] ? viewKind.properties[req].customReq : undefined; - //custom ? store.loadData(req, custom.req) : store.loadData(testReq); - const coll = store.getColl(testReq); - let data = coll?.data; - if (!data || data.length === 0) { + + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); + + const coll = collIriOverride ? store.getColl(collIriOverride) : undefined; + let collData = coll?.data; + if (collData) collData = getSnapshot(collData); + + if (!collData || collData.length === 0) { return ; } - data = getSnapshot(data); - data = data[0]; + + const data = collData[0]; const onValidate = (data: any) => { if (viewKindElement.options && Array.isArray(viewKindElement.options.validation)) { const validation = viewKindElement.options.validation; @@ -91,9 +95,9 @@ export const withStoreToControlProps = (Component: React.FC): return ( {}} @@ -108,7 +112,7 @@ export const withStoreToControlProps = (Component: React.FC): }); export const withStoreToFormProps = (Component: React.FC): React.FC => - observer(({ viewKindElement, viewKind, enabled, form }) => { + observer(({ viewKind, viewKindElement, viewDescr, viewDescrElement, enabled, form }) => { if (!viewKind['@id']) { return null; } @@ -119,10 +123,12 @@ export const withStoreToFormProps = (Component: React.FC): React.FC store.onSaveFormData(id)} @@ -137,22 +143,32 @@ export const withStoreToFormProps = (Component: React.FC): React.FC observer(({ ...props }: any) => { - const { viewKindElement, viewKind } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement } = props; const { store } = useContext(MstContext); const scope = viewKindElement.resultsScope; if (!store.getSelectedDataJs(scope)) { return ; } //const id = store.getSelectedDataJs(scope).type; - return ; + return ( + + ); }); export const withStoreToViewProps = (Component: any): any => observer(({ ...props }: any) => { - const { viewKind, viewKindElement } = props; + const { viewKind, viewDescr } = props; const { store } = useContext(MstContext); - const scope = viewKindElement.resultsScope; - const coll = store.getColl(scope); + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); + const coll = store.getColl(collIriOverride); let data = coll?.data; if (!data) { //if (scope === 'rm:dataModelView') { @@ -166,7 +182,7 @@ export const withStoreToViewProps = (Component: any): any => //} //const id = store.getSelectedDataJs(scope)['@type']; //const selection = getSnapshot(store.selectedData); - const newView = store.getSelectedDataJs(scope); + const newView = store.getSelectedDataJs(collIriOverride); if (!newView) { return ; } @@ -174,10 +190,12 @@ export const withStoreToViewProps = (Component: any): any => console.log('withStoreToViewProps', { viewKind, viewKindElement, newView, newViewElement }); return ( store.setEditing(viewKindElement.resultsScope, state)} + viewKindElement={newViewElement} + viewDescr={viewDescr} + viewDescrElement={viewDescrElement} + onChange={(state: boolean) => store.setEditing(id, state)} /> ); }); @@ -227,16 +245,13 @@ export const withStoreToCellProps = (Component: React.FC): React.FC => export const withStoreToDataControlProps = (Component: any): any => observer(({ ...props }: any) => { - const { viewKindElement, viewKind } = props; + const { viewKind, viewDescr } = props; const { store } = useContext(MstContext); - //if (viewKindElement.resultsScope && !store.saveLogicTree[viewKindElement.resultsScope]) { - // store.setSaveLogic(viewKindElement.resultsScope); - //} - const custom = viewKind[viewKindElement.resultsScope.split('/')[0]] - ? viewKind[viewKind.resultsScope.split('/')[0]].customReq - : undefined; - const scope = custom ? custom : viewKindElement.resultsScope; - const coll = store.getColl(scope); + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); + const coll = store.getColl(collIriOverride); let data = coll?.data; if (!data || data.length === 0) { //if (store.data[scope] === undefined) { @@ -244,7 +259,7 @@ export const withStoreToDataControlProps = (Component: any): any => return ; } data = cloneDeep(getSnapshot(data)); - const options = viewKindElement.options || {}; + const options = viewKindElement?.options || {}; const withConnections = options.connections; const onChange = (data: any) => { /*if (data) { @@ -258,31 +273,33 @@ export const withStoreToDataControlProps = (Component: any): any => }*/ }; const getData = (parentId: string) => { - const conditions = { ...store.queries[scope].shapes[0].conditions, parent: parentId }; - const newQuery = cloneDeep(store.queries[scope]); + const conditions = { ...store.queries[collIriOverride].shapes[0].conditions, parent: parentId }; + const newQuery = cloneDeep(store.queries[collIriOverride]); newQuery.shapes[0].conditions = conditions; return store.getDataByQuery(newQuery); }; const onDnD = ({ childId, parentId }: any) => { - store.updateObjectData({ parent: parentId }, scope, childId); + store.updateObjectData({ parent: parentId }, collIriOverride, childId); }; const onCreateFolder = (data: any) => { - return store.onCreateObject(data, scope); + return store.onCreateObject(data, collIriOverride); }; const onDeleteFolder = (id: any) => { if (id) { - return store.onDeleteObject(id, scope); + return store.onDeleteObject(id, collIriOverride); } }; const onRename = (newTitle: string, id: any) => { - store.updateObjectData({ title: newTitle }, scope, id); + store.updateObjectData({ title: newTitle }, collIriOverride, id); }; return ( export const withStoreToSelectControlProps = (Component: any): any => observer(({ ...props }: any) => { - const { viewKindElement, viewKind } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement } = props; const { store } = useContext(MstContext); const id = viewKind['@id']; const scope = viewKindElement.resultsScope; @@ -322,6 +339,8 @@ export const withStoreToSelectControlProps = (Component: any): any => export const withStoreToTabProps = (Component: any): any => observer(({ ...props }: any) => { - const { schema, viewKindElement, viewKind } = props; + const { schema, viewKind } = props; const { store } = useContext(MstContext); //if (viewKindElement.resultsScope && !store.saveLogicTree[viewKindElement.resultsScope]) { // store.setSaveLogic(viewKindElement.resultsScope); //} + + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); const options = viewKindElement.options || {}; - const custom = viewKind[viewKindElement.resultsScope.split('/')[0]] - ? viewKind[viewKindElement.resultsScope.split('/')[0]].customReq - : undefined; - const scope = custom ? custom : viewKindElement.resultsScope; - const coll = store.getColl(scope); + + const coll = store.getColl(collIriOverride); let data = coll?.data; if (!data) { return ; @@ -349,7 +370,7 @@ export const withStoreToTabProps = (Component: any): any => data = getSnapshot(data); const withConnections = options.connections; const onChange = (data: any) => { - store.setSelectedData(scope, data); + store.setSelectedData(collIriOverride, data); if (withConnections) { store.editConn(withConnections, data['@id']); // options.connections.forEach((e: any) => { @@ -359,22 +380,24 @@ export const withStoreToTabProps = (Component: any): any => // }); } }; - return ; + return ; }); export const withStoreToMenuProps = (Component: any): any => observer(({ ...props }: any) => { - const { schema, viewKindElement, viewKind } = props; + const { schema, viewKind, viewDescr } = props; const { store } = useContext(MstContext); //if (viewKindElement.resultsScope && !store.saveLogicTree[viewKindElement.resultsScope]) { // store.setSaveLogic(viewKindElement.resultsScope); //} + + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); const options = viewKindElement.options || {}; - const custom = viewKind[viewKindElement.resultsScope.split('/')[0]] - ? viewKind[viewKindElement.resultsScope.split('/')[0]].customReq - : undefined; - const scope = custom ? custom : viewKindElement.resultsScope; - const coll = store.getColl(scope); + + const coll = store.getColl(collIriOverride); let data = coll?.data; if (!data) { return ; @@ -387,9 +410,11 @@ export const withStoreToMenuProps = (Component: any): any => } schema={schema} viewKind={viewKind} - uri={scope} + viewDescr={viewDescr} + viewDescrElement={viewDescrElement} + uri={id} tabs={data} - handleChange={(data: JsObject) => store.setSelectedData(scope, data)} + handleChange={(data: JsObject) => store.setSelectedData(collIriOverride, data)} options={options} setModalVisible={(uri: string, state: boolean) => store.setModalVisible(uri, state)} /> @@ -398,25 +423,35 @@ export const withStoreToMenuProps = (Component: any): any => export const withStoreToCollapseProps = (Component: any): any => observer(({ ...props }: any) => { - const { viewKindElement, viewKind } = props; + const { viewKind, viewKindElement, viewDescr, viewDescrElement } = props; const options = viewKindElement.options || {}; - return ; + return ( + + ); }); export const withStoreToArrayProps = (Component: any): any => observer(({ ...props }: any) => { - const { schema, viewKindElement, viewKind } = props; + const { viewKind, viewDescr, schema } = props; const { store } = useContext(MstContext); //if (viewKindElement.resultsScope && !store.saveLogicTree[viewKindElement.resultsScope]) { // store.setSaveLogic(viewKindElement.resultsScope); //} + + const [id, collIri, collIriOverride, inCollPath, viewKindElement, viewDescrElement] = processViewKindOverride( + props, + store, + ); const options = viewKindElement.options || {}; - const custom = viewKind[viewKindElement.resultsScope.split('/')[0]] - ? viewKind[viewKindElement.resultsScope.split('/')[0]].customReq - : undefined; - const scope = custom ? custom : viewKindElement.resultsScope; - const coll = store.getColl(scope); + + const coll = store.getColl(collIriOverride); let data = coll?.data; if (!data) { return ; @@ -446,7 +481,7 @@ export const withStoreToArrayProps = (Component: any): any => limit={10 /*store.queries[viewKindElement.resultsScope].limit*/} loadExpandedData={loadExpandedData} sortDir={{} /*store.queries[scope].orderBy*/} - uri={scope} + uri={id} loadMoreData={loadMoreData} onSort={(property: string, sortDir: any) => { /*store.onSort(scope, property, sortDir)*/ @@ -459,7 +494,7 @@ export const withStoreToArrayProps = (Component: any): any => }); export const withLayoutProps = (Component: React.FC): React.FC => - observer(({ viewKindElement, viewKind, viewDescr, viewDescrElement, enabled, form }) => { + observer(({ viewKind, viewKindElement, viewDescr, viewDescrElement, schema, enabled, form }) => { const id = viewKindElement['@id'] || ''; const enabledLayout = enabled && checkProperty('editable', id, viewKindElement, viewKind); const visible = checkProperty('visible', id, viewKindElement, viewKind); @@ -469,10 +504,12 @@ export const withLayoutProps = (Component: React.FC): React.FC< } return ( ; } -export const renderLayoutElements = ({ viewKindElement, viewKind, enabled, Render }: RenderLayoutProps) => { +export const renderLayoutElements = ({ viewKind, viewKindElement, viewDescr, enabled, Render }: RenderLayoutProps) => { const elements = viewKindElement.elements; //const id = viewKind['@id']; //const sort = id ? viewKind.properties && viewKind.properties[id] && viewKind.properties[id].order : undefined; if (!elements || elements.length === 0) return <>; return elements.map((el: IViewKindElement, idx: number) => ( - + )); }; diff --git a/stories/FormOverride.stories.tsx b/stories/FormOverride.stories.tsx new file mode 100644 index 0000000..04d4fac --- /dev/null +++ b/stories/FormOverride.stories.tsx @@ -0,0 +1,170 @@ +/******************************************************************************** + * Copyright (c) 2020 Agentlab and others. + * + * This program and the accompanying materials are made available under the + * terms of the GNU General Public License v. 3.0 which is available at + * https://www.gnu.org/licenses/gpl-3.0.html. + * + * SPDX-License-Identifier: GPL-3.0-only + ********************************************************************************/ +import moment from 'moment'; +import React from 'react'; +import { Meta, Story } from '@storybook/react'; + +import { Provider } from 'react-redux'; +import { asReduxStore, connectReduxDevtools } from 'mst-middlewares'; +import { SparqlClientImpl, rootModelInitialState, CollState } from '@agentlab/sparql-jsld-client'; + +import { + RendererRegistryEntry, + MstContextProvider, + Form, + antdCells, + antdControlRenderers, + antdLayoutRenderers, +} from '../src'; +import { viewKindCollConstr, viewDescrCollConstr } from '../src/models/ViewCollConstrs'; +import { createUiModelFromState } from '../src/models/MstViewDescr'; + +const antdRenderers: RendererRegistryEntry[] = [...antdControlRenderers, ...antdLayoutRenderers]; + +const viewKinds = [ + { + '@id': 'rm:FormViewKind', + '@type': 'aldkg:ViewKind', + title: 'Малая форма', + description: 'Small form', + + collsConstrs: [ + { + '@id': 'rm:FormView_Artifacts_Coll', + '@type': 'aldkg:CollConstr', + entConstrs: [ + { + '@id': 'rm:FormView_Artifacts_Coll_Ent0', + '@type': 'aldkg:EntConstr', + schema: 'rm:ArtifactShape', + }, + ], + //orderBy: [{ expression: variable('identifier0'), descending: false }], + }, + ], + elements: [ + { + '@id': 'rm:_83hd7f', + '@type': 'aldkg:FormLayout', + elements: [ + { + '@id': 'rm:_17Gj78', + '@type': 'aldkg:Control', + resultsScope: 'rm:FormView_Artifacts_Coll/creator', + }, + { + '@id': 'rm:_297Hgf56', + '@type': 'aldkg:Control', + resultsScope: 'rm:FormView_Artifacts_Coll/assetFolder', + }, + { + '@id': 'rm:_934jHd67', + '@type': 'aldkg:Control', + resultsScope: 'rm:FormView_Artifacts_Coll/description', + options: { + validation: [ + { + validator: 'RegExp', + propsToValidator: { + regExp: 'bo*', + }, + validateStatus: 'error', + help: 'Работает', + }, + ], + }, + }, + ], + }, + ], + }, +]; + +const viewDescrs = [ + { + '@id': 'rm:FormViewDescr', + '@type': 'aldkg:ViewDescr', + viewKind: 'rm:FormViewKind', + title: 'CardCellGrid', + description: 'CardCellGrid', + collsConstrs: [ + { + '@id': 'rm:FormView_Artifacts_Coll_ViewDescr', + '@type': 'aldkg:CollConstr', + '@parent': 'rm:FormView_Artifacts_Coll', + entConstrs: [ + { + '@id': 'rm:FormView_Artifacts_Coll_Ent0_ViewDescr', + '@type': 'aldkg:EntConstr', + '@parent': 'rm:FormView_Artifacts_Coll_Ent0', + conditions: { + '@id': 'rm:_2Yud6', + '@type': 'aldkg:EntConstrCondition', + assetFolder: 'folders:samples_collection', + }, + }, + ], + }, + ], + // child ui elements configs + elements: [], + }, +]; + +const additionalColls: CollState[] = [ + // ViewKinds Collection + { + constr: viewKindCollConstr, + data: viewKinds, + opt: { + updPeriod: undefined, + lastSynced: moment.now(), + //resolveCollConstrs: false, // disable data loading from the server for viewKinds.collConstrs + }, + }, + // ViewDescrs Collection + { + constr: viewDescrCollConstr, + data: viewDescrs, + opt: { + updPeriod: undefined, + lastSynced: moment.now(), + //resolveCollConstrs: false, // 'true' here (by default) triggers data loading from the server + // for viewDescrs.collConstrs (it loads lazily -- after the first access) + }, + }, +]; + +const client = new SparqlClientImpl('https://rdf4j.agentlab.ru/rdf4j-server'); +const rootStore = createUiModelFromState('reqs2', client, rootModelInitialState, additionalColls); +const store: any = asReduxStore(rootStore); +// eslint-disable-next-line @typescript-eslint/no-var-requires +connectReduxDevtools(require('remotedev'), rootStore); + +export default { + title: 'Form/ArtifactFormOverride', + component: Form, + argTypes: { + backgroundColor: { control: 'color' }, + }, +} as Meta; + +const Template: Story = (args: any) => ( + + +
+
+
+
+
+); + +export const RemoteData = Template.bind({}); +RemoteData.args = {};