@evoke-platform/ui-components 1.10.0-testing.15 → 1.10.0-testing.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/published/components/custom/FormV2/FormRenderer.js +2 -1
  2. package/dist/published/components/custom/FormV2/FormRendererContainer.js +1 -15
  3. package/dist/published/components/custom/FormV2/components/AccordionSections.js +7 -2
  4. package/dist/published/components/custom/FormV2/components/Body.d.ts +1 -1
  5. package/dist/published/components/custom/FormV2/components/FieldWrapper.js +1 -1
  6. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +2 -2
  7. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.d.ts +9 -0
  8. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +5 -5
  9. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +1 -1
  10. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +1 -1
  11. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +1 -1
  12. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +1 -1
  13. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +2 -2
  14. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +1 -1
  15. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +57 -66
  16. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +2 -2
  17. package/dist/published/components/custom/FormV2/components/Header.d.ts +11 -3
  18. package/dist/published/components/custom/FormV2/components/Header.js +4 -4
  19. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +14 -19
  20. package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
  21. package/dist/published/components/custom/FormV2/components/utils.d.ts +2 -2
  22. package/dist/published/components/custom/FormV2/components/utils.js +7 -7
  23. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +3 -0
  24. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +155 -0
  25. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Renderer.d.ts +13 -0
  26. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Renderer.js +140 -0
  27. package/dist/published/components/custom/ViewDetailsV2/index.d.ts +2 -0
  28. package/dist/published/components/custom/ViewDetailsV2/index.js +2 -0
  29. package/dist/published/components/custom/index.d.ts +1 -0
  30. package/dist/published/components/custom/index.js +1 -0
  31. package/dist/published/index.d.ts +1 -1
  32. package/dist/published/index.js +1 -1
  33. package/dist/published/stories/FormRendererData.d.ts +12 -0
  34. package/dist/published/stories/FormRendererData.js +23 -0
  35. package/dist/published/stories/ViewDetailsV2Container.stories.d.ts +26 -0
  36. package/dist/published/stories/ViewDetailsV2Container.stories.js +37 -0
  37. package/dist/published/stories/ViewDetailsV2Data.d.ts +4 -0
  38. package/dist/published/stories/ViewDetailsV2Data.js +203 -0
  39. package/dist/published/stories/sharedMswHandlers.js +49 -10
  40. package/dist/published/theme/hooks.d.ts +3 -3
  41. package/package.json +2 -2
@@ -10,7 +10,7 @@ export declare function isAddressProperty(key: string): boolean;
10
10
  /**
11
11
  * Determine if a form entry is visible or not.
12
12
  */
13
- export declare const entryIsVisible: (entry: FormEntry, formValues: FieldValues, instance?: FieldValues) => boolean;
13
+ export declare const entryIsVisible: (entry: FormEntry, instance?: FieldValues, formValues?: FieldValues) => boolean;
14
14
  /**
15
15
  * Recursively retrieves all parameter IDs from a given entry of type Sections or Columns.
16
16
  *
@@ -86,7 +86,7 @@ export declare function formatSubmission(submission: FieldValues, apiServices?:
86
86
  message?: string;
87
87
  isError: boolean;
88
88
  }>>): Promise<FieldValues>;
89
- export declare function filterEmptySections(entry: Sections | Columns, formData: FieldValues, instance?: FieldValues): Sections | Columns | null;
89
+ export declare function filterEmptySections(entry: Sections | Columns, instance?: FieldValues, formData?: FieldValues): Sections | Columns | null;
90
90
  export declare function assignIdsToSectionsAndRichText(entries: FormEntry[], object?: Obj, parameters?: InputParameter[]): FormEntry[];
91
91
  /**
92
92
  * Converts a plain text string to RTF format suitable for a RichTextEditor.
@@ -68,7 +68,7 @@ export function isAddressProperty(key) {
68
68
  /**
69
69
  * Determine if a form entry is visible or not.
70
70
  */
71
- export const entryIsVisible = (entry, formValues, instance) => {
71
+ export const entryIsVisible = (entry, instance, formValues) => {
72
72
  const display = 'display' in entry ? entry.display : undefined;
73
73
  const { visibility } = display ?? ('visibility' in entry ? entry : {});
74
74
  if (isObject(visibility) && 'conditions' in visibility && isArray(visibility.conditions)) {
@@ -669,21 +669,21 @@ export async function formatSubmission(submission, apiServices, objectId, instan
669
669
  }
670
670
  return submission;
671
671
  }
672
- export function filterEmptySections(entry, formData, instance) {
672
+ export function filterEmptySections(entry, instance, formData) {
673
673
  if (entry.type === 'sections' && isArray(entry.sections)) {
674
674
  const visibleSections = entry.sections.filter((section) => {
675
675
  if (!section.entries || section.entries.length === 0)
676
676
  return false;
677
677
  for (const sectionEntry of section.entries) {
678
678
  if (sectionEntry.type === 'sections' || sectionEntry.type === 'columns') {
679
- if (sectionEntry.visibility && !entryIsVisible(sectionEntry, formData, instance)) {
679
+ if (sectionEntry.visibility && !entryIsVisible(sectionEntry, instance, formData)) {
680
680
  return false;
681
681
  }
682
- else if (filterEmptySections(sectionEntry, formData, instance)) {
682
+ else if (filterEmptySections(sectionEntry, instance, formData)) {
683
683
  return true;
684
684
  }
685
685
  }
686
- else if (entryIsVisible(sectionEntry, formData, instance)) {
686
+ else if (entryIsVisible(sectionEntry, instance, formData)) {
687
687
  return true;
688
688
  }
689
689
  }
@@ -703,13 +703,13 @@ export function filterEmptySections(entry, formData, instance) {
703
703
  let hasVisibleEntry = false;
704
704
  for (const columnEntry of column.entries) {
705
705
  if (columnEntry.type === 'sections' || columnEntry.type === 'columns') {
706
- if (filterEmptySections(columnEntry, formData, instance)) {
706
+ if (filterEmptySections(columnEntry, instance, formData)) {
707
707
  hasVisibleEntry = true;
708
708
  break;
709
709
  }
710
710
  }
711
711
  else {
712
- if (entryIsVisible(columnEntry, formData, instance)) {
712
+ if (entryIsVisible(columnEntry, instance, formData)) {
713
713
  hasVisibleEntry = true;
714
714
  break;
715
715
  }
@@ -0,0 +1,3 @@
1
+ import { EntryRendererProps } from '../FormV2/components/types';
2
+ declare function ViewOnlyEntryRenderer(props: EntryRendererProps): any;
3
+ export default ViewOnlyEntryRenderer;
@@ -0,0 +1,155 @@
1
+ import { useApiServices, useApp, } from '@evoke-platform/context';
2
+ import { CancelRounded, CheckCircleRounded } from '@mui/icons-material';
3
+ import DOMPurify from 'dompurify';
4
+ import { isEmpty } from 'lodash';
5
+ import { DateTime } from 'luxon';
6
+ import React, { useEffect, useMemo, useState } from 'react';
7
+ import { useFormContext } from '../../../theme/hooks';
8
+ import { Link, Typography } from '../../core';
9
+ import { Box, Grid } from '../../layout';
10
+ import AccordionSections from '../FormV2/components/AccordionSections';
11
+ import FieldWrapper from '../FormV2/components/FieldWrapper';
12
+ import AddressFields from '../FormV2/components/FormFieldTypes/AddressFields';
13
+ import DropdownRepeatableField from '../FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField';
14
+ import RepeatableField from '../FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField';
15
+ import Criteria from '../FormV2/components/FormFieldTypes/Criteria';
16
+ import { Document } from '../FormV2/components/FormFieldTypes/DocumentFiles/Document';
17
+ import { Image } from '../FormV2/components/FormFieldTypes/Image';
18
+ import { entryIsVisible, fetchCollectionData, filterEmptySections, getDefaultPages, isAddressProperty, } from '../FormV2/components/utils';
19
+ function ViewOnlyEntryRenderer(props) {
20
+ const { entry } = props;
21
+ const { fetchedOptions, setFetchedOptions, object, instance, richTextEditor: RichTextEditor } = useFormContext();
22
+ const entryId = entry.propertyId || 'defaultId';
23
+ const apiServices = useApiServices();
24
+ const { defaultPages, findDefaultPageSlugFor } = useApp();
25
+ const [navigationSlug, setNavigationSlug] = useState(fetchedOptions[`${entryId}NavigationSlug`]);
26
+ const initialMiddleObjectInstances = fetchedOptions[`${entryId}InitialMiddleObjectInstances`];
27
+ const middleObject = fetchedOptions[`${entryId}MiddleObject`];
28
+ const display = 'display' in entry ? entry.display : undefined;
29
+ const fieldDefinition = useMemo(() => {
30
+ const def = entry.type === 'readonlyField'
31
+ ? isAddressProperty(entry.propertyId)
32
+ ? object?.properties?.find((prop) => prop.id === entry.propertyId.split('.')[0])
33
+ : object?.properties?.find((prop) => prop.id === entry.propertyId)
34
+ : undefined;
35
+ if (def?.enum && def.type === 'string') {
36
+ const cloned = structuredClone(def);
37
+ // single select must be made to be type choices for label and error handling
38
+ cloned.type = 'choices';
39
+ return cloned;
40
+ }
41
+ return def;
42
+ }, [entry, object]);
43
+ useEffect(() => {
44
+ if (fieldDefinition?.type === 'collection' && fieldDefinition?.manyToManyPropertyId && instance) {
45
+ fetchCollectionData(apiServices, fieldDefinition, setFetchedOptions, instance.id, fetchedOptions, initialMiddleObjectInstances);
46
+ }
47
+ }, [fieldDefinition, initialMiddleObjectInstances, setFetchedOptions, instance, fetchedOptions, apiServices]);
48
+ useEffect(() => {
49
+ (async () => {
50
+ if (object?.properties && !fetchedOptions[`${entryId}NavigationSlug`]) {
51
+ const pages = await getDefaultPages(object.properties, defaultPages, findDefaultPageSlugFor);
52
+ if (fieldDefinition?.objectId && pages[fieldDefinition.objectId]) {
53
+ setNavigationSlug(pages[fieldDefinition.objectId]);
54
+ setFetchedOptions({
55
+ [`${entryId}NavigationSlug`]: pages[fieldDefinition.objectId],
56
+ });
57
+ }
58
+ }
59
+ })();
60
+ }, [object, defaultPages, findDefaultPageSlugFor, fieldDefinition]);
61
+ // If the entry is hidden, clear its value and any nested values, and skip rendering
62
+ if (!entryIsVisible(entry, instance)) {
63
+ return null;
64
+ }
65
+ if (entry.type === 'content') {
66
+ return (React.createElement(Box, { dangerouslySetInnerHTML: { __html: DOMPurify.sanitize(entry.html) }, sx: {
67
+ fontFamily: 'Roboto, Helvetica, Arial, sans-serif',
68
+ } }));
69
+ }
70
+ else if (entry.type === 'readonlyField') {
71
+ if (isAddressProperty(entryId)) {
72
+ return (React.createElement(AddressFields, { entry: entry, viewOnly: true, entryId: entryId, fieldDefinition: fieldDefinition }));
73
+ }
74
+ else {
75
+ let fieldValue = instance?.[entryId] || '';
76
+ switch (fieldDefinition?.type) {
77
+ case 'object':
78
+ if (navigationSlug && fieldDefinition?.objectId) {
79
+ return (React.createElement(FieldWrapper, { inputId: entryId, inputType: "object", label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, viewOnly: true },
80
+ React.createElement(Link, { sx: { cursor: 'pointer', fontFamily: 'sans-serif' }, href: `${'/app'}${navigationSlug.replace(':instanceId', fieldValue.id)}` }, fieldValue.name)));
81
+ }
82
+ fieldValue = fieldValue.name;
83
+ break;
84
+ case 'array':
85
+ if (!isEmpty(fieldValue)) {
86
+ fieldValue = fieldValue && fieldValue.join(', ');
87
+ }
88
+ else {
89
+ fieldValue = '';
90
+ }
91
+ break;
92
+ case 'user':
93
+ fieldValue = fieldValue && fieldValue.name;
94
+ break;
95
+ case 'date':
96
+ fieldValue = fieldValue && DateTime.fromISO(fieldValue).toFormat('MM/dd/yyyy');
97
+ break;
98
+ case 'date-time':
99
+ fieldValue =
100
+ fieldValue && fieldValue instanceof Date
101
+ ? DateTime.fromJSDate(fieldValue).toFormat('MM/dd/yyyy hh:mm a')
102
+ : fieldValue && DateTime.fromISO(fieldValue).toFormat('MM/dd/yyyy hh:mm a');
103
+ break;
104
+ case 'time':
105
+ fieldValue =
106
+ fieldValue &&
107
+ DateTime.fromISO(DateTime.now().toISODate() + 'T' + fieldValue).toFormat('hh:mm a');
108
+ break;
109
+ default:
110
+ break;
111
+ }
112
+ if (fieldDefinition) {
113
+ if (fieldDefinition.type === 'image') {
114
+ return (React.createElement(FieldWrapper, { inputId: entryId, inputType: 'image', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, viewOnly: true },
115
+ React.createElement(Image, { id: entryId, canUpdateProperty: false, value: fieldValue, hasDescription: !!display?.description })));
116
+ }
117
+ else if (fieldDefinition.type === 'richText') {
118
+ return (React.createElement(FieldWrapper, { inputId: entryId, inputType: 'richText', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, viewOnly: true }, RichTextEditor ? (React.createElement(RichTextEditor
119
+ // RichTexts get a uniqueId when the form is loaded to prevent issues with multiple rich text fields on one form
120
+ , {
121
+ // RichTexts get a uniqueId when the form is loaded to prevent issues with multiple rich text fields on one form
122
+ id: entry.uniqueId, value: fieldValue, format: "rtf", disabled: true, rows: display?.rowCount, hasError: false })) : (React.createElement(Typography, { variant: "body1", key: entryId, sx: { height: '24px' } }, fieldValue))));
123
+ }
124
+ else if (fieldDefinition.type === 'document') {
125
+ return (React.createElement(FieldWrapper, { inputId: entryId, inputType: 'document', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, prefix: display?.prefix, suffix: display?.suffix, viewOnly: true },
126
+ React.createElement(Document, { id: entryId, error: false, value: fieldValue, canUpdateProperty: false })));
127
+ }
128
+ else if (fieldDefinition.type === 'collection') {
129
+ return fieldDefinition?.manyToManyPropertyId ? (middleObject && initialMiddleObjectInstances && (React.createElement(FieldWrapper, { inputId: entryId, inputType: 'collection', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, viewOnly: true },
130
+ React.createElement(DropdownRepeatableField, { initialMiddleObjectInstances: fetchedOptions[`${entryId}MiddleObjectInstances`] ||
131
+ initialMiddleObjectInstances, id: entryId, middleObject: middleObject, fieldDefinition: fieldDefinition, readOnly: true })))) : (React.createElement(FieldWrapper, { inputId: entryId, inputType: 'collection', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, viewOnly: true },
132
+ React.createElement(RepeatableField, { fieldDefinition: fieldDefinition, canUpdateProperty: false, entry: entry, viewLayout: display?.viewLayout })));
133
+ }
134
+ else if (fieldDefinition.type === 'criteria') {
135
+ return (React.createElement(FieldWrapper, { inputId: entryId, inputType: 'criteria', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, viewOnly: true },
136
+ React.createElement(Criteria, { value: fieldValue, fieldDefinition: fieldDefinition, canUpdateProperty: false })));
137
+ }
138
+ else {
139
+ return (React.createElement(FieldWrapper, { inputId: entryId, inputType: fieldDefinition?.type || 'string', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, prefix: display?.prefix, suffix: display?.suffix, viewOnly: true }, fieldDefinition?.type === 'boolean' && fieldValue ? (React.createElement(CheckCircleRounded, { "aria-label": "Checked", color: "success" })) : fieldDefinition?.type === 'boolean' ? (React.createElement(CancelRounded, { "aria-label": "Unchecked", color: "error" })) : (React.createElement(Typography, { variant: "body1", key: entryId, sx: { height: '24px' } }, fieldValue))));
140
+ }
141
+ }
142
+ }
143
+ }
144
+ else if (entry.type === 'columns') {
145
+ return (React.createElement(Grid, { container: true, spacing: 4 }, entry.columns.map((column, colIndex) => (React.createElement(Grid, { item: true, key: colIndex, xs: 12, sm: column.width }, column.entries?.map((columnEntry, entryIndex) => {
146
+ return (React.createElement(ViewOnlyEntryRenderer, { key: entryIndex + (columnEntry?.parameterId ?? ''), entry: columnEntry }));
147
+ }))))));
148
+ }
149
+ else if (entry.type === 'sections') {
150
+ const filteredEntry = filterEmptySections(entry, instance);
151
+ return filteredEntry ? React.createElement(AccordionSections, { entry: filteredEntry, readOnly: true }) : null;
152
+ }
153
+ return null;
154
+ }
155
+ export default ViewOnlyEntryRenderer;
@@ -0,0 +1,13 @@
1
+ import React, { ComponentType } from 'react';
2
+ import { FormRendererProps } from '../FormV2/FormRenderer';
3
+ import { BaseProps, SimpleEditorProps } from '../FormV2/components/types';
4
+ export type ViewDetailsV2ContainerProps = BaseProps & {
5
+ panelLayoutId?: string;
6
+ instanceId?: string;
7
+ objectId: string;
8
+ richTextEditor?: ComponentType<SimpleEditorProps>;
9
+ renderHeader?: FormRendererProps['renderHeader'];
10
+ renderBody?: FormRendererProps['renderBody'];
11
+ };
12
+ declare function ViewDetailsV2Container(props: ViewDetailsV2ContainerProps): React.JSX.Element;
13
+ export default ViewDetailsV2Container;
@@ -0,0 +1,140 @@
1
+ import { useApiServices, useObject, } from '@evoke-platform/context';
2
+ import axios from 'axios';
3
+ import React, { useEffect, useMemo, useState } from 'react';
4
+ import { useWidgetSize } from '../../../theme';
5
+ import { Skeleton, Snackbar } from '../../core';
6
+ import { Box } from '../../layout';
7
+ import ErrorComponent from '../ErrorComponent';
8
+ import { FormContext } from '../FormV2';
9
+ import Header from '../FormV2/components/Header';
10
+ import { assignIdsToSectionsAndRichText, getPrefixedUrl } from '../FormV2/components/utils';
11
+ import ViewOnlyEntryRenderer from './InstanceEntryRenderer';
12
+ function ViewDetailsV2Container(props) {
13
+ const { instanceId, panelLayoutId, objectId, richTextEditor, renderHeader, renderBody } = props;
14
+ const apiServices = useApiServices();
15
+ const [sanitizedObject, setSanitizedObject] = useState();
16
+ const [instance, setInstance] = useState();
17
+ const [error, setError] = useState();
18
+ const [panelLayout, setPanelLayout] = useState();
19
+ const [expandedSections, setExpandedSections] = useState([]);
20
+ const [fetchedOptions, setFetchedOptions] = useState({});
21
+ const [expandAll, setExpandAll] = useState();
22
+ const [snackbarError, setSnackbarError] = useState({
23
+ showAlert: false,
24
+ isError: true,
25
+ });
26
+ function handleExpandAll() {
27
+ setExpandAll(true);
28
+ }
29
+ function handleCollapseAll() {
30
+ setExpandAll(false);
31
+ }
32
+ const updateFetchedOptions = (newData) => {
33
+ setFetchedOptions((prev) => ({
34
+ ...prev,
35
+ ...newData,
36
+ }));
37
+ };
38
+ const { ref: containerRef, breakpoints, width, } = useWidgetSize({
39
+ scroll: false,
40
+ defaultWidth: 1200,
41
+ });
42
+ const { isXs, isSm } = breakpoints;
43
+ const objectStore = useObject(panelLayout?.objectId ?? objectId);
44
+ const onError = (err) => {
45
+ const code = axios.isAxiosError(err) ? err.response?.status : undefined;
46
+ setSnackbarError({ ...snackbarError, isError: true });
47
+ setError(code ?? true);
48
+ };
49
+ useEffect(() => {
50
+ (async () => {
51
+ try {
52
+ if (instanceId) {
53
+ const instance = await objectStore.getInstance(instanceId);
54
+ setInstance(instance);
55
+ }
56
+ const object = await apiServices.get(getPrefixedUrl(`/objects/${objectId}${instanceId ? `/instances/${instanceId}/object` : '/effective'}`), { params: { sanitizedVersion: true } });
57
+ setSanitizedObject(object);
58
+ }
59
+ catch (error) {
60
+ const isAxiosErr = axios.isAxiosError(error);
61
+ console.error(isAxiosErr ? error.message : 'Error while fetching object');
62
+ setError(isAxiosErr ? error.status : true);
63
+ }
64
+ })();
65
+ }, [objectId, instanceId]);
66
+ useEffect(() => {
67
+ if (panelLayoutId || sanitizedObject?.defaultPanelLayoutId) {
68
+ apiServices
69
+ .get(getPrefixedUrl(`/objects/${objectId}/panelLayouts/${panelLayoutId || sanitizedObject?.defaultPanelLayoutId}`))
70
+ .then((evokeForm) => {
71
+ setPanelLayout(evokeForm);
72
+ })
73
+ .catch((error) => {
74
+ onError(error);
75
+ });
76
+ }
77
+ }, [panelLayoutId, sanitizedObject, objectId]);
78
+ const updatedEntries = useMemo(() => {
79
+ return assignIdsToSectionsAndRichText(panelLayout?.entries || []);
80
+ }, [panelLayout?.id]);
81
+ const isLoading = (instanceId && !instance) || !panelLayout || !sanitizedObject;
82
+ const hasSections = updatedEntries.some((entry) => entry.type === 'sections');
83
+ const headerProps = {
84
+ title: panelLayout?.name,
85
+ onExpandAll: handleExpandAll,
86
+ onCollapseAll: handleCollapseAll,
87
+ expandedSections,
88
+ hasAccordions: hasSections,
89
+ autosaveEnabled: false,
90
+ };
91
+ return !error ? (React.createElement(Box, { sx: {
92
+ backgroundColor: '#ffffff',
93
+ borderRadius: '6px',
94
+ padding: '0px',
95
+ border: !isLoading ? '1px solid #dbe0e4' : undefined,
96
+ } },
97
+ !isLoading ? (React.createElement(Box, { ref: containerRef },
98
+ (hasSections || panelLayout.name || renderHeader) &&
99
+ (renderHeader ? renderHeader({ ...headerProps }) : React.createElement(Header, { ...headerProps })),
100
+ React.createElement(Box, { sx: {
101
+ paddingX: isSm || isXs ? 2 : 3,
102
+ borderTop: '1px solid #e9ecef',
103
+ } },
104
+ React.createElement(FormContext.Provider, { value: {
105
+ fetchedOptions,
106
+ setFetchedOptions: updateFetchedOptions,
107
+ object: sanitizedObject,
108
+ instance,
109
+ richTextEditor,
110
+ expandedSections,
111
+ setExpandedSections,
112
+ expandAll,
113
+ setExpandAll,
114
+ width,
115
+ } }, renderBody
116
+ ? renderBody({
117
+ isInitializing: isLoading,
118
+ entries: updatedEntries,
119
+ onExpandAll: handleExpandAll,
120
+ onCollapseAll: handleCollapseAll,
121
+ expandedSections,
122
+ hasAccordions: hasSections,
123
+ })
124
+ : updatedEntries.map((entry, index) => (React.createElement(ViewOnlyEntryRenderer, { entry: entry, key: index }))))))) : (React.createElement(Box, { sx: { padding: '20px' } },
125
+ React.createElement(Box, { display: 'flex', width: '100%', justifyContent: 'space-between' },
126
+ React.createElement(Skeleton, { width: '78%', sx: { borderRadius: '8px', height: '40px' } }),
127
+ React.createElement(Skeleton, { width: '20%', sx: { borderRadius: '8px', height: '40px' } })),
128
+ React.createElement(Box, { display: 'flex', width: '100%', justifyContent: 'space-between' },
129
+ React.createElement(Skeleton, { width: '32%', sx: { borderRadius: '8px', height: '40px' } }),
130
+ React.createElement(Skeleton, { width: '33%', sx: { borderRadius: '8px', height: '40px' } }),
131
+ React.createElement(Skeleton, { width: '32%', sx: { borderRadius: '8px', height: '40px' } })),
132
+ React.createElement(Box, { display: 'flex', width: '100%', justifyContent: 'space-between' },
133
+ React.createElement(Skeleton, { width: '49%', sx: { borderRadius: '8px', height: '40px' } }),
134
+ React.createElement(Skeleton, { width: '49%', sx: { borderRadius: '8px', height: '40px' } })))),
135
+ React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({
136
+ isError: snackbarError.isError,
137
+ showAlert: false,
138
+ }), message: snackbarError.message, error: snackbarError.isError }))) : (React.createElement(ErrorComponent, { colspan: props.colspan, code: error === 403 ? 'AccessDenied' : error === 404 ? 'NotFound' : 'Misconfigured' }));
139
+ }
140
+ export default ViewDetailsV2Container;
@@ -0,0 +1,2 @@
1
+ export { default as ViewOnlyEntryRenderer } from './InstanceEntryRenderer';
2
+ export { default as ViewDetailsV2Container } from './ViewDetailsV2Renderer';
@@ -0,0 +1,2 @@
1
+ export { default as ViewOnlyEntryRenderer } from './InstanceEntryRenderer';
2
+ export { default as ViewDetailsV2Container } from './ViewDetailsV2Renderer';
@@ -14,3 +14,4 @@ export { RepeatableField } from './RepeatableField';
14
14
  export { ResponsiveOverflow } from './ResponsiveOverflow';
15
15
  export { RichTextViewer } from './RichTextViewer';
16
16
  export { UserAvatar } from './UserAvatar';
17
+ export { ViewDetailsV2Container, ViewOnlyEntryRenderer } from './ViewDetailsV2';
@@ -12,3 +12,4 @@ export { RepeatableField } from './RepeatableField';
12
12
  export { ResponsiveOverflow } from './ResponsiveOverflow';
13
13
  export { RichTextViewer } from './RichTextViewer';
14
14
  export { UserAvatar } from './UserAvatar';
15
+ export { ViewDetailsV2Container, ViewOnlyEntryRenderer } from './ViewDetailsV2';
@@ -2,7 +2,7 @@ export { ClickAwayListener, createTheme, darken, lighten, styled, Toolbar, useMe
2
2
  export { CalendarPicker, DateTimePicker, MonthPicker, PickersDay, StaticDateTimePicker, StaticTimePicker, TimePicker, YearPicker, } from '@mui/x-date-pickers';
3
3
  export * from './colors';
4
4
  export * from './components/core';
5
- export { BuilderGrid, CriteriaBuilder, DataGrid, ErrorComponent, Form, FormContext, FormField, FormRenderer, FormRendererContainer, getReadableQuery, HistoryLog, MenuBar, MultiSelect, RecursiveEntryRenderer, RepeatableField, ResponsiveOverflow, RichTextViewer, UserAvatar, } from './components/custom';
5
+ export { BuilderGrid, CriteriaBuilder, DataGrid, ErrorComponent, Form, FormContext, FormField, FormRenderer, FormRendererContainer, getReadableQuery, HistoryLog, MenuBar, MultiSelect, RecursiveEntryRenderer, RepeatableField, ResponsiveOverflow, RichTextViewer, UserAvatar, ViewDetailsV2Container, ViewOnlyEntryRenderer, } from './components/custom';
6
6
  export type { BodyProps, FooterProps, FormRef, GridSortModel, HeaderProps } from './components/custom';
7
7
  export { NumericFormat } from './components/custom/FormField/InputFieldComponent';
8
8
  export { Box, Container, Grid, Stack } from './components/layout';
@@ -2,7 +2,7 @@ export { ClickAwayListener, createTheme, darken, lighten, styled, Toolbar, useMe
2
2
  export { CalendarPicker, DateTimePicker, MonthPicker, PickersDay, StaticDateTimePicker, StaticTimePicker, TimePicker, YearPicker, } from '@mui/x-date-pickers';
3
3
  export * from './colors';
4
4
  export * from './components/core';
5
- export { BuilderGrid, CriteriaBuilder, DataGrid, ErrorComponent, Form, FormContext, FormField, FormRenderer, FormRendererContainer, getReadableQuery, HistoryLog, MenuBar, MultiSelect, RecursiveEntryRenderer, RepeatableField, ResponsiveOverflow, RichTextViewer, UserAvatar, } from './components/custom';
5
+ export { BuilderGrid, CriteriaBuilder, DataGrid, ErrorComponent, Form, FormContext, FormField, FormRenderer, FormRendererContainer, getReadableQuery, HistoryLog, MenuBar, MultiSelect, RecursiveEntryRenderer, RepeatableField, ResponsiveOverflow, RichTextViewer, UserAvatar, ViewDetailsV2Container, ViewOnlyEntryRenderer, } from './components/custom';
6
6
  export { NumericFormat } from './components/custom/FormField/InputFieldComponent';
7
7
  export { Box, Container, Grid, Stack } from './components/layout';
8
8
  export * from './theme';
@@ -121,3 +121,15 @@ export declare const users: {
121
121
  email: string;
122
122
  name: string;
123
123
  }[];
124
+ export declare const customerLayout: {
125
+ id: string;
126
+ name: string;
127
+ properties: {
128
+ id: string;
129
+ }[];
130
+ sort: {
131
+ colId: string;
132
+ sort: string;
133
+ };
134
+ objectId: string;
135
+ };
@@ -93,6 +93,11 @@ export const mockGenericEvokeFormObject = {
93
93
  name: 'User',
94
94
  type: 'user',
95
95
  },
96
+ {
97
+ id: 'boolean',
98
+ name: 'Boolean',
99
+ type: 'boolean',
100
+ },
96
101
  ],
97
102
  actions: [
98
103
  {
@@ -1054,3 +1059,21 @@ export const users = [
1054
1059
  name: 'User 2',
1055
1060
  },
1056
1061
  ];
1062
+ // Layouts
1063
+ export const customerLayout = {
1064
+ id: 'layoutId',
1065
+ name: 'Generic Layout 3',
1066
+ properties: [
1067
+ {
1068
+ id: 'name',
1069
+ },
1070
+ {
1071
+ id: 'document',
1072
+ },
1073
+ ],
1074
+ sort: {
1075
+ colId: 'name',
1076
+ sort: 'asc',
1077
+ },
1078
+ objectId: 'customers',
1079
+ };
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react/dist/types-0fc72a6d").R, import("../components/custom/FormV2/components/types").BaseProps & {
3
+ panelLayoutId?: string | undefined;
4
+ instanceId?: string | undefined;
5
+ objectId: string;
6
+ richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
7
+ renderHeader?: ((props: import("../components/custom").HeaderProps) => React.ReactNode) | undefined;
8
+ renderBody?: ((props: import("../components/custom").BodyProps) => React.ReactNode) | undefined;
9
+ }>;
10
+ export default _default;
11
+ export declare const ViewDetails: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0fc72a6d").R, import("../components/custom/FormV2/components/types").BaseProps & {
12
+ panelLayoutId?: string | undefined;
13
+ instanceId?: string | undefined;
14
+ objectId: string;
15
+ richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
16
+ renderHeader?: ((props: import("../components/custom").HeaderProps) => React.ReactNode) | undefined;
17
+ renderBody?: ((props: import("../components/custom").BodyProps) => React.ReactNode) | undefined;
18
+ }>;
19
+ export declare const ViewWithSections: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0fc72a6d").R, import("../components/custom/FormV2/components/types").BaseProps & {
20
+ panelLayoutId?: string | undefined;
21
+ instanceId?: string | undefined;
22
+ objectId: string;
23
+ richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
24
+ renderHeader?: ((props: import("../components/custom").HeaderProps) => React.ReactNode) | undefined;
25
+ renderBody?: ((props: import("../components/custom").BodyProps) => React.ReactNode) | undefined;
26
+ }>;
@@ -0,0 +1,37 @@
1
+ import { Box } from '@mui/material';
2
+ import React from 'react';
3
+ import { MemoryRouter } from 'react-router-dom';
4
+ import { ViewDetailsV2Container } from '../components/custom';
5
+ import { sharedObjectHandlers } from './sharedMswHandlers';
6
+ export default {
7
+ title: 'Custom/ViewDetailsV2Container',
8
+ component: ViewDetailsV2Container,
9
+ parameters: {
10
+ layout: 'fullscreen',
11
+ msw: {
12
+ handlers: [sharedObjectHandlers],
13
+ },
14
+ },
15
+ decorators: [
16
+ (Story) => (React.createElement(Box, { sx: { padding: '2rem', paddingBottom: 0, '& .css-x8wy7h': { bottom: -1 } } },
17
+ React.createElement(Story, null))),
18
+ ],
19
+ };
20
+ const mockProps = {
21
+ panelLayoutId: 'genericEvokeView',
22
+ instanceId: 'genericEvokeViewInstanceId',
23
+ objectId: 'genericEvokeForm',
24
+ };
25
+ const Template = (args) => {
26
+ return (React.createElement(MemoryRouter, null,
27
+ React.createElement(ViewDetailsV2Container, { ...args })));
28
+ };
29
+ export const ViewDetails = Template.bind({});
30
+ ViewDetails.args = {
31
+ ...mockProps,
32
+ };
33
+ export const ViewWithSections = Template.bind({});
34
+ ViewWithSections.args = {
35
+ ...mockProps,
36
+ panelLayoutId: 'genericEvokeViewWithSections',
37
+ };
@@ -0,0 +1,4 @@
1
+ import { EvokeForm, ObjectInstance } from '@evoke-platform/context';
2
+ export declare const viewInstance: ObjectInstance;
3
+ export declare const mockGenericEvokeView: Omit<EvokeForm, 'actionId'>;
4
+ export declare const mockGenericEvokeViewWithSections: EvokeForm;