@axinom/mosaic-ui 0.33.0-rc.0 → 0.33.0-rc.19

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 (112) hide show
  1. package/dist/components/Actions/Actions.models.d.ts +0 -13
  2. package/dist/components/Actions/Actions.models.d.ts.map +1 -1
  3. package/dist/components/DynamicDataList/DynamicDataList.d.ts.map +1 -1
  4. package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts +3 -1
  5. package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts.map +1 -1
  6. package/dist/components/FormElements/CustomTags/CustomTags.d.ts.map +1 -1
  7. package/dist/components/FormElements/FileUploadControl/FileUploadControl.d.ts.map +1 -1
  8. package/dist/components/FormElements/Radio/Radio.d.ts.map +1 -1
  9. package/dist/components/FormElements/Tags/Tags.d.ts.map +1 -1
  10. package/dist/components/FormElements/formStoryHelper.d.ts.map +1 -1
  11. package/dist/components/FormStation/FormStation.d.ts.map +1 -1
  12. package/dist/components/FormStation/SaveOnNavigate/SaveOnNavigate.d.ts +2 -0
  13. package/dist/components/FormStation/SaveOnNavigate/SaveOnNavigate.d.ts.map +1 -1
  14. package/dist/components/FormStation/SaveOnNavigate/handleNavigationAttempt.d.ts +1 -1
  15. package/dist/components/FormStation/SaveOnNavigate/handleNavigationAttempt.d.ts.map +1 -1
  16. package/dist/components/FormStation/StationErrorStateType.d.ts +5 -0
  17. package/dist/components/FormStation/StationErrorStateType.d.ts.map +1 -0
  18. package/dist/components/FormStation/useValidationError.d.ts +15 -0
  19. package/dist/components/FormStation/useValidationError.d.ts.map +1 -0
  20. package/dist/components/InfoPanel/InfoImage/InfoImage.d.ts.map +1 -1
  21. package/dist/components/List/List.d.ts.map +1 -1
  22. package/dist/components/Loaders/ImageLoader/ImageLoader.d.ts +2 -0
  23. package/dist/components/Loaders/ImageLoader/ImageLoader.d.ts.map +1 -1
  24. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts +1 -11
  25. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts.map +1 -1
  26. package/dist/components/index.d.ts +1 -1
  27. package/dist/components/index.d.ts.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.es.js +3 -3
  31. package/dist/index.es.js.map +1 -1
  32. package/dist/index.js +3 -3
  33. package/dist/index.js.map +1 -1
  34. package/dist/initialize.d.ts +7 -4
  35. package/dist/initialize.d.ts.map +1 -1
  36. package/dist/utils/ErrorMapper/ApolloClient/ApolloErrorMapper.d.ts +9 -0
  37. package/dist/utils/ErrorMapper/ApolloClient/ApolloErrorMapper.d.ts.map +1 -0
  38. package/dist/utils/ErrorMapper/ErrorMapper.d.ts +15 -0
  39. package/dist/utils/ErrorMapper/ErrorMapper.d.ts.map +1 -0
  40. package/dist/utils/ErrorMapper/ErrorMapper.type.d.ts +11 -0
  41. package/dist/utils/ErrorMapper/ErrorMapper.type.d.ts.map +1 -0
  42. package/dist/utils/ErrorMapper/index.d.ts +4 -0
  43. package/dist/utils/ErrorMapper/index.d.ts.map +1 -0
  44. package/dist/utils/ErrorTypeToStationError.d.ts +1 -1
  45. package/dist/utils/ErrorTypeToStationError.d.ts.map +1 -1
  46. package/package.json +3 -3
  47. package/src/components/Accordion/Accordion.scss +1 -1
  48. package/src/components/Accordion/AccordionItem/AccordionItem.scss +1 -1
  49. package/src/components/Actions/Action/Action.scss +0 -2
  50. package/src/components/Actions/Actions.models.ts +0 -14
  51. package/src/components/Actions/Actions.stories.tsx +4 -1
  52. package/src/components/Buttons/Button/Button.scss +66 -20
  53. package/src/components/Buttons/CompositeButton/CompositeButton.scss +50 -18
  54. package/src/components/Buttons/TextButton/TextButton.scss +45 -16
  55. package/src/components/DateTime/DatePicker/DatePicker.scss +15 -6
  56. package/src/components/DateTime/DateTimePicker.scss +6 -2
  57. package/src/components/DynamicDataList/DynamicDataList.tsx +1 -0
  58. package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.scss +15 -5
  59. package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createInputRenderer/createInputRenderer.tsx +1 -1
  60. package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.scss +36 -12
  61. package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.tsx +2 -2
  62. package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.scss +26 -0
  63. package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.tsx +7 -1
  64. package/src/components/Filters/Filter/Filter.scss +8 -2
  65. package/src/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.scss +4 -1
  66. package/src/components/FormElements/Checkbox/Checkbox.scss +20 -0
  67. package/src/components/FormElements/CustomTags/CustomTags.scss +24 -0
  68. package/src/components/FormElements/CustomTags/CustomTags.tsx +8 -2
  69. package/src/components/FormElements/DateTimeField/DateTimeText.spec.tsx +1 -1
  70. package/src/components/FormElements/DateTimeField/DateTimeText.tsx +1 -1
  71. package/src/components/FormElements/FileUploadControl/FileUploadControl.scss +4 -2
  72. package/src/components/FormElements/FileUploadControl/FileUploadControl.tsx +12 -3
  73. package/src/components/FormElements/Radio/Radio.scss +18 -3
  74. package/src/components/FormElements/Radio/Radio.tsx +2 -2
  75. package/src/components/FormElements/Select/Select.scss +10 -0
  76. package/src/components/FormElements/SingleLineText/SingleLineText.spec.tsx +2 -2
  77. package/src/components/FormElements/SingleLineText/SingleLineText.tsx +1 -1
  78. package/src/components/FormElements/Tags/Tags.scss +14 -0
  79. package/src/components/FormElements/Tags/Tags.tsx +5 -1
  80. package/src/components/FormElements/TextArea/TextArea.spec.tsx +2 -2
  81. package/src/components/FormElements/TextArea/TextArea.tsx +1 -1
  82. package/src/components/FormElements/formStoryHelper.tsx +163 -97
  83. package/src/components/FormStation/FormStation.spec.tsx +13 -3
  84. package/src/components/FormStation/FormStation.tsx +43 -19
  85. package/src/components/FormStation/SaveOnNavigate/SaveOnNavigate.tsx +5 -0
  86. package/src/components/FormStation/SaveOnNavigate/handleNavigationAttempt.spec.ts +21 -0
  87. package/src/components/FormStation/SaveOnNavigate/handleNavigationAttempt.ts +2 -0
  88. package/src/components/FormStation/StationErrorStateType.tsx +5 -0
  89. package/src/components/FormStation/useValidationError.tsx +59 -0
  90. package/src/components/InfoPanel/InfoImage/InfoImage.scss +8 -3
  91. package/src/components/InfoPanel/InfoImage/InfoImage.tsx +1 -0
  92. package/src/components/List/List.tsx +3 -1
  93. package/src/components/List/ListCheckBox/ListCheckBox.scss +8 -3
  94. package/src/components/List/ListRow/ListRow.scss +0 -4
  95. package/src/components/Loaders/ImageLoader/ImageLoader.scss +27 -21
  96. package/src/components/Loaders/ImageLoader/ImageLoader.spec.tsx +8 -12
  97. package/src/components/Loaders/ImageLoader/ImageLoader.tsx +8 -3
  98. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.model.ts +0 -10
  99. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.scss +0 -1
  100. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.stories.tsx +5 -0
  101. package/src/components/index.ts +1 -1
  102. package/src/index.ts +1 -0
  103. package/src/initialize.ts +15 -12
  104. package/src/styles/variables.scss +32 -2
  105. package/src/utils/ErrorMapper/ApolloClient/ApolloErrorMapper.spec.ts +32 -0
  106. package/src/utils/ErrorMapper/ApolloClient/ApolloErrorMapper.tsx +33 -0
  107. package/src/utils/ErrorMapper/ErrorMapper.spec.ts +67 -0
  108. package/src/utils/ErrorMapper/ErrorMapper.ts +31 -0
  109. package/src/utils/ErrorMapper/ErrorMapper.type.ts +11 -0
  110. package/src/utils/ErrorMapper/index.ts +3 -0
  111. package/src/utils/ErrorTypeToStationError.spec.tsx +12 -0
  112. package/src/utils/ErrorTypeToStationError.tsx +5 -1
@@ -1,7 +1,18 @@
1
1
  import { faker } from '@faker-js/faker';
2
2
  import React, { useState } from 'react';
3
- import { CustomTags, ReadOnlyField, Select, SingleLineText, Tags } from '.';
3
+ import {
4
+ Checkbox,
5
+ CustomTags,
6
+ Divider,
7
+ FileUploadControl,
8
+ Radio,
9
+ ReadOnlyField,
10
+ Select,
11
+ SingleLineText,
12
+ Tags,
13
+ } from '.';
4
14
  import { generateItemArray } from '../../helpers/storybook';
15
+ import { TextButton } from '../Buttons';
5
16
  import { DynamicListColumn } from '../DynamicDataList/DynamicDataList.model';
6
17
  import {
7
18
  createInputRenderer,
@@ -10,6 +21,8 @@ import {
10
21
  import { formatDateTime } from '../Utils';
11
22
  import { BooleanViewField } from './BooleanView/BooleanViewField';
12
23
  import { DynamicDataListControl } from './DynamicDataListControl/DynamicDataListControl';
24
+ import { FileUpload } from './FileUploadControl/FileUploadControl';
25
+ import { TextArea } from './TextArea/TextArea';
13
26
 
14
27
  interface DynamicListStoryData {
15
28
  position: number;
@@ -67,13 +80,13 @@ const dataListColumns: DynamicListColumn<DynamicListStoryData>[] = [
67
80
  ];
68
81
 
69
82
  export const SampleForm: React.FC = () => useCreateForm().form;
70
-
71
83
  export const useCreateForm = (): {
72
84
  form: JSX.Element;
73
85
  title: string;
74
86
  } => {
75
87
  const [id] = useState(faker.datatype.uuid());
76
88
  const [title, setTitle] = useState(`The ${generateTitle()}`);
89
+ const [title2, setTitle2] = useState(`The ${generateTitle()}`);
77
90
  const [genres, setGenres] = useState<string[]>(['Crime', 'Drama']);
78
91
  const [ratings, setRatings] = useState<
79
92
  string | number | string[] | undefined
@@ -94,104 +107,157 @@ export const useCreateForm = (): {
94
107
  country: faker.address.country(),
95
108
  })),
96
109
  );
110
+ const [radio, setRadio] = useState(1);
111
+ const [check, setCheck] = useState<boolean>();
112
+ const [file, setFile] = useState<FileUpload>();
113
+
114
+ const [disabled, setDisabled] = useState<boolean>(false);
97
115
 
98
116
  return {
99
117
  form: (
100
- <form
101
- style={{
102
- display: 'grid',
103
- gridAutoRows: 'max-content',
104
- gridTemplateColumns: '1fr',
105
- rowGap: '20px',
106
- }}
107
- >
108
- <ReadOnlyField label="Id" value={id} />
109
- <SingleLineText
110
- name="title"
111
- label="Title"
112
- value={title}
113
- onChange={(e) => setTitle(e.currentTarget.value)}
114
- />
115
- <ReadOnlyField label="Publish State" value="PUBLISHED" />
116
- <BooleanViewField
117
- label="Status"
118
- value={status}
119
- trueLabel="Enabled"
120
- falseLabel="Disabled"
121
- />
122
- <Tags
123
- name="genres"
124
- label="Genre(s)"
125
- value={genres}
126
- tagsOptions={genresOptions}
127
- onChange={(e) =>
128
- setGenres(e.currentTarget.value as unknown as string[])
129
- }
130
- />
131
- <Select
132
- name="ratings"
133
- label="Rating"
134
- value={ratings}
135
- options={selectOptions}
136
- onChange={(e) => setRatings(e.currentTarget.value)}
137
- />
138
- <SingleLineText
139
- name="shortDescription"
140
- label="Short Description"
141
- value={shortDescription}
142
- placeholder="Enter a short description..."
143
- onChange={(e) => setShortDescription(e.currentTarget.value)}
144
- />
145
- <SingleLineText
146
- name="longDescription"
147
- label="Long Description"
148
- value={longDescription}
149
- placeholder="Enter a description..."
150
- onChange={(e) => setLongDescription(e.currentTarget.value)}
151
- />
152
- <CustomTags
153
- name="cast"
154
- value={cast}
155
- label="Cast"
156
- onChange={(e) =>
157
- setCast(e.currentTarget.value as unknown as string[])
158
- }
159
- />
160
- <CustomTags
161
- name="directors"
162
- value={directors}
163
- label="Director(s)"
164
- onChange={(e) =>
165
- setDirectors(e.currentTarget.value as unknown as string[])
166
- }
167
- />
168
- <Tags
169
- name="locales"
170
- value={locales}
171
- label="Locale(s)"
172
- tagsOptions={['en_US', 'pt_PT', 'zh_CN']}
173
- onChange={(e) =>
174
- setLocales(e.currentTarget.value as unknown as string[])
175
- }
176
- />
177
- <DynamicDataListControl
178
- name={'dataList'}
179
- label={'Subtitles'}
180
- columns={dataListColumns}
181
- value={dataList}
182
- positionPropertyName={'position'}
183
- allowReordering={true}
184
- allowRowDragging={true}
185
- allowNewData={true}
186
- onChange={(e) => setDataList(e as DynamicListStoryData[])}
187
- />
188
- <ReadOnlyField
189
- label="Created At"
190
- value={createdTime.toISOString()}
191
- transform={formatDateTime}
192
- />
193
- <ReadOnlyField label="Writer(s)" value={writers} />
194
- </form>
118
+ <>
119
+ <div style={{ marginBottom: 10 }}>
120
+ <TextButton
121
+ onButtonClicked={() => setDisabled(!disabled)}
122
+ text="Toggle Disabled"
123
+ />
124
+ </div>
125
+ <Divider />
126
+ <form
127
+ style={{
128
+ display: 'grid',
129
+ gridAutoRows: 'max-content',
130
+ gridTemplateColumns: '1fr',
131
+ rowGap: '20px',
132
+ }}
133
+ >
134
+ <ReadOnlyField label="Id" value={id} />
135
+ <SingleLineText
136
+ name="title"
137
+ label="Title"
138
+ value={title}
139
+ disabled={disabled}
140
+ onChange={(e) => setTitle(e.currentTarget.value)}
141
+ />
142
+ <ReadOnlyField label="Publish State" value="PUBLISHED" />
143
+ <BooleanViewField
144
+ label="Status"
145
+ value={status}
146
+ trueLabel="Enabled"
147
+ falseLabel="Disabled"
148
+ />
149
+ <Tags
150
+ name="genres"
151
+ label="Genre(s)"
152
+ value={genres}
153
+ tagsOptions={genresOptions}
154
+ onChange={(e) =>
155
+ setGenres(e.currentTarget.value as unknown as string[])
156
+ }
157
+ disabled={disabled}
158
+ />
159
+ <Select
160
+ name="ratings"
161
+ label="Rating"
162
+ value={ratings}
163
+ options={selectOptions}
164
+ onChange={(e) => setRatings(e.currentTarget.value)}
165
+ disabled={disabled}
166
+ />
167
+ <SingleLineText
168
+ name="shortDescription"
169
+ label="Short Description"
170
+ value={shortDescription}
171
+ placeholder="Enter a short description..."
172
+ onChange={(e) => setShortDescription(e.currentTarget.value)}
173
+ disabled={disabled}
174
+ />
175
+ <TextArea
176
+ name="longDescription"
177
+ label="Long Description"
178
+ value={longDescription}
179
+ placeholder="Enter a description..."
180
+ onChange={(e) => setLongDescription(e.currentTarget.value)}
181
+ disabled={disabled}
182
+ />
183
+ <CustomTags
184
+ name="cast"
185
+ value={cast}
186
+ label="Cast"
187
+ onChange={(e) =>
188
+ setCast(e.currentTarget.value as unknown as string[])
189
+ }
190
+ disabled={disabled}
191
+ />
192
+ <CustomTags
193
+ name="directors"
194
+ value={directors}
195
+ label="Director(s)"
196
+ onChange={(e) =>
197
+ setDirectors(e.currentTarget.value as unknown as string[])
198
+ }
199
+ disabled={disabled}
200
+ />
201
+ <Tags
202
+ name="locales"
203
+ value={locales}
204
+ label="Locale(s)"
205
+ tagsOptions={['en_US', 'pt_PT', 'zh_CN']}
206
+ onChange={(e) =>
207
+ setLocales(e.currentTarget.value as unknown as string[])
208
+ }
209
+ disabled={disabled}
210
+ />
211
+ <DynamicDataListControl
212
+ name={'dataList'}
213
+ label={'Subtitles'}
214
+ columns={dataListColumns}
215
+ value={dataList}
216
+ positionPropertyName={'position'}
217
+ allowReordering={true}
218
+ allowRowDragging={true}
219
+ allowNewData={true}
220
+ onChange={(e) => setDataList(e as DynamicListStoryData[])}
221
+ disabled={disabled}
222
+ />
223
+
224
+ <Radio
225
+ value={radio}
226
+ onChange={(e) => setRadio(Number(e.currentTarget.value))}
227
+ options={[
228
+ { value: 1, label: 'A' },
229
+ { value: 2, label: 'B' },
230
+ { value: 3, label: 'C' },
231
+ ]}
232
+ name={'radio'}
233
+ label={'Category'}
234
+ disabled={disabled}
235
+ />
236
+
237
+ <Checkbox
238
+ value={check}
239
+ onChange={setCheck}
240
+ name={'check'}
241
+ label={'Is Premium?'}
242
+ disabled={disabled}
243
+ />
244
+
245
+ <FileUploadControl
246
+ name={'file'}
247
+ value={file}
248
+ onFileSelected={(file) => setFile(file)}
249
+ placeholder={'Select a file'}
250
+ label={'File'}
251
+ disabled={disabled}
252
+ />
253
+ <ReadOnlyField
254
+ label="Created At"
255
+ value={createdTime.toISOString()}
256
+ transform={formatDateTime}
257
+ />
258
+ <ReadOnlyField label="Writer(s)" value={writers} />
259
+ </form>
260
+ </>
195
261
  ),
196
262
  title,
197
263
  };
@@ -5,7 +5,7 @@ import { act } from 'react-dom/test-utils';
5
5
  import { MemoryRouter, Route } from 'react-router-dom';
6
6
  import * as Yup from 'yup';
7
7
  import { noop } from '../../helpers/utils';
8
- import { hideSaveIndicator, showSaveIndicator } from '../../initialize';
8
+ import { IndicatorType, setSaveIndicator } from '../../initialize';
9
9
  import { ActionData, Actions } from '../Actions';
10
10
  import { Action } from '../Actions/Action';
11
11
  import { MessageBar } from '../MessageBar/MessageBar';
@@ -675,6 +675,11 @@ describe('Details', () => {
675
675
  </FormStation>
676
676
  </MemoryRouter>,
677
677
  );
678
+ expect(setSaveIndicator).toHaveBeenNthCalledWith(
679
+ 1,
680
+ IndicatorType.Inactive,
681
+ ); // 1. inactive 2. dirty
682
+ expect(setSaveIndicator).toHaveBeenNthCalledWith(2, IndicatorType.Dirty); // 1. inactive 2. dirty
678
683
 
679
684
  // submit form
680
685
  const actionSelected = wrapper
@@ -692,7 +697,7 @@ describe('Details', () => {
692
697
 
693
698
  wrapper.update();
694
699
 
695
- expect(showSaveIndicator).toHaveBeenCalledTimes(1);
700
+ expect(setSaveIndicator).toHaveBeenNthCalledWith(3, IndicatorType.Saving);
696
701
 
697
702
  // complete form submission
698
703
  await act(async () => {
@@ -700,7 +705,12 @@ describe('Details', () => {
700
705
  });
701
706
  wrapper.update();
702
707
 
703
- expect(hideSaveIndicator).toHaveBeenCalledTimes(1);
708
+ expect(setSaveIndicator).toHaveBeenNthCalledWith(
709
+ 4,
710
+ IndicatorType.Inactive,
711
+ );
712
+
713
+ console.warn((setSaveIndicator as jest.Mock).mock.calls);
704
714
  });
705
715
  });
706
716
  });
@@ -16,7 +16,7 @@ import React, {
16
16
  } from 'react';
17
17
  import { useHistory } from 'react-router-dom';
18
18
  import { OptionalObjectSchema } from 'yup/lib/object';
19
- import { hideSaveIndicator, showSaveIndicator } from '../../initialize';
19
+ import { IndicatorType, setSaveIndicator } from '../../initialize';
20
20
  import { Data } from '../../types/data';
21
21
  import { ErrorTypeToStationError } from '../../utils/ErrorTypeToStationError';
22
22
  import { Actions, ActionsProps } from '../Actions';
@@ -28,10 +28,12 @@ import {
28
28
  PageHeaderActionType,
29
29
  PageHeaderProps,
30
30
  } from '../PageHeader';
31
- import { ErrorType, StationError, StationMessage } from '../models';
31
+ import { ErrorType, StationMessage } from '../models';
32
32
  import { FormActionData, InitialFormData } from './FormStation.models';
33
33
  import classes from './FormStation.scss';
34
34
  import { SaveOnNavigate } from './SaveOnNavigate/SaveOnNavigate';
35
+ import { StationErrorStateType } from './StationErrorStateType';
36
+ import { useValidationError } from './useValidationError';
35
37
 
36
38
  export type ObjectSchemaDefinition<
37
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -103,7 +105,8 @@ interface FormActionProps<T, Y> extends Omit<ActionsProps, 'actions'> {
103
105
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
106
  validationSchema?: OptionalObjectSchema<ObjectSchemaDefinition<any>>;
105
107
  actions?: FormActionData<T, Y>[];
106
- setStationError: (error: StationError) => void;
108
+ setStationError: (error: StationErrorStateType) => void;
109
+ setValidationError: () => void;
107
110
  submitResponse?: React.MutableRefObject<Y | undefined>;
108
111
  alwaysSubmitBeforeAction?: boolean;
109
112
  className?: string;
@@ -131,8 +134,12 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
131
134
  }: PropsWithChildren<
132
135
  FormStationProps<TValues, TSubmitResponse>
133
136
  >): JSX.Element => {
134
- const [stationError, setStationError] = useState<StationError>();
135
- const [isLoadingError, setIsLoadingError] = useState(false);
137
+ const [stationError, setStationError] = useState<StationErrorStateType>();
138
+
139
+ const { setValidationError, validationWatcher } = useValidationError(
140
+ stationError,
141
+ setStationError,
142
+ );
136
143
 
137
144
  const submitResponse = useRef<TSubmitResponse>();
138
145
  const [isFormSubmitting, setIsFormSubmitting] = useState<boolean>(false);
@@ -148,7 +155,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
148
155
 
149
156
  try {
150
157
  setIsFormSubmitting(true);
151
- showSaveIndicator();
158
+ setSaveIndicator(IndicatorType.Saving);
152
159
  setStationError(undefined);
153
160
  if (!initialData.loading && saveData) {
154
161
  const response = await saveData(values, initialData, formikHelpers);
@@ -164,13 +171,14 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
164
171
  ),
165
172
  );
166
173
 
174
+ setSaveIndicator(IndicatorType.Dirty);
175
+
167
176
  // We still throw the error, to make sure that navigation or action execution
168
177
  // will not continue after a failed save.
169
178
  throw error;
170
179
  } finally {
171
180
  formikHelpers.setSubmitting(false);
172
181
  setIsFormSubmitting(false);
173
- hideSaveIndicator();
174
182
  }
175
183
  },
176
184
  [isFormSubmitting, initialData, saveData, setStationError],
@@ -182,19 +190,20 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
182
190
  (initialData.data === null && !initialData.loading) ||
183
191
  initialData.entityNotFound
184
192
  ) {
185
- const stationError = ErrorTypeToStationError(
186
- initialData.error,
187
- 'An error occurred when trying to load data.',
188
- 'Entity not found',
189
- );
193
+ const stationError = {
194
+ ...ErrorTypeToStationError(
195
+ initialData.error,
196
+ 'An error occurred when trying to load data.',
197
+ 'Entity not found',
198
+ ),
199
+ type: 'loading',
200
+ };
190
201
 
191
202
  setStationError(stationError);
192
- setIsLoadingError(true);
193
203
  } else {
194
- if (isLoadingError === true) {
204
+ if (stationError?.type === 'loading') {
195
205
  // Only clear the error if it is a loading error, which now seems to be cleared.
196
206
  setStationError(undefined);
197
- setIsLoadingError(false);
198
207
  }
199
208
  }
200
209
  }, [
@@ -202,7 +211,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
202
211
  initialData.error,
203
212
  initialData.entityNotFound,
204
213
  initialData.data,
205
- isLoadingError,
214
+ stationError?.type,
206
215
  ]);
207
216
 
208
217
  const getContent = (): JSX.Element | undefined => {
@@ -223,7 +232,10 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
223
232
  // Loading successful
224
233
  return (
225
234
  <>
226
- <SaveOnNavigate isSubmitting={isFormSubmitting} />
235
+ <SaveOnNavigate
236
+ isSubmitting={isFormSubmitting}
237
+ onNavigationCancelled={setValidationError}
238
+ />
227
239
  <div className={classes.main}>
228
240
  <div
229
241
  className={clsx(classes.formWrapper, {
@@ -272,6 +284,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
272
284
  enableReinitialize={true}
273
285
  >
274
286
  <>
287
+ {validationWatcher}
275
288
  <FormStationHeader
276
289
  titleProperty={titleProperty as string}
277
290
  defaultTitle={defaultTitle}
@@ -296,6 +309,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
296
309
  width={actionsWidth}
297
310
  validationSchema={validationSchema}
298
311
  setStationError={setStationError}
312
+ setValidationError={setValidationError}
299
313
  submitResponse={submitResponse}
300
314
  alwaysSubmitBeforeAction={alwaysSubmitBeforeAction}
301
315
  className={classes.actionsPanel}
@@ -317,6 +331,7 @@ const FormStationAction = <T, Y>(
317
331
  actions,
318
332
  validationSchema,
319
333
  setStationError,
334
+ setValidationError,
320
335
  submitResponse,
321
336
  alwaysSubmitBeforeAction,
322
337
  className = '',
@@ -352,8 +367,7 @@ const FormStationAction = <T, Y>(
352
367
  !isValid ||
353
368
  (await validationSchema?.isValid(values)) === false
354
369
  ) {
355
- // eslint-disable-next-line no-console
356
- console.log('form invalid, action not performed');
370
+ setValidationError();
357
371
  // Making sure that the fields will actually show the validation messages (they won't if the from was not touched yet - e.g. on a create station)
358
372
  validateForm();
359
373
  return;
@@ -405,6 +419,7 @@ const FormStationAction = <T, Y>(
405
419
  isValid,
406
420
  resetForm,
407
421
  setStationError,
422
+ setValidationError,
408
423
  submitForm,
409
424
  submitResponse,
410
425
  validateForm,
@@ -431,6 +446,15 @@ const FormStationHeader: React.FC<
431
446
  > = ({ titleProperty, defaultTitle, subtitle, cancelNavigationUrl }) => {
432
447
  const { dirty, resetForm, values } = useFormikContext<FormikValues>();
433
448
 
449
+ useEffect(() => {
450
+ // Set the save indicator to dirty depending on the form state
451
+ if (dirty) {
452
+ setSaveIndicator(IndicatorType.Dirty);
453
+ } else {
454
+ setSaveIndicator(IndicatorType.Inactive);
455
+ }
456
+ }, [dirty]);
457
+
434
458
  const history = useHistory();
435
459
 
436
460
  const title =
@@ -1,11 +1,14 @@
1
1
  import { useFormikContext } from 'formik';
2
2
  import React, { useState } from 'react';
3
+ import { noop } from '../../../helpers/utils';
3
4
  import { NavigationAPI, useReactRouterPause } from '../../../hooks';
4
5
  import { handleNavigationAttempt } from './handleNavigationAttempt';
5
6
 
6
7
  interface SaveOnNavigateProps {
7
8
  /** If set to true, will prevent form submission when navigating away. (default: false) */
8
9
  isSubmitting?: boolean;
10
+ /** Callback that will be called when a navigation attempt was cancelled */
11
+ onNavigationCancelled?: () => void;
9
12
  }
10
13
 
11
14
  /**
@@ -13,6 +16,7 @@ interface SaveOnNavigateProps {
13
16
  */
14
17
  export const SaveOnNavigate: React.FC<SaveOnNavigateProps> = ({
15
18
  isSubmitting = false,
19
+ onNavigationCancelled = noop,
16
20
  }) => {
17
21
  const { dirty, isValid, submitForm } = useFormikContext();
18
22
  const [canSubmit, setCanSubmit] = useState<boolean>(true);
@@ -28,6 +32,7 @@ export const SaveOnNavigate: React.FC<SaveOnNavigateProps> = ({
28
32
  isSubmitting,
29
33
  canSubmit,
30
34
  setCanSubmit,
35
+ onNavigationCancelled,
31
36
  ),
32
37
  },
33
38
  dirty,