@openmrs/esm-form-builder-app 2.0.2-pre.523 → 2.0.2-pre.548

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.
@@ -1,10 +1,10 @@
1
1
  @use '@carbon/styles/scss/type';
2
2
  @use '@carbon/styles/scss/spacing';
3
- @import '~@openmrs/esm-styleguide/src/vars';
3
+ @use '@carbon/styles/scss/colors';
4
4
 
5
5
  .container {
6
6
  padding: 2rem;
7
- background-color: $ui-01;
7
+ background-color: colors.$gray-10;
8
8
  }
9
9
 
10
10
  .backgroundDataFetchingIndicator {
@@ -19,18 +19,49 @@
19
19
  .toolbarWrapper {
20
20
  position: relative;
21
21
  display: flex;
22
- height: spacing.$spacing-09;
23
22
  justify-content: flex-end;
24
23
  }
25
24
 
25
+ :global(.omrs-breakpoint-lt-desktop) {
26
+ .toolbarWrapper {
27
+ height: spacing.$spacing-09;
28
+ }
29
+ }
30
+
31
+ :global(.omrs-breakpoint-gt-tablet) {
32
+ .toolbarWrapper {
33
+ height: spacing.$spacing-07;
34
+ }
35
+ }
36
+
26
37
  .tableToolbar {
27
38
  width: 20%;
28
39
  min-width: 12.5rem;
29
40
  }
30
41
 
31
- .table tr:last-of-type {
42
+ .headerContainer {
43
+ background-color: colors.$gray-10;
44
+ }
45
+
46
+ .searchbox {
47
+ input {
48
+ outline: 2px solid colors.$orange-40 !important;
49
+ }
50
+ }
51
+
52
+
53
+ .table {
54
+ tr {
55
+ &:last-of-type {
56
+ td {
57
+ border-bottom: none;
58
+ }
59
+ }
60
+ }
61
+
32
62
  td {
33
- border-bottom: none;
63
+ padding-top: 0;
64
+ padding-bottom: 0;
34
65
  }
35
66
  }
36
67
 
@@ -42,7 +73,7 @@
42
73
  position: relative;
43
74
  display: flex;
44
75
  width: 100%;
45
- min-height: 3rem;
76
+ min-height: 2rem;
46
77
  margin-bottom: 0.5rem;
47
78
  }
48
79
 
@@ -57,7 +88,7 @@
57
88
  padding: 0;
58
89
 
59
90
  :global(.cds--data-table-content) {
60
- border: 1px solid $ui-03;
91
+ border: 1px solid colors.$gray-20;
61
92
  border-bottom: none;
62
93
  overflow: visible;
63
94
  }
@@ -65,7 +96,7 @@
65
96
  :global(.cds--table-toolbar) {
66
97
  position: relative;
67
98
  height: 2rem;
68
- min-height: 0rem;
99
+ min-height: 0rem;
69
100
  overflow: visible;
70
101
  top: 0;
71
102
  }
@@ -89,13 +120,13 @@
89
120
 
90
121
  .content {
91
122
  @include type.type-style('heading-compact-02');
92
- color: $text-02;
123
+ color: colors.$gray-70;
93
124
  margin-bottom: 0.5rem;
94
125
  }
95
126
 
96
127
  .tileContainer {
97
- background-color: $ui-02;
98
- border-top: 1px solid $ui-03;
128
+ background-color: colors.$white-0;
129
+ border-top: 1px solid colors.$gray-70;
99
130
  padding: 5rem 0;
100
131
  }
101
132
 
@@ -1,6 +1,6 @@
1
+ @use '@carbon/styles/scss/colors';
1
2
  @use '@carbon/styles/scss/spacing';
2
3
  @use '@carbon/styles/scss/type';
3
- @import '~@openmrs/esm-styleguide/src/vars';
4
4
 
5
5
  .action {
6
6
  margin-bottom: spacing.$spacing-03;
@@ -8,7 +8,7 @@
8
8
 
9
9
  .content {
10
10
  @include type.type-style("heading-compact-01");
11
- color: $text-02;
11
+ color: colors.$gray-70;
12
12
  margin-top: spacing.$spacing-05;
13
13
  margin-bottom: spacing.$spacing-03;
14
14
  }
@@ -16,14 +16,14 @@
16
16
  .desktopHeading {
17
17
  h4 {
18
18
  @include type.type-style('heading-compact-02');
19
- color: $text-02;
19
+ color: colors.$gray-70;
20
20
  }
21
21
  }
22
22
 
23
23
  .tabletHeading {
24
24
  h4 {
25
25
  @include type.type-style('heading-03');
26
- color: $text-02;
26
+ color: colors.$gray-70;
27
27
  }
28
28
  }
29
29
 
@@ -51,5 +51,5 @@
51
51
 
52
52
  .tile {
53
53
  text-align: center;
54
- border: 1px solid $ui-03;
54
+ border: 1px solid colors.$gray-70;
55
55
  }
@@ -1,6 +1,6 @@
1
+ @use '@carbon/styles/scss/colors';
1
2
  @use '@carbon/styles/scss/spacing';
2
3
  @use '@carbon/styles/scss/type';
3
- @import '~@openmrs/esm-styleguide/src/vars';
4
4
 
5
5
  .errorMessage {
6
6
  @include type.type-style("heading-compact-02");
@@ -12,20 +12,20 @@
12
12
  .errorCopy {
13
13
  margin-bottom: spacing.$spacing-03;
14
14
  @include type.type-style("body-01");
15
- color: $text-02;
15
+ color: colors.$gray-70;
16
16
  }
17
17
 
18
18
  .desktopHeading {
19
19
  h4 {
20
20
  @include type.type-style('heading-compact-02');
21
- color: $text-02;
21
+ color: colors.$gray-70;
22
22
  }
23
23
  }
24
24
 
25
25
  .tabletHeading {
26
26
  h4 {
27
27
  @include type.type-style('heading-03');
28
- color: $text-02;
28
+ color: colors.$gray-70;
29
29
  }
30
30
  }
31
31
 
@@ -45,5 +45,5 @@
45
45
 
46
46
  .tile {
47
47
  text-align: center;
48
- border: 1px solid $ui-03;
48
+ border: 1px solid colors.$gray-20;
49
49
  }
@@ -3,6 +3,7 @@ import {
3
3
  Button,
4
4
  Column,
5
5
  ComposedModal,
6
+ InlineLoading,
6
7
  InlineNotification,
7
8
  Form,
8
9
  Grid,
@@ -18,10 +19,12 @@ import {
18
19
  import { useParams } from "react-router-dom";
19
20
  import { useTranslation } from "react-i18next";
20
21
  import { ExtensionSlot } from "@openmrs/esm-framework";
22
+ import type { OHRIFormSchema } from "@openmrs/openmrs-form-engine-lib";
21
23
 
22
24
  import type { Schema, RouteParams } from "../../types";
23
25
  import { useClobdata } from "../../hooks/useClobdata";
24
26
  import { useForm } from "../../hooks/useForm";
27
+ import ActionButtons from "../action-buttons/action-buttons.component";
25
28
  import FormRenderer from "../form-renderer/form-renderer.component";
26
29
  import InteractiveBuilder from "../interactive-builder/interactive-builder.component";
27
30
  import SchemaEditor from "../schema-editor/schema-editor.component";
@@ -49,11 +52,15 @@ const Error = ({ error, title }: ErrorProps) => {
49
52
  const FormEditor: React.FC = () => {
50
53
  const { t } = useTranslation();
51
54
  const { formUuid } = useParams<RouteParams>();
55
+ const isNewSchema = !formUuid;
52
56
  const [schema, setSchema] = useState<Schema>();
53
57
  const [showDraftSchemaModal, setShowDraftSchemaModal] = useState(false);
54
58
  const { form, formError, isLoadingForm } = useForm(formUuid);
55
59
  const { clobdata, clobdataError, isLoadingClobdata } = useClobdata(form);
56
60
  const [status, setStatus] = useState<Status>("idle");
61
+ const [stringifiedSchema, setStringifiedSchema] = useState(
62
+ schema ? JSON.stringify(schema, null, 2) : ""
63
+ );
57
64
 
58
65
  const isLoadingFormOrSchema =
59
66
  formUuid && (isLoadingClobdata || isLoadingForm);
@@ -87,17 +94,112 @@ const FormEditor: React.FC = () => {
87
94
  status,
88
95
  ]);
89
96
 
97
+ useEffect(() => {
98
+ setStringifiedSchema(JSON.stringify(schema, null, 2));
99
+ }, [schema]);
100
+
90
101
  const handleLoadDraftSchema = useCallback(() => {
91
102
  setShowDraftSchemaModal(false);
92
103
  const draftSchema = localStorage.getItem("formJSON");
93
104
  setSchema(JSON.parse(draftSchema));
94
105
  }, []);
95
106
 
107
+ const handleSchemaChange = useCallback((updatedSchema: string) => {
108
+ setStringifiedSchema(updatedSchema);
109
+ }, []);
110
+
96
111
  const updateSchema = useCallback((updatedSchema) => {
97
112
  setSchema(updatedSchema);
98
113
  localStorage.setItem("formJSON", JSON.stringify(updatedSchema));
99
114
  }, []);
100
115
 
116
+ const inputDummySchema = useCallback(() => {
117
+ const dummySchema: OHRIFormSchema = {
118
+ encounterType: "",
119
+ name: "Sample Form",
120
+ processor: "EncounterFormProcessor",
121
+ referencedForms: [],
122
+ uuid: "",
123
+ version: "1.0",
124
+ pages: [
125
+ {
126
+ label: "First Page",
127
+ sections: [
128
+ {
129
+ label: "A Section",
130
+ isExpanded: "true",
131
+ questions: [
132
+ {
133
+ label: "A Question of type obs that renders a text input",
134
+ type: "obs",
135
+ questionOptions: {
136
+ rendering: "text",
137
+ concept: "a-system-defined-concept-uuid",
138
+ },
139
+ id: "sampleQuestion",
140
+ },
141
+ ],
142
+ },
143
+ {
144
+ label: "Another Section",
145
+ isExpanded: "true",
146
+ questions: [
147
+ {
148
+ label:
149
+ "Another Question of type obs whose answers get rendered as radio inputs",
150
+ type: "obs",
151
+ questionOptions: {
152
+ rendering: "radio",
153
+ concept: "system-defined-concept-uuid",
154
+ answers: [
155
+ {
156
+ concept: "another-system-defined-concept-uuid",
157
+ label: "Choice 1",
158
+ conceptMappings: [],
159
+ },
160
+ {
161
+ concept: "yet-another-system-defined-concept-uuid",
162
+ label: "Choice 2",
163
+ conceptMappings: [],
164
+ },
165
+ {
166
+ concept: "yet-one-more-system-defined-concept-uuid",
167
+ label: "Choice 3",
168
+ conceptMappings: [],
169
+ },
170
+ ],
171
+ },
172
+ id: "anotherSampleQuestion",
173
+ },
174
+ ],
175
+ },
176
+ ],
177
+ },
178
+ ],
179
+ };
180
+
181
+ setStringifiedSchema(JSON.stringify(dummySchema, null, 2));
182
+ updateSchema({ ...dummySchema });
183
+ }, [updateSchema]);
184
+
185
+ const [invalidJsonErrorMessage, setInvalidJsonErrorMessage] = useState("");
186
+
187
+ const resetErrorMessage = useCallback(() => {
188
+ setInvalidJsonErrorMessage("");
189
+ }, []);
190
+
191
+ const renderSchemaChanges = useCallback(() => {
192
+ resetErrorMessage();
193
+
194
+ try {
195
+ const parsedJson: Schema = JSON.parse(stringifiedSchema);
196
+ updateSchema(parsedJson);
197
+ setStringifiedSchema(JSON.stringify(parsedJson, null, 2));
198
+ } catch (error) {
199
+ setInvalidJsonErrorMessage(error.message);
200
+ }
201
+ }, [stringifiedSchema, updateSchema, resetErrorMessage]);
202
+
101
203
  const DraftSchemaModal = () => {
102
204
  return (
103
205
  <ComposedModal
@@ -140,36 +242,53 @@ const FormEditor: React.FC = () => {
140
242
  {showDraftSchemaModal && <DraftSchemaModal />}
141
243
  <Grid className={styles.grid}>
142
244
  <Column lg={8} md={8} className={styles.column}>
143
- <Tabs>
144
- <TabList aria-label="Schema editor">
145
- <Tab>{t("schemaEditor", "Schema Editor")}</Tab>
146
- </TabList>
147
- <TabPanels>
148
- <TabPanel>
149
- <>
150
- {formError ? (
151
- <Error
152
- error={formError}
153
- title={t("formError", "Error loading form metadata")}
154
- />
155
- ) : null}
156
- {clobdataError ? (
157
- <Error
158
- error={clobdataError}
159
- title={t("schemaLoadError", "Error loading schema")}
160
- />
161
- ) : null}
162
- <SchemaEditor
163
- schema={schema}
164
- onSchemaChange={updateSchema}
165
- isLoading={isLoadingFormOrSchema}
166
- />
167
- </>
168
- </TabPanel>
169
- </TabPanels>
170
- </Tabs>
245
+ <div className={styles.actionButtons}>
246
+ {isLoadingFormOrSchema ? (
247
+ <InlineLoading
248
+ description={t("loadingSchema", "Loading schema") + "..."}
249
+ />
250
+ ) : null}
251
+
252
+ {isNewSchema && !schema ? (
253
+ <Button kind="ghost" onClick={inputDummySchema}>
254
+ {t("inputDummySchema", "Input dummy schema")}
255
+ </Button>
256
+ ) : null}
257
+
258
+ {schema ? (
259
+ <Button kind="ghost" onClick={renderSchemaChanges}>
260
+ <span>{t("renderChanges", "Render changes")}</span>
261
+ </Button>
262
+ ) : null}
263
+ </div>
264
+ <div>
265
+ <span className={styles.tabHeading}>
266
+ {t("schemaEditor", "Schema Editor")}
267
+ </span>
268
+ {formError ? (
269
+ <Error
270
+ error={formError}
271
+ title={t("formError", "Error loading form metadata")}
272
+ />
273
+ ) : null}
274
+ {clobdataError ? (
275
+ <Error
276
+ error={clobdataError}
277
+ title={t("schemaLoadError", "Error loading schema")}
278
+ />
279
+ ) : null}
280
+ <div className={styles.editorContainer}>
281
+ <SchemaEditor
282
+ invalidJsonErrorMessage={invalidJsonErrorMessage}
283
+ isLoading={isLoadingFormOrSchema}
284
+ onSchemaChange={handleSchemaChange}
285
+ stringifiedSchema={stringifiedSchema}
286
+ />
287
+ </div>
288
+ </div>
171
289
  </Column>
172
290
  <Column lg={8} md={8} className={styles.column}>
291
+ <ActionButtons schema={schema} t={t} />
173
292
  <Tabs>
174
293
  <TabList aria-label="Form previews">
175
294
  <Tab>{t("preview", "Preview")}</Tab>
@@ -1,6 +1,6 @@
1
+ @use "@carbon/styles/scss/colors";
1
2
  @use "@carbon/styles/scss/spacing";
2
3
  @use "@carbon/styles/scss/type";
3
- @import '~@openmrs/esm-styleguide/src/vars';
4
4
 
5
5
  .container {
6
6
  padding: 2rem;
@@ -10,7 +10,7 @@
10
10
 
11
11
  .breadcrumbsContainer {
12
12
  nav {
13
- background-color: $ui-02;
13
+ background-color: colors.$white-0;
14
14
  }
15
15
  }
16
16
 
@@ -22,7 +22,7 @@
22
22
  max-width: 100%;
23
23
 
24
24
  :global(.cds--tabs__nav-item--selected) {
25
- border-bottom: 2px solid var(--cds-border-interactive, #005d5d);
25
+ border-bottom: 2px solid var(--cds-border-interactive, colors.$teal-70);
26
26
  outline: none !important;
27
27
  }
28
28
  }
@@ -37,3 +37,27 @@
37
37
  margin: 0;
38
38
  padding: 0;
39
39
  }
40
+
41
+ .actionButtons {
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: flex-end;
45
+ margin: 1rem 0;
46
+
47
+ button {
48
+ margin-left: 1rem
49
+ }
50
+ }
51
+
52
+ .editorContainer {
53
+ padding: 1rem;
54
+ }
55
+
56
+ .tabHeading {
57
+ display: flex;
58
+ align-items: center;
59
+ @include type.type-style('heading-compact-01');
60
+ min-height: 2.5rem;
61
+ width: 100%;
62
+ padding: 0.75rem;
63
+ }
@@ -3,21 +3,19 @@ import { ErrorBoundary } from "react-error-boundary";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { Button, InlineLoading, Tile } from "@carbon/react";
5
5
  import { OHRIFormSchema, OHRIForm } from "@openmrs/openmrs-form-engine-lib";
6
-
7
- import ActionButtons from "../action-buttons/action-buttons.component";
8
6
  import styles from "./form-renderer.scss";
9
7
 
8
+ type ErrorFallbackProps = {
9
+ error: Error;
10
+ resetErrorBoundary: () => void;
11
+ };
12
+
10
13
  type FormRendererProps = {
11
14
  isLoading: boolean;
12
15
  onSchemaChange?: (schema: OHRIFormSchema) => void;
13
16
  schema: OHRIFormSchema;
14
17
  };
15
18
 
16
- type ErrorFallbackProps = {
17
- error: Error;
18
- resetErrorBoundary: () => void;
19
- };
20
-
21
19
  const FormRenderer: React.FC<FormRendererProps> = ({ isLoading, schema }) => {
22
20
  const { t } = useTranslation();
23
21
 
@@ -72,34 +70,26 @@ const FormRenderer: React.FC<FormRendererProps> = ({ isLoading, schema }) => {
72
70
  }
73
71
 
74
72
  return (
75
- <>
76
- <ActionButtons schema={schema} t={t} />
77
-
78
- <div className={styles.container}>
79
- {!schema && (
80
- <Tile className={styles.emptyStateTile}>
81
- <h4 className={styles.heading}>
82
- {t("noSchemaLoaded", "No schema loaded")}
83
- </h4>
84
- <p className={styles.helperText}>
85
- {t(
86
- "formRendererHelperText",
87
- "Load a form schema in the Schema Editor to the left to see it rendered here by the Form Engine."
88
- )}
89
- </p>
90
- </Tile>
91
- )}
92
- {schema === schemaToRender && (
93
- <ErrorBoundary FallbackComponent={ErrorFallback}>
94
- <OHRIForm
95
- formJson={schemaToRender}
96
- mode={"enter"}
97
- patientUUID={""}
98
- />
99
- </ErrorBoundary>
100
- )}
101
- </div>
102
- </>
73
+ <div className={styles.container}>
74
+ {!schema && (
75
+ <Tile className={styles.emptyStateTile}>
76
+ <h4 className={styles.heading}>
77
+ {t("noSchemaLoaded", "No schema loaded")}
78
+ </h4>
79
+ <p className={styles.helperText}>
80
+ {t(
81
+ "formRendererHelperText",
82
+ "Load a form schema in the Schema Editor to the left to see it rendered here by the Form Engine."
83
+ )}
84
+ </p>
85
+ </Tile>
86
+ )}
87
+ {schema === schemaToRender && (
88
+ <ErrorBoundary FallbackComponent={ErrorFallback}>
89
+ <OHRIForm formJson={schemaToRender} mode={"enter"} patientUUID={""} />
90
+ </ErrorBoundary>
91
+ )}
92
+ </div>
103
93
  );
104
94
  };
105
95
 
@@ -1,10 +1,9 @@
1
1
  @use '@carbon/styles/scss/colors';
2
2
  @use '@carbon/styles/scss/spacing';
3
3
  @use '@carbon/styles/scss/type';
4
- @import '~@openmrs/esm-styleguide/src/vars';
5
4
 
6
5
  .container {
7
- background-color: white;
6
+ background-color: colors.$white-0;
8
7
  padding: 1rem;
9
8
  overflow-y: scroll;
10
9
  height: 100vh;
@@ -12,7 +11,7 @@
12
11
 
13
12
  .loadingContainer {
14
13
  @extend .container;
15
- background-color: $ui-01;
14
+ background-color: colors.$gray-10;
16
15
  margin-top: 5rem;
17
16
  }
18
17
 
@@ -1,6 +1,6 @@
1
- @use '@carbon/styles/scss/type';
1
+ @use '@carbon/styles/scss/colors';
2
2
  @use '@carbon/styles/scss/spacing';
3
- @import '~@openmrs/esm-styleguide/src/vars';
3
+ @use '@carbon/styles/scss/type';
4
4
 
5
5
  .buttonsContainer {
6
6
  display: flex;
@@ -12,17 +12,20 @@
12
12
  align-items: center;
13
13
  }
14
14
 
15
+ .questionLabel {
16
+ @include type.type-style('body-01');
17
+ }
18
+
15
19
  .isDragged, .normal {
16
20
  display: flex;
17
21
  height: 3rem;
18
22
  justify-content: space-between;
19
23
  align-items: center;
20
- margin: spacing.$spacing-02 spacing.$spacing-03;
21
24
  width: 100%;
22
25
  }
23
26
 
24
27
  .normal:hover {
25
- background-color: $ui-03;
28
+ background-color: colors.$gray-70;
26
29
  }
27
30
 
28
31
  .isDragged {
@@ -32,18 +35,16 @@
32
35
 
33
36
  .dragIconContainer {
34
37
  cursor: pointer;
35
- margin-right: spacing.$spacing-03;
36
-
37
-
38
38
 
39
39
  :hover {
40
- background-color: $ui-02;
40
+ background-color: colors.$white-0;
41
41
  }
42
42
  }
43
+
43
44
  .dragIcon {
44
45
  margin: 0 spacing.$spacing-03;
45
46
  padding: 0.25rem;
46
- color: $ui-04;
47
+ color: colors.$gray-50;
47
48
  margin-top: 0.2rem;
48
49
  width: 1.5rem;
49
50
  height: 1.5rem;
@@ -1,5 +1,4 @@
1
1
  @use '@carbon/styles/scss/type';
2
- @import '~@openmrs/esm-styleguide/src/vars';
3
2
 
4
3
  .schemaLabel {
5
4
  @include type.type-style('heading-03');