Skip to content

Commit 7dafd35

Browse files
committed
refactor the subscription logic
1 parent 80d6694 commit 7dafd35

14 files changed

+203
-153
lines changed

src/admin/units/units.component.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ import {
2525
COLUMN_FILTER_MODE_OPTIONS,
2626
COLUMN_FILTER_VARIANTS,
2727
TableBodyCellOverFlowTip,
28-
TableHeaderOverflowTip,
2928
TableCellOverFlowTipProps,
29+
TableHeaderOverflowTip,
30+
customFilterFunctions,
3031
displayTableRowCountText,
3132
formatDateTimeStrings,
3233
getInitialColumnFilterFnState,
3334
getPageHeightCalc,
3435
mrtTheme,
35-
customFilterFunctions,
3636
} from '../../utils';
3737
import DeleteUnitDialog from './deleteUnitsDialog.component.tsx';
3838
import UnitsDialog from './unitsDialog.component.tsx';

src/catalogue/category/catalogueCategoryDialog.component.tsx

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import { AxiosError } from 'axios';
2121
import React from 'react';
2222
import { Controller, FormProvider, useForm } from 'react-hook-form';
2323
import {
24-
APIError,
2524
AllowedValues,
25+
APIError,
2626
CatalogueCategory,
2727
CatalogueCategoryPost,
2828
CatalogueCategoryPostProperty,
@@ -40,6 +40,7 @@ import {
4040
} from '../../app.types';
4141
import { CatalogueCategorySchema, RequestType } from '../../form.schemas';
4242
import handleIMS_APIError from '../../handleIMS_APIError';
43+
import { createFormControlWithRootErrorClearing } from '../../utils';
4344
import CatalogueItemsPropertiesTable from './property/catalogueItemPropertiesTable.component';
4445

4546
// Function to convert a list of strings to a list of numbers
@@ -139,6 +140,38 @@ function transformPostPropertyToAddProperty(
139140
return modifiedCatalogueItemProperty;
140141
}
141142

143+
// Using `any` instead of `FieldPath` to avoid circular dependencies
144+
function getProperty<T extends Record<string, unknown>>(
145+
obj: T,
146+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
147+
key: any
148+
) {
149+
if (key === undefined) return undefined;
150+
151+
const keys = key.toString().split('.');
152+
let current: unknown = obj;
153+
154+
for (const part of keys) {
155+
if (current && typeof current === 'object' && part in current) {
156+
current = (current as Record<string, unknown>)[part];
157+
} else {
158+
return undefined;
159+
}
160+
}
161+
162+
return current;
163+
}
164+
165+
const formControl =
166+
createFormControlWithRootErrorClearing<AddCatalogueCategoryWithPlacementIds>({
167+
// @ts-expect-error: The callback is missing name, when the type exist
168+
customCallback: ({ name, errors }) => {
169+
if (errors && !!getProperty(errors, name)) {
170+
formControl.clearErrors(name);
171+
}
172+
},
173+
});
174+
142175
export interface CatalogueCategoryDialogProps {
143176
open: boolean;
144177
onClose: () => void;
@@ -187,6 +220,7 @@ const CatalogueCategoryDialog = (props: CatalogueCategoryDialogProps) => {
187220
}, [requestType, duplicate, selectedCatalogueCategory]);
188221

189222
const formMethods = useForm<AddCatalogueCategoryWithPlacementIds>({
223+
formControl,
190224
resolver: zodResolver(CatalogueCategorySchema),
191225
defaultValues: initialCatalogueCategory,
192226
});
@@ -285,9 +319,7 @@ const CatalogueCategoryDialog = (props: CatalogueCategoryDialogProps) => {
285319
if (requestType === 'patch') {
286320
handleEditCatalogueCategory(transformedData);
287321
} else {
288-
handleAddCatalogueCategory({
289-
...transformedData,
290-
});
322+
handleAddCatalogueCategory({ ...transformedData });
291323
}
292324
};
293325

src/catalogue/category/property/propertyDialog.component.tsx

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -49,28 +49,6 @@ import {
4949
import { transformAllowedValues } from '../catalogueCategoryDialog.component';
5050
import AllowedValuesListTextFields from './allowedValuesListTextFields.component';
5151

52-
// Using `any` instead of `FieldPath` to avoid circular dependencies
53-
function getProperty<T extends Record<string, unknown>>(
54-
obj: T,
55-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
56-
key: any
57-
) {
58-
if (key === undefined) return undefined;
59-
60-
const keys = key.toString().split('.');
61-
let current: unknown = obj;
62-
63-
for (const part of keys) {
64-
if (current && typeof current === 'object' && part in current) {
65-
current = (current as Record<string, unknown>)[part];
66-
} else {
67-
return undefined;
68-
}
69-
}
70-
71-
return current;
72-
}
73-
7452
interface MigrationWarningMessageProps {
7553
isChecked: boolean;
7654
setIsChecked: (isChecked: boolean) => void;
@@ -155,21 +133,10 @@ const PropertyDialog = (props: PropertyDialogProps) => {
155133
setValue: setValueAdd,
156134
resetField: resetFieldAdd,
157135
trigger: triggerAdd,
158-
clearErrors: clearErrorsAdd,
159136
} = formMethodsAdd;
160137

161138
const propertyAdd = watchAdd();
162139

163-
// Clears form errors when a value has been changed
164-
React.useEffect(() => {
165-
const subscription = watchAdd((_, type) => {
166-
if (type.name && !!getProperty(errorsAdd, type.name)) {
167-
clearErrorsAdd(type.name);
168-
}
169-
});
170-
return () => subscription.unsubscribe();
171-
}, [clearErrorsAdd, errorsAdd, watchAdd]);
172-
173140
const allowedValuesTypeAdd =
174141
propertyAdd.properties &&
175142
propertyAdd?.properties[index]?.allowed_values?.type;

src/catalogue/items/catalogueItemsDialog.component.test.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,20 @@ describe('Catalogue Items Dialog', () => {
10821082
)
10831083
).toBeInTheDocument();
10841084
});
1085+
1086+
await user.click(screen.getByRole('button', { name: 'Back' }));
1087+
1088+
await modifyValues({
1089+
name: 'test',
1090+
});
1091+
1092+
await waitFor(() =>
1093+
expect(
1094+
screen.queryByText(
1095+
"There have been no changes made. Please change a field's value or press Cancel to exit."
1096+
)
1097+
).not.toBeInTheDocument()
1098+
);
10851099
});
10861100

10871101
it('displays error message when editing manufacturer_id if catalogue item has child elements', async () => {
@@ -1109,8 +1123,8 @@ describe('Catalogue Items Dialog', () => {
11091123
await waitFor(() => {
11101124
expect(
11111125
screen.getByText(
1112-
'Unable to update catalogue item properties and manufacturer '
1113-
+ '(Manufacturer A), as the catalogue item has associated items.'
1126+
'Unable to update catalogue item properties and manufacturer ' +
1127+
'(Manufacturer A), as the catalogue item has associated items.'
11141128
)
11151129
).toBeInTheDocument();
11161130
});
@@ -1152,8 +1166,8 @@ describe('Catalogue Items Dialog', () => {
11521166
await waitFor(() => {
11531167
expect(
11541168
screen.getByText(
1155-
'Unable to update catalogue item properties and manufacturer '
1156-
+ '(Manufacturer A), as the catalogue item has associated items.'
1169+
'Unable to update catalogue item properties and manufacturer ' +
1170+
'(Manufacturer A), as the catalogue item has associated items.'
11571171
)
11581172
).toBeInTheDocument();
11591173
});

src/catalogue/items/catalogueItemsDialog.component.tsx

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ import {
5252
} from '../../form.schemas';
5353
import handleIMS_APIError from '../../handleIMS_APIError';
5454
import ManufacturerDialog from '../../manufacturer/manufacturerDialog.component';
55-
import { sortDataList } from '../../utils';
55+
import {
56+
createFormControlWithRootErrorClearing,
57+
sortDataList,
58+
} from '../../utils';
5659

5760
const RECENT_MANUFACTURER_CUTOFF_TIME = 10 * 60 * 1000;
5861

@@ -157,6 +160,15 @@ function convertToCatalogueItemDetailsStepPost(
157160
};
158161
}
159162

163+
const formControlPropertiesStep =
164+
createFormControlWithRootErrorClearing<PropertiesStep>();
165+
166+
const formControlDetailsStep =
167+
createFormControlWithRootErrorClearing<CatalogueItemDetailsStep>({
168+
customCallback: () =>
169+
formControlPropertiesStep.clearErrors('root.formError'),
170+
});
171+
160172
export interface CatalogueItemsDialogProps {
161173
open: boolean;
162174
onClose: () => void;
@@ -181,8 +193,9 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
181193
[parentInfo]
182194
);
183195

184-
const CatalogueItemDetailsStepFormMethods = useForm<CatalogueItemDetailsStep>(
196+
const catalogueItemDetailsStepFormMethods = useForm<CatalogueItemDetailsStep>(
185197
{
198+
formControl: formControlDetailsStep,
186199
resolver: zodResolver(CatalogueItemDetailsStepSchema(requestType)),
187200
defaultValues: toCatalogueItemDetailsStep(selectedCatalogueItem),
188201
}
@@ -195,10 +208,10 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
195208
control: controlDetailsStep,
196209
clearErrors: clearErrorsDetailsStep,
197210
reset: resetDetailsStep,
198-
watch: watchDetailsStep,
199-
} = CatalogueItemDetailsStepFormMethods;
211+
} = catalogueItemDetailsStepFormMethods;
200212

201213
const catalogueItemPropertiesStepFormMethods = useForm<PropertiesStep>({
214+
formControl: formControlPropertiesStep,
202215
resolver: zodResolver(PropertiesStepSchema),
203216
defaultValues: {
204217
properties: convertToPropertyValueList(
@@ -215,7 +228,6 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
215228
control: controlPropertiesStep,
216229
clearErrors: clearErrorsPropertiesStep,
217230
reset: resetPropertiesStep,
218-
watch: watchPropertiesStep,
219231
setError: setErrorPropertiesStep,
220232
} = catalogueItemPropertiesStepFormMethods;
221233

@@ -251,21 +263,6 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
251263
selectedCatalogueItem?.properties,
252264
]);
253265

254-
// Clears form errors when a value has been changed
255-
React.useEffect(() => {
256-
const subscription1 = watchDetailsStep(() =>
257-
clearErrorsPropertiesStep('root.formError')
258-
);
259-
return () => subscription1.unsubscribe();
260-
}, [clearErrorsPropertiesStep, watchDetailsStep]);
261-
262-
React.useEffect(() => {
263-
const subscription = watchPropertiesStep(() =>
264-
clearErrorsPropertiesStep('root.formError')
265-
);
266-
return () => subscription.unsubscribe();
267-
}, [clearErrorsPropertiesStep, watchPropertiesStep]);
268-
269266
const { mutateAsync: postCatalogueItem, isPending: isAddPending } =
270267
usePostCatalogueItem();
271268
const { mutateAsync: patchCatalogueItem, isPending: isEditPending } =
@@ -387,12 +384,17 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
387384
if (response && error.response?.status === 409) {
388385
if (response.detail.includes('child elements')) {
389386
// find the name of the manufacturer, so it can be used in the error message
390-
const manufacturerName = manufacturerList?.find(
391-
(manufacturer) => manufacturer.id === selectedCatalogueItem?.manufacturer_id
392-
) || null;
387+
const manufacturerName =
388+
manufacturerList?.find(
389+
(manufacturer) =>
390+
manufacturer.id ===
391+
selectedCatalogueItem?.manufacturer_id
392+
) || null;
393393
// add the manufacturer name into the error message
394-
const childElementsMessage = "Unable to update catalogue item properties and manufacturer ("
395-
+ manufacturerName?.name + "), as the catalogue item has associated items.";
394+
const childElementsMessage =
395+
'Unable to update catalogue item properties and manufacturer (' +
396+
manufacturerName?.name +
397+
'), as the catalogue item has associated items.';
396398
setErrorPropertiesStep('root.formError', {
397399
message: childElementsMessage,
398400
});
@@ -504,10 +506,7 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
504506
const options = (): Array<Manufacturer & { isRecent: string }> => {
505507
const classifiedManufacturers = manufacturerList
506508
? manufacturerList.map((option) => {
507-
return {
508-
...option,
509-
isRecent: 'A-Z',
510-
};
509+
return { ...option, isRecent: 'A-Z' };
511510
})
512511
: [];
513512

@@ -521,10 +520,7 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
521520
return isRecent;
522521
})
523522
.map((option) => {
524-
return {
525-
...option,
526-
isRecent: 'Recently Added',
527-
};
523+
return { ...option, isRecent: 'Recently Added' };
528524
});
529525

530526
/*returns them in reverse alphabetical order, since they will be sorted by "isRecent",
@@ -940,10 +936,8 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
940936
sx={{ marginTop: 2 }}
941937
>
942938
{STEPS.map((label, index) => {
943-
const labelProps: {
944-
optional?: React.ReactNode;
945-
error?: boolean;
946-
} = {};
939+
const labelProps: { optional?: React.ReactNode; error?: boolean } =
940+
{};
947941

948942
if (isStepFailed(index)) {
949943
labelProps.optional = (
@@ -1002,11 +996,7 @@ function CatalogueItemsDialog(props: CatalogueItemsDialogProps) {
1002996
)}
1003997
</DialogActions>
1004998
<Box
1005-
sx={{
1006-
width: '100%',
1007-
justifyContent: 'center',
1008-
alignItems: 'center',
1009-
}}
999+
sx={{ width: '100%', justifyContent: 'center', alignItems: 'center' }}
10101000
>
10111001
{errorsPropertiesStep.root?.formError && (
10121002
<FormHelperText sx={{ marginBottom: 2, textAlign: 'center' }} error>

src/common/editFileDialog.component.test.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ vi.mock('../handleIMS_APIError');
1616

1717
describe('Edit file dialog', () => {
1818
const onClose = vi.fn();
19-
let axiosPatchSpy : MockInstance;
19+
let axiosPatchSpy: MockInstance;
2020
let props: FileDialogProps;
2121
let user: UserEvent;
2222
const createView = () => {
@@ -121,6 +121,18 @@ describe('Edit file dialog', () => {
121121
"There have been no changes made. Please change a field's value or press Cancel to exit."
122122
)
123123
).toBeInTheDocument();
124+
125+
modifyFileValues({
126+
file_name: 'test_file_name.jpeg',
127+
});
128+
129+
await waitFor(() =>
130+
expect(
131+
screen.queryByText(
132+
"There have been no changes made. Please change a field's value or press Cancel to exit."
133+
)
134+
).not.toBeInTheDocument()
135+
);
124136
});
125137

126138
it('shows error message if required fields are whitespace or their current value was removed', async () => {

0 commit comments

Comments
 (0)