Skip to content

Commit 6397773

Browse files
authored
Merge pull request #1195 from PADAS/ERA-10484
ERA-10484: Support EFB schemas in ER > Sections and Layout
2 parents 9f754e9 + 6e09366 commit 6397773

File tree

36 files changed

+2127
-1141
lines changed

36 files changed

+2127
-1141
lines changed

src/App.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ div.mapboxgl-popup-tip {
496496

497497
[class*=sideBar],
498498
.primary-nav {
499-
color-adjust: exact !important;
499+
print-color-adjust: exact !important;
500500
-webkit-print-color-adjust: exact !important;
501501
}
502502
}

src/PatrolDetailView/Header/styles.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
height: 4rem;
1111
position: relative;
1212
width: 100%;
13+
z-index: 1;
1314

1415
@media (min-width: $md-layout-width-min) {
1516
height: 5rem;

src/PatrolDetailView/index.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ const PatrolDetailView = () => {
568568

569569
<div className={styles.content}>
570570
<QuickLinks.SectionsWrapper className={styles.sectionWrapper}>
571-
<QuickLinks.Section anchorTitle={t('quickLinksTitles.plan')}>
571+
<QuickLinks.Section anchorTitle={t('quickLinksTitles.plan')} className={styles.section}>
572572
<PlanSection
573573
onPatrolEndDateChange={onPatrolEndDateChange}
574574
onPatrolEndLocationChange={onPatrolEndLocationChange}
@@ -582,7 +582,11 @@ const PatrolDetailView = () => {
582582

583583
{shouldRenderActivitySection && <div className={`${styles.sectionSeparation} ${styles.hideOnPrint}`} />}
584584

585-
<QuickLinks.Section anchorTitle={t('quickLinksTitles.activity')} hidden={!shouldRenderActivitySection}>
585+
<QuickLinks.Section
586+
anchorTitle={t('quickLinksTitles.activity')}
587+
className={styles.section}
588+
hidden={!shouldRenderActivitySection}
589+
>
586590
<ActivitySection
587591
attachments={patrolAttachments}
588592
attachmentsToAdd={attachmentsToAdd}
@@ -602,11 +606,12 @@ const PatrolDetailView = () => {
602606

603607
{shouldRenderHistorySection && <div className={`${styles.sectionSeparation} ${styles.hideOnPrint}`} />}
604608

605-
<QuickLinks.Section anchorTitle={t('quickLinksTitles.history')} hidden={!shouldRenderHistorySection}>
606-
<HistorySection
607-
updates={patrolUpdates}
608-
className={styles.hideOnPrint}
609-
/>
609+
<QuickLinks.Section
610+
anchorTitle={t('quickLinksTitles.history')}
611+
className={styles.section}
612+
hidden={!shouldRenderHistorySection}
613+
>
614+
<HistorySection className={styles.hideOnPrint} updates={patrolUpdates} />
610615
</QuickLinks.Section>
611616
</QuickLinks.SectionsWrapper>
612617

src/PatrolDetailView/styles.module.scss

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,16 @@
5151
}
5252
}
5353

54+
.section {
55+
background-color: white;
56+
border-bottom: 1px solid $very-light-gray-alt;
57+
border-top: 1px solid $very-light-gray-alt;
58+
padding: 1.125rem;
59+
}
60+
5461
.sectionSeparation {
62+
height: 0.5rem;
5563
width: calc(100% + 2.5rem);
56-
border-top: 1px solid $very-light-gray-alt;
57-
margin: 3rem -1.125rem;
5864
}
5965

6066
.footer {

src/QuickLinks/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ SectionsWrapper.propTypes = {
116116
QuickLinks.SectionsWrapper = SectionsWrapper;
117117

118118

119-
const Section = ({ anchorTitle, children, hidden }) => {
119+
const Section = ({ anchorTitle, className, children, hidden }) => {
120120
const { getSectionElement, onSectionElementChange } = useContext(QuickLinksContext);
121121

122122
const [sectionElement, setSectionElement] = useState();
@@ -129,15 +129,19 @@ const Section = ({ anchorTitle, children, hidden }) => {
129129
}
130130
}, [anchorTitle, getSectionElement, onSectionElementChange, sectionElement]);
131131

132-
return !hidden ? <div data-testid={`quickLinks-section-${anchorTitle}`} ref={sectionRef}>
132+
return !hidden ? <div className={className} data-testid={`quickLinks-section-${anchorTitle}`} ref={sectionRef}>
133133
{children}
134134
</div> : null;
135135
};
136136

137-
Section.defaultProps = { hidden: false };
137+
Section.defaultProps = {
138+
className: '',
139+
hidden: false,
140+
};
138141

139142
Section.propTypes = {
140143
anchorTitle: PropTypes.string.isRequired,
144+
className: PropTypes.string,
141145
children: PropTypes.node.isRequired,
142146
hidden: PropTypes.bool,
143147
};

src/QuickLinks/styles.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@
8686
}
8787

8888
.sectionsWrapper {
89+
background-color: $very-light-gray;
8990
position: relative;
9091
overflow-x: hidden;
91-
padding: 1.125rem;
9292
width: 100%;
9393
height: 100%;
9494

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,26 @@
11
import React, { createContext } from 'react';
2-
3-
import { FORM_FIELDS_TYPES } from '../constants';
4-
import { textFieldDetailsFactory } from '../fields/fieldDetailsFactory';
5-
import { isFieldRequired } from '../utils';
6-
import { cloneDeep } from 'lodash-es';
2+
import PropTypes from 'prop-types';
73

84
export const SchemaFormContext = createContext(null);
95

6+
const SchemaFormContextProvider = ({ children, fields, formData, onFormChange }) => {
7+
const onFieldChange = (fieldId, value) => {
8+
// TODO: Update with recursivity for collections.
9+
const newFormData = { ...formData, [fieldId]: value };
1010

11-
const SchemaFormContextProvider = ({ schema, onFormChange, formData, formErrors, children }) => {
12-
13-
const onFieldChange = (field, value) => {
14-
onFormChange({
15-
formData: {
16-
[field]: value
17-
}
18-
});
11+
onFormChange({ formData: newFormData });
1912
};
2013

21-
const getFieldDetails = (fieldName) => {
22-
const fieldSchema = schema.json.properties[fieldName];
23-
const isRequired = isFieldRequired(fieldName, schema);
24-
const uiDetails = schema.ui.fields[fieldName];
25-
const formValue = formData[fieldName] ?? '';
26-
const formError = formErrors?.[fieldName] ?? null;
27-
28-
switch (uiDetails.type) {
29-
case FORM_FIELDS_TYPES.TEXT: {
30-
return textFieldDetailsFactory(fieldSchema, {
31-
...uiDetails,
32-
isRequired
33-
}, formValue, formError);
34-
}
35-
36-
default: return {};
37-
}
38-
};
39-
40-
const getSchema = () => cloneDeep(schema);
41-
42-
return <SchemaFormContext.Provider value={{
43-
formErrors,
44-
getFieldDetails,
45-
getSchema,
46-
onFieldChange
47-
}}>
14+
return <SchemaFormContext.Provider value={{ fields, formData, onFieldChange }}>
4815
{children}
4916
</SchemaFormContext.Provider>;
5017
};
5118

19+
SchemaFormContextProvider.propTypes = {
20+
children: PropTypes.node.isRequired,
21+
fields: PropTypes.object.isRequired,
22+
formData: PropTypes.object.isRequired,
23+
onFormChange: PropTypes.func.isRequired,
24+
};
25+
5226
export default SchemaFormContextProvider;
Lines changed: 78 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,87 @@
1-
import React, { useContext } from 'react';
2-
import { render, screen } from '../../../test-utils';
3-
import userEvent from '@testing-library/user-event';
1+
import React, { useContext, useEffect } from 'react';
2+
3+
import { FORM_ELEMENT_TYPES, ROOT_CANVAS_ID, TEXT_ELEMENT_INPUT_TYPES } from '../constants';
4+
import { render, screen } from '../../../../test-utils';
45

56
import SchemaFormContextProvider, { SchemaFormContext } from './';
67

7-
describe('ReportManager - SchemaForm - SchemaFormContext', () => {
8-
9-
const schema = {
10-
'json': {
11-
'$schema': 'https://json-schema.org/draft/2020-12/schema',
12-
'additionalProperties': false,
13-
'properties': {
14-
'this_is_a_text': {
15-
'default': 'initial value',
16-
'deprecated': false,
17-
'description': 'some good description',
18-
'title': 'This is a text',
19-
'type': 'string'
20-
}
21-
},
22-
'required': [
23-
'this_is_a_text'
24-
],
25-
'type': 'object'
26-
},
27-
'ui': {
28-
'fields': {
29-
'this_is_a_text': {
30-
'inputType': 'SHORT_TEXT',
31-
'placeholder': 'a placeholder',
32-
'type': 'TEXT',
33-
'parent': 'section-_PdgePvPWyACfu9sgN_F6'
34-
}
35-
},
36-
'headers': {
37-
'header-ghqdjqGinaJMptIEJBQmO': {
38-
'label': 'A great header',
39-
'section': 'section-_PdgePvPWyACfu9sgN_F6',
40-
'size': 'LARGE'
41-
}
42-
},
43-
'order': [
44-
'section-_PdgePvPWyACfu9sgN_F6'
45-
],
46-
'sections': {
47-
'section-_PdgePvPWyACfu9sgN_F6': {
48-
'columns': 1,
49-
'isActive': true,
50-
'label': '',
51-
'leftColumn': [
52-
{
53-
'name': 'header-ghqdjqGinaJMptIEJBQmO',
54-
'type': 'header'
55-
},
56-
{
57-
'name': 'this_is_a_text',
58-
'type': 'field'
59-
}
60-
],
61-
'rightColumn': []
62-
}
63-
}
64-
}
65-
};
66-
67-
const initialContextProps = {
68-
schema,
69-
onFormChange: () => {},
70-
formData: {
71-
'this_is_a_text': 'a text value'
72-
},
73-
formErrors: {}
74-
};
75-
76-
const renderSchemaFormContext = (contextProps = initialContextProps, TestComponent, testComponentProps) => render(
77-
<SchemaFormContextProvider {...contextProps}>
78-
<TestComponent {...testComponentProps} />
79-
</SchemaFormContextProvider>
80-
);
81-
82-
test('gets text field details', async () => {
83-
const onClick = jest.fn();
84-
85-
const Component = ({ onClick }) => {
86-
const { getFieldDetails } = useContext(SchemaFormContext);
87-
88-
return (
89-
<button onClick={() => onClick( getFieldDetails('this_is_a_text') )}>
90-
button
91-
</button>
92-
);
8+
describe('ReportManager - DetailsSection -SchemaForm - SchemaFormContext', () => {
9+
const onFormChange = jest.fn();
10+
11+
const renderSchemaFormContext = ({ children, ...props }) => render(<SchemaFormContextProvider
12+
fields={{
13+
'text-1': {
14+
details: {
15+
defaultInput: 'Text 1 Default Input',
16+
description: 'Text 1 Description',
17+
inputType: TEXT_ELEMENT_INPUT_TYPES.SHORT,
18+
isRequired: true,
19+
label: 'Text 1 Label',
20+
placeholder: 'Text 1 Placeholder',
21+
value: 'text-1',
22+
},
23+
parentId: 'section-1',
24+
type: FORM_ELEMENT_TYPES.TEXT,
25+
},
26+
[ROOT_CANVAS_ID]: { details: { fields: ['section-1'] } },
27+
'section-1': {
28+
details: {
29+
columns: 2,
30+
label: 'Section 1 Label',
31+
leftColumn: ['text-1'],
32+
rightColumn: [],
33+
},
34+
parentId: ROOT_CANVAS_ID,
35+
type: FORM_ELEMENT_TYPES.SECTION,
36+
},
37+
}}
38+
formData={{ 'text-1': 'Text 1' }}
39+
onFormChange={onFormChange}
40+
{...props}
41+
>
42+
{children}
43+
</SchemaFormContextProvider>);
44+
45+
test('provides the fields object', async () => {
46+
const Component = () => {
47+
const { fields } = useContext(SchemaFormContext);
48+
49+
return <div data-testid="fields">{JSON.stringify(fields)}</div>;
50+
};
51+
52+
renderSchemaFormContext({ children: <Component /> });
53+
54+
expect(screen.getByTestId('fields')).toHaveTextContent(
55+
'{"text-1":{"details":{"defaultInput":"Text 1 Default Input","description":"Text 1 Description","inputType":"SHORT_TEXT","isRequired":true,"label":"Text 1 Label","placeholder":"Text 1 Placeholder","value":"text-1"},"parentId":"section-1","type":"TEXT"},"root":{"details":{"fields":["section-1"]}},"section-1":{"details":{"columns":2,"label":"Section 1 Label","leftColumn":["text-1"],"rightColumn":[]},"parentId":"root","type":"SECTION"}}'
56+
);
57+
});
58+
59+
test('provides the form data object', async () => {
60+
const Component = () => {
61+
const { formData } = useContext(SchemaFormContext);
62+
63+
return <div data-testid="formData">{JSON.stringify(formData)}</div>;
9364
};
9465

95-
renderSchemaFormContext(undefined, Component, { onClick });
96-
97-
await userEvent.click(screen.getByRole('button'));
98-
99-
expect( onClick ).toHaveBeenCalledWith({
100-
defaultInput: 'initial value',
101-
description: 'some good description',
102-
inputType: 'SHORT_TEXT',
103-
isActive: true,
104-
isRequired: true,
105-
label: 'This is a text',
106-
placeholder: 'a placeholder',
107-
value: 'a text value',
108-
error: null
109-
});
66+
renderSchemaFormContext({ children: <Component /> });
67+
68+
expect(screen.getByTestId('formData')).toHaveTextContent('{"text-1":"Text 1"}');
11069
});
11170

112-
/*ToDo: add coverage for getFieldDetails as support for new field types is added */
71+
test('triggers a form change when there is a field update', async () => {
72+
const Component = () => {
73+
const { onFieldChange } = useContext(SchemaFormContext);
74+
75+
useEffect(() => {
76+
onFieldChange('text-1', 'New Text 1');
77+
}, [onFieldChange]);
11378

79+
return null;
80+
};
81+
82+
renderSchemaFormContext({ children: <Component /> });
83+
84+
expect(onFormChange).toHaveBeenCalledTimes(1);
85+
expect(onFormChange).toHaveBeenCalledWith({ formData: { 'text-1': 'New Text 1' } });
86+
});
11487
});

src/ReportManager/DetailsSection/SchemaForm/SchemaFormContext/useFieldDetails/index.js

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)