@pautena/react-design-system 0.2.1 → 0.4.0
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.
- package/dist/cjs/index.js +13 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/enhanced-select/enhanced-select.d.ts +15 -0
- package/dist/cjs/types/components/enhanced-select/index.d.ts +1 -0
- package/dist/cjs/types/components/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/value-datetime/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/value-datetime/value-datetime.d.ts +18 -0
- package/dist/cjs/types/generators/generators.mock.d.ts +9 -5
- package/dist/cjs/types/generators/generators.model.d.ts +25 -1
- package/dist/cjs/types/generators/model-router/model-router.types.d.ts +1 -0
- package/dist/cjs/types/generators/model-router/screens/details-screen.d.ts +1 -1
- package/dist/cjs/types/hooks/index.d.ts +1 -0
- package/dist/cjs/types/hooks/routing/index.d.ts +1 -0
- package/dist/cjs/types/hooks/routing/routing.hooks.d.ts +5 -0
- package/dist/cjs/types/providers/notification-center/index.d.ts +1 -0
- package/dist/cjs/types/providers/notification-center/notification-center.hooks.d.ts +6 -0
- package/dist/esm/index.js +13 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/enhanced-select/enhanced-select.d.ts +15 -0
- package/dist/esm/types/components/enhanced-select/index.d.ts +1 -0
- package/dist/esm/types/components/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/value-datetime/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/value-datetime/value-datetime.d.ts +18 -0
- package/dist/esm/types/generators/generators.mock.d.ts +9 -5
- package/dist/esm/types/generators/generators.model.d.ts +25 -1
- package/dist/esm/types/generators/model-router/model-router.types.d.ts +1 -0
- package/dist/esm/types/generators/model-router/screens/details-screen.d.ts +1 -1
- package/dist/esm/types/hooks/index.d.ts +1 -0
- package/dist/esm/types/hooks/routing/index.d.ts +1 -0
- package/dist/esm/types/hooks/routing/routing.hooks.d.ts +5 -0
- package/dist/esm/types/providers/notification-center/index.d.ts +1 -0
- package/dist/esm/types/providers/notification-center/notification-center.hooks.d.ts +6 -0
- package/dist/index.d.ts +66 -2
- package/package.json +6 -2
- package/src/components/enhanced-select/enhanced-select.stories.tsx +70 -0
- package/src/components/enhanced-select/enhanced-select.test.tsx +90 -0
- package/src/components/enhanced-select/enhanced-select.tsx +84 -0
- package/src/components/enhanced-select/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/components/value-displays/index.ts +1 -0
- package/src/components/value-displays/value-datetime/index.ts +1 -0
- package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +21 -0
- package/src/components/value-displays/value-datetime/value-datetime.test.tsx +23 -0
- package/src/components/value-displays/value-datetime/value-datetime.tsx +40 -0
- package/src/components/value-displays/value-text/{value-test.test.tsx → value-text.test.tsx} +0 -0
- package/src/generators/generators.mock.ts +56 -17
- package/src/generators/generators.model.ts +39 -1
- package/src/generators/model-form/model-form.stories.tsx +2 -2
- package/src/generators/model-form/model-form.test.tsx +39 -22
- package/src/generators/model-form/model-form.tsx +220 -33
- package/src/generators/model-router/model-router.test.tsx +285 -65
- package/src/generators/model-router/model-router.types.ts +4 -0
- package/src/generators/model-router/screens/add-screen.tsx +16 -20
- package/src/generators/model-router/screens/details-screen.tsx +3 -2
- package/src/generators/model-router/screens/list-screen.tsx +17 -0
- package/src/generators/model-router/screens/update-screen.tsx +22 -13
- package/src/generators/model-router/stories/model-router.stories.tsx +6 -2
- package/src/generators/object-details/object-details.tsx +5 -4
- package/src/hooks/index.ts +1 -0
- package/src/hooks/routing/index.ts +1 -0
- package/src/hooks/routing/routing.hooks.ts +23 -0
- package/src/hooks/routing/routing.test.tsx +83 -0
- package/src/providers/notification-center/index.ts +1 -0
- package/src/providers/notification-center/notification-center.hooks.ts +23 -0
- package/src/providers/notification-center/notification-center.test.tsx +87 -1
- package/src/storybook.tsx +10 -0
- package/src/tests/actions.ts +43 -0
- package/src/tests/assertions.ts +75 -1
- package/src/tests/index.ts +1 -0
- package/src/tests/testing-library.tsx +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { DummyModelRouter, InternalModelRouter } from "./stories/model-router.stories";
|
|
3
3
|
import {
|
|
4
4
|
expectModelFieldInputExist,
|
|
@@ -9,16 +9,34 @@ import {
|
|
|
9
9
|
TestRouter,
|
|
10
10
|
expectModelFieldValue,
|
|
11
11
|
expectModelFieldInputValue,
|
|
12
|
+
selectOption,
|
|
12
13
|
} from "~/tests";
|
|
13
14
|
import { data as mockData } from "./stories/templates";
|
|
14
15
|
import userEvent from "@testing-library/user-event";
|
|
15
16
|
import { getRandomItem } from "../../utils";
|
|
16
17
|
import { Model } from "../generators.model";
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
BirthDateFormat,
|
|
20
|
+
createModelInstance,
|
|
21
|
+
MockInstance,
|
|
22
|
+
mockModel,
|
|
23
|
+
ReturnTimeFormat,
|
|
24
|
+
TradeDateFormat,
|
|
25
|
+
} from "../generators.mock";
|
|
18
26
|
import { NotificationCenterProvider } from "../../providers";
|
|
19
27
|
import { Box } from "@mui/system";
|
|
20
28
|
import { Button } from "@mui/material";
|
|
21
|
-
import {
|
|
29
|
+
import { useNavigate } from "react-router-dom";
|
|
30
|
+
import {
|
|
31
|
+
clearCheckbox,
|
|
32
|
+
clearMultiSelect,
|
|
33
|
+
expectAlert,
|
|
34
|
+
expectToHaveBeenCalledOnceWithMockInstance,
|
|
35
|
+
pickDatetime,
|
|
36
|
+
selectOptions,
|
|
37
|
+
} from "../../tests";
|
|
38
|
+
import { AddScreen, ListScreen, UpdateScreen } from "./screens";
|
|
39
|
+
import { IdleRequest, LoadingRequest, SuccessRequest } from "./model-router.types";
|
|
22
40
|
|
|
23
41
|
const REQUEST_TIMEOUT = 20;
|
|
24
42
|
|
|
@@ -82,30 +100,6 @@ describe("ModelRouter", () => {
|
|
|
82
100
|
expectNotToHaveMenuOption: ({ id }: { id: string }) => {
|
|
83
101
|
expect(screen.queryByTestId(`options-${id}`)).not.toBeInTheDocument();
|
|
84
102
|
},
|
|
85
|
-
expectSubmitInstanceCall: (mockFn: jest.Mock, instance: MockInstance) => {
|
|
86
|
-
expect(mockFn).toHaveBeenCalledTimes(1);
|
|
87
|
-
expect(mockFn).toHaveBeenCalledWith({
|
|
88
|
-
id: instance.id,
|
|
89
|
-
firstName: instance.firstName,
|
|
90
|
-
middleName: instance.middleName,
|
|
91
|
-
lastName: instance.lastName,
|
|
92
|
-
gender: instance.gender,
|
|
93
|
-
age: instance.age.toString(),
|
|
94
|
-
birthDate: instance.birthDate,
|
|
95
|
-
car: {
|
|
96
|
-
model: instance.car.model,
|
|
97
|
-
manufacturer: instance.car.manufacturer,
|
|
98
|
-
color: instance.car.color,
|
|
99
|
-
type: instance.car.type,
|
|
100
|
-
vin: instance.car.vin,
|
|
101
|
-
vrm: instance.car.vrm,
|
|
102
|
-
},
|
|
103
|
-
quantity: instance.quantity.toString(),
|
|
104
|
-
available: instance.available.toString(),
|
|
105
|
-
currency: instance.currency,
|
|
106
|
-
tradeDate: instance.tradeDate,
|
|
107
|
-
});
|
|
108
|
-
},
|
|
109
103
|
};
|
|
110
104
|
|
|
111
105
|
const actions = {
|
|
@@ -149,17 +143,18 @@ describe("ModelRouter", () => {
|
|
|
149
143
|
const firstNameElement = screen.getByRole("textbox", { name: /first name/i });
|
|
150
144
|
const middleNameElement = screen.getByRole("textbox", { name: /middle name/i });
|
|
151
145
|
const lastNameElement = screen.getByRole("textbox", { name: /last name/i });
|
|
152
|
-
const genderElement = screen.getByRole("
|
|
146
|
+
const genderElement = screen.getByRole("button", { name: /gender/i });
|
|
153
147
|
const ageElement = screen.getByRole("spinbutton", { name: /age/i });
|
|
154
148
|
const birthDateElement = screen.getByRole("textbox", { name: /birth date/i });
|
|
155
|
-
const manufacturerElement = screen.getByRole("
|
|
156
|
-
const modelElement = screen.getByRole("
|
|
149
|
+
const manufacturerElement = screen.getByRole("button", { name: /manufacturer/i });
|
|
150
|
+
const modelElement = screen.getByRole("button", { name: /model/i });
|
|
157
151
|
const colorElement = screen.getByRole("textbox", { name: /color/i });
|
|
158
|
-
const typeElement = screen.getByRole("
|
|
152
|
+
const typeElement = screen.getByRole("button", { name: /type/i });
|
|
159
153
|
const vinElement = screen.getByRole("textbox", { name: /vin/i });
|
|
160
154
|
const vrmElement = screen.getByRole("textbox", { name: /vrm/i });
|
|
155
|
+
const timeReturnElement = screen.getByRole("textbox", { name: /return time/i });
|
|
161
156
|
const quantityElement = screen.getByRole("spinbutton", { name: /q/i });
|
|
162
|
-
const availableElement = screen.getByRole("
|
|
157
|
+
const availableElement = screen.getByRole("checkbox", { name: /available/i });
|
|
163
158
|
const currencyElement = screen.getByRole("textbox", { name: /currency/i });
|
|
164
159
|
const tradeDateElement = screen.getByRole("textbox", { name: /trade date/i });
|
|
165
160
|
|
|
@@ -168,37 +163,39 @@ describe("ModelRouter", () => {
|
|
|
168
163
|
await userEvent.clear(firstNameElement);
|
|
169
164
|
await userEvent.clear(middleNameElement);
|
|
170
165
|
await userEvent.clear(lastNameElement);
|
|
171
|
-
await userEvent.clear(genderElement);
|
|
172
166
|
await userEvent.clear(ageElement);
|
|
173
167
|
await userEvent.clear(birthDateElement);
|
|
174
|
-
await userEvent.clear(manufacturerElement);
|
|
175
|
-
await userEvent.clear(modelElement);
|
|
176
168
|
await userEvent.clear(colorElement);
|
|
177
|
-
await userEvent.clear(typeElement);
|
|
178
169
|
await userEvent.clear(vinElement);
|
|
179
170
|
await userEvent.clear(vrmElement);
|
|
180
171
|
await userEvent.clear(quantityElement);
|
|
181
172
|
await userEvent.clear(availableElement);
|
|
182
173
|
await userEvent.clear(currencyElement);
|
|
183
174
|
await userEvent.clear(tradeDateElement);
|
|
175
|
+
await userEvent.clear(timeReturnElement);
|
|
176
|
+
await clearCheckbox(availableElement);
|
|
177
|
+
await clearMultiSelect(typeElement);
|
|
184
178
|
}
|
|
185
179
|
await userEvent.type(idElement, instance.id);
|
|
186
180
|
await userEvent.type(firstNameElement, instance.firstName);
|
|
187
181
|
await userEvent.type(middleNameElement, instance.middleName);
|
|
188
182
|
await userEvent.type(lastNameElement, instance.lastName);
|
|
189
|
-
await
|
|
183
|
+
await selectOption(genderElement, instance.gender);
|
|
190
184
|
await userEvent.type(ageElement, instance.age.toString());
|
|
191
|
-
|
|
192
|
-
await
|
|
193
|
-
await
|
|
185
|
+
pickDatetime(birthDateElement, instance.birthDate, BirthDateFormat);
|
|
186
|
+
await selectOption(modelElement, instance.car.model);
|
|
187
|
+
await selectOption(manufacturerElement, instance.car.manufacturer);
|
|
194
188
|
await userEvent.type(colorElement, instance.car.color);
|
|
195
|
-
await
|
|
189
|
+
await selectOptions(typeElement, instance.car.type);
|
|
196
190
|
await userEvent.type(vinElement, instance.car.vin);
|
|
197
191
|
await userEvent.type(vrmElement, instance.car.vrm);
|
|
192
|
+
pickDatetime(timeReturnElement, instance.car.returnTime, ReturnTimeFormat);
|
|
198
193
|
await userEvent.type(quantityElement, instance.quantity.toString());
|
|
199
|
-
|
|
194
|
+
if (instance.available) {
|
|
195
|
+
await userEvent.click(availableElement);
|
|
196
|
+
}
|
|
200
197
|
await userEvent.type(currencyElement, instance.currency);
|
|
201
|
-
|
|
198
|
+
pickDatetime(tradeDateElement, instance.tradeDate, TradeDateFormat);
|
|
202
199
|
|
|
203
200
|
submit && (await userEvent.click(screen.getByRole("button", { name: /save/i })));
|
|
204
201
|
|
|
@@ -296,6 +293,98 @@ describe("ModelRouter", () => {
|
|
|
296
293
|
);
|
|
297
294
|
};
|
|
298
295
|
|
|
296
|
+
const renderListScreen = () => {
|
|
297
|
+
const error = "Lorem ipsum error";
|
|
298
|
+
|
|
299
|
+
const TestComponent = () => {
|
|
300
|
+
const [deleteRequest, setDeleteRequest] = useState(IdleRequest);
|
|
301
|
+
|
|
302
|
+
return (
|
|
303
|
+
<NotificationCenterProvider>
|
|
304
|
+
<ListScreen
|
|
305
|
+
onRequestList={jest.fn()}
|
|
306
|
+
listData={[]}
|
|
307
|
+
onClickDeleteItem={jest.fn()}
|
|
308
|
+
listRequest={IdleRequest}
|
|
309
|
+
deleteRequest={deleteRequest}
|
|
310
|
+
modelName="Items"
|
|
311
|
+
model={mockModel}
|
|
312
|
+
/>
|
|
313
|
+
<Box>
|
|
314
|
+
<Button onClick={() => setDeleteRequest(IdleRequest)}>idle</Button>
|
|
315
|
+
<Button onClick={() => setDeleteRequest(LoadingRequest)}>loading</Button>
|
|
316
|
+
<Button onClick={() => setDeleteRequest(SuccessRequest)}>success</Button>
|
|
317
|
+
<Button onClick={() => setDeleteRequest({ error })}>error</Button>
|
|
318
|
+
</Box>
|
|
319
|
+
</NotificationCenterProvider>
|
|
320
|
+
);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const instance = render(<TestComponent />);
|
|
324
|
+
|
|
325
|
+
return { ...instance, error, model: mockModel };
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const renderAddScreen = () => {
|
|
329
|
+
const error = "Lorem ipsum error";
|
|
330
|
+
|
|
331
|
+
const TestComponent = () => {
|
|
332
|
+
const [newItemRequest, setNewItemRequest] = useState(IdleRequest);
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<NotificationCenterProvider>
|
|
336
|
+
<AddScreen
|
|
337
|
+
modelName="Items"
|
|
338
|
+
model={mockModel}
|
|
339
|
+
onSubmitNewItem={jest.fn()}
|
|
340
|
+
newItemRequest={newItemRequest}
|
|
341
|
+
/>
|
|
342
|
+
<Box>
|
|
343
|
+
<Button onClick={() => setNewItemRequest(IdleRequest)}>idle</Button>
|
|
344
|
+
<Button onClick={() => setNewItemRequest(LoadingRequest)}>loading</Button>
|
|
345
|
+
<Button onClick={() => setNewItemRequest(SuccessRequest)}>success</Button>
|
|
346
|
+
<Button onClick={() => setNewItemRequest({ error })}>error</Button>
|
|
347
|
+
</Box>
|
|
348
|
+
</NotificationCenterProvider>
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const instance = render(<TestComponent />, { router: "router" });
|
|
353
|
+
|
|
354
|
+
return { ...instance, error, model: mockModel };
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const renderUpdateScreen = () => {
|
|
358
|
+
const error = "Lorem ipsum error";
|
|
359
|
+
|
|
360
|
+
const TestComponent = () => {
|
|
361
|
+
const [updateRequest, setUpdateRequest] = useState(IdleRequest);
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<NotificationCenterProvider>
|
|
365
|
+
<UpdateScreen
|
|
366
|
+
modelName="Items"
|
|
367
|
+
model={mockModel}
|
|
368
|
+
onSubmitUpdateItem={jest.fn()}
|
|
369
|
+
submitUpdateItemRequest={updateRequest}
|
|
370
|
+
updateItemRequest={IdleRequest}
|
|
371
|
+
onRequestUpdateItem={jest.fn()}
|
|
372
|
+
/>
|
|
373
|
+
<Box>
|
|
374
|
+
<Button onClick={() => setUpdateRequest(IdleRequest)}>idle</Button>
|
|
375
|
+
<Button onClick={() => setUpdateRequest(LoadingRequest)}>loading</Button>
|
|
376
|
+
<Button onClick={() => setUpdateRequest(SuccessRequest)}>success</Button>
|
|
377
|
+
<Button onClick={() => setUpdateRequest({ error })}>error</Button>
|
|
378
|
+
</Box>
|
|
379
|
+
</NotificationCenterProvider>
|
|
380
|
+
);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const instance = render(<TestComponent />, { router: "router" });
|
|
384
|
+
|
|
385
|
+
return { ...instance, error, model: mockModel };
|
|
386
|
+
};
|
|
387
|
+
|
|
299
388
|
describe("router screens", () => {
|
|
300
389
|
it("would render the list screen by default", async () => {
|
|
301
390
|
await renderComponent();
|
|
@@ -381,6 +470,26 @@ describe("ModelRouter", () => {
|
|
|
381
470
|
await assertions.expectAddScreen();
|
|
382
471
|
});
|
|
383
472
|
|
|
473
|
+
it("would be able to go back to the list sreen from the add screen", async () => {
|
|
474
|
+
renderComponentInsideRouter();
|
|
475
|
+
|
|
476
|
+
await actions.navigateToInternal();
|
|
477
|
+
await actions.navigateToAddScreen();
|
|
478
|
+
await userEvent.click(screen.getByRole("link", { name: "Items" }));
|
|
479
|
+
|
|
480
|
+
await assertions.expectListScreen();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("would navigate to the add screen if I click the navigaiton path", async () => {
|
|
484
|
+
renderComponentInsideRouter();
|
|
485
|
+
|
|
486
|
+
await actions.navigateToInternal();
|
|
487
|
+
await actions.navigateToAddScreen();
|
|
488
|
+
await userEvent.click(screen.getByRole("link", { name: "Add new Items" }));
|
|
489
|
+
|
|
490
|
+
await assertions.expectAddScreen();
|
|
491
|
+
});
|
|
492
|
+
|
|
384
493
|
it("would navigate to the detail screen", async () => {
|
|
385
494
|
renderComponentInsideRouter();
|
|
386
495
|
const {
|
|
@@ -393,6 +502,32 @@ describe("ModelRouter", () => {
|
|
|
393
502
|
await assertions.expectDetailScreen({ id });
|
|
394
503
|
});
|
|
395
504
|
|
|
505
|
+
it("would be able to go back to the list sreen from the details screen", async () => {
|
|
506
|
+
renderComponentInsideRouter();
|
|
507
|
+
const {
|
|
508
|
+
item: { firstName },
|
|
509
|
+
} = getRandomItem<MockInstance>(mockData);
|
|
510
|
+
|
|
511
|
+
await actions.navigateToInternal();
|
|
512
|
+
await actions.navigateToDetailScreen({ name: firstName });
|
|
513
|
+
await userEvent.click(screen.getByRole("link", { name: "Items" }));
|
|
514
|
+
|
|
515
|
+
await assertions.expectListScreen();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("would navigate to the add screen if I click the navigation path", async () => {
|
|
519
|
+
renderComponentInsideRouter();
|
|
520
|
+
const {
|
|
521
|
+
item: { id, firstName },
|
|
522
|
+
} = getRandomItem<MockInstance>(mockData);
|
|
523
|
+
|
|
524
|
+
await actions.navigateToInternal();
|
|
525
|
+
await actions.navigateToDetailScreen({ name: firstName });
|
|
526
|
+
await userEvent.click(screen.getByRole("link", { name: id }));
|
|
527
|
+
|
|
528
|
+
await assertions.expectDetailScreen({ id });
|
|
529
|
+
});
|
|
530
|
+
|
|
396
531
|
it("would navigate to the update screen", async () => {
|
|
397
532
|
renderComponentInsideRouter();
|
|
398
533
|
const {
|
|
@@ -404,6 +539,32 @@ describe("ModelRouter", () => {
|
|
|
404
539
|
|
|
405
540
|
await assertions.expectUpdateScreen({ id });
|
|
406
541
|
});
|
|
542
|
+
|
|
543
|
+
it("would be able to go back to the list sreen from the details screen", async () => {
|
|
544
|
+
renderComponentInsideRouter();
|
|
545
|
+
const {
|
|
546
|
+
item: { id },
|
|
547
|
+
} = getRandomItem<MockInstance>(mockData);
|
|
548
|
+
|
|
549
|
+
await actions.navigateToInternal();
|
|
550
|
+
await actions.navigateToUpdateScreen({ id });
|
|
551
|
+
await userEvent.click(screen.getByRole("link", { name: "Items" }));
|
|
552
|
+
|
|
553
|
+
await assertions.expectListScreen();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("would navigate to the add screen if I click the navigation path", async () => {
|
|
557
|
+
renderComponentInsideRouter();
|
|
558
|
+
const {
|
|
559
|
+
item: { id },
|
|
560
|
+
} = getRandomItem<MockInstance>(mockData);
|
|
561
|
+
|
|
562
|
+
await actions.navigateToInternal();
|
|
563
|
+
await actions.navigateToUpdateScreen({ id });
|
|
564
|
+
await userEvent.click(screen.getByRole("link", { name: `Edit ${id}` }));
|
|
565
|
+
|
|
566
|
+
await assertions.expectUpdateScreen({ id });
|
|
567
|
+
});
|
|
407
568
|
});
|
|
408
569
|
|
|
409
570
|
describe("list screen", () => {
|
|
@@ -544,7 +705,7 @@ describe("ModelRouter", () => {
|
|
|
544
705
|
|
|
545
706
|
const newInstance = await actions.fullfillModelForm({ model, submit: true });
|
|
546
707
|
|
|
547
|
-
|
|
708
|
+
expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem, newInstance);
|
|
548
709
|
});
|
|
549
710
|
|
|
550
711
|
it("would show a loading indicator when the request is in progress", async () => {
|
|
@@ -555,24 +716,36 @@ describe("ModelRouter", () => {
|
|
|
555
716
|
expectProgressIndicator();
|
|
556
717
|
});
|
|
557
718
|
|
|
558
|
-
|
|
559
|
-
|
|
719
|
+
it("would show a success message if the request finish with a success", async () => {
|
|
720
|
+
renderAddScreen();
|
|
560
721
|
|
|
561
|
-
|
|
722
|
+
await userEvent.click(screen.getByRole("button", { name: /success/i }));
|
|
562
723
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
724
|
+
await expectAlert({
|
|
725
|
+
message: /item added successfully/i,
|
|
726
|
+
severity: "success",
|
|
727
|
+
});
|
|
728
|
+
});
|
|
568
729
|
|
|
569
|
-
|
|
570
|
-
|
|
730
|
+
it("would navigate to the list screen if the request finish with a success", async () => {
|
|
731
|
+
const { history } = renderAddScreen();
|
|
571
732
|
|
|
572
|
-
|
|
733
|
+
await userEvent.click(screen.getByRole("button", { name: /success/i }));
|
|
573
734
|
|
|
574
|
-
|
|
575
|
-
|
|
735
|
+
expect(history.location.pathname).toBe("/");
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it("would show an error message if the request finish with an error", async () => {
|
|
739
|
+
const { error } = renderAddScreen();
|
|
740
|
+
|
|
741
|
+
await userEvent.click(screen.getByRole("button", { name: /error/i }));
|
|
742
|
+
|
|
743
|
+
await expectAlert({
|
|
744
|
+
title: /we had an error/i,
|
|
745
|
+
message: error,
|
|
746
|
+
severity: "error",
|
|
747
|
+
});
|
|
748
|
+
});
|
|
576
749
|
});
|
|
577
750
|
|
|
578
751
|
describe("details screen", () => {
|
|
@@ -707,12 +880,12 @@ describe("ModelRouter", () => {
|
|
|
707
880
|
});
|
|
708
881
|
|
|
709
882
|
it("would make a request with the new values when the form is submitted", async () => {
|
|
710
|
-
const { model } = await renderComponent({ screen: "update" });
|
|
883
|
+
const { model, onSubmitUpdate } = await renderComponent({ screen: "update" });
|
|
711
884
|
|
|
712
885
|
await waitForProgressIndicatorToBeRemoved();
|
|
713
|
-
const newInstance = await actions.fullfillModelForm({ model, clear: true });
|
|
886
|
+
const newInstance = await actions.fullfillModelForm({ model, clear: true, submit: true });
|
|
714
887
|
|
|
715
|
-
|
|
888
|
+
expectToHaveBeenCalledOnceWithMockInstance(onSubmitUpdate, newInstance);
|
|
716
889
|
});
|
|
717
890
|
|
|
718
891
|
it("would show a loading indicator while the submit request is in progress", async () => {
|
|
@@ -724,13 +897,36 @@ describe("ModelRouter", () => {
|
|
|
724
897
|
expectProgressIndicator();
|
|
725
898
|
});
|
|
726
899
|
|
|
727
|
-
it("would
|
|
728
|
-
|
|
900
|
+
it("would show a success message if the request finish with a success", async () => {
|
|
901
|
+
renderUpdateScreen();
|
|
729
902
|
|
|
730
|
-
await
|
|
731
|
-
|
|
903
|
+
await userEvent.click(screen.getByRole("button", { name: /success/i }));
|
|
904
|
+
|
|
905
|
+
await expectAlert({
|
|
906
|
+
title: /item updated/i,
|
|
907
|
+
message: /has been updated successfully/i,
|
|
908
|
+
severity: "success",
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it("would navigate to the list screen if the request finish with a success", async () => {
|
|
913
|
+
const { history } = renderUpdateScreen();
|
|
914
|
+
|
|
915
|
+
await userEvent.click(screen.getByRole("button", { name: /success/i }));
|
|
732
916
|
|
|
733
|
-
|
|
917
|
+
expect(history.location.pathname).toBe("/");
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it("would show an error message if the request finish with an error", async () => {
|
|
921
|
+
const { error } = renderUpdateScreen();
|
|
922
|
+
|
|
923
|
+
await userEvent.click(screen.getByRole("button", { name: /error/i }));
|
|
924
|
+
|
|
925
|
+
await expectAlert({
|
|
926
|
+
title: /we had an error/i,
|
|
927
|
+
message: error,
|
|
928
|
+
severity: "error",
|
|
929
|
+
});
|
|
734
930
|
});
|
|
735
931
|
});
|
|
736
932
|
|
|
@@ -776,6 +972,30 @@ describe("ModelRouter", () => {
|
|
|
776
972
|
|
|
777
973
|
expect(screen.queryByRole("cell", { name: firstName })).not.toBeInTheDocument();
|
|
778
974
|
});
|
|
975
|
+
|
|
976
|
+
it("would show a success message if the delete request finish with a success", async () => {
|
|
977
|
+
renderListScreen();
|
|
978
|
+
|
|
979
|
+
await userEvent.click(screen.getByRole("button", { name: /success/i }));
|
|
980
|
+
|
|
981
|
+
await expectAlert({
|
|
982
|
+
title: /item deleted/i,
|
|
983
|
+
message: /the item has been deleted successfully/i,
|
|
984
|
+
severity: "success",
|
|
985
|
+
});
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
it("would show a error message if the delete request finish with an error", async () => {
|
|
989
|
+
const { error } = renderListScreen();
|
|
990
|
+
|
|
991
|
+
await userEvent.click(screen.getByRole("button", { name: /error/i }));
|
|
992
|
+
|
|
993
|
+
await expectAlert({
|
|
994
|
+
title: /we had an error/i,
|
|
995
|
+
message: error,
|
|
996
|
+
severity: "error",
|
|
997
|
+
});
|
|
998
|
+
});
|
|
779
999
|
});
|
|
780
1000
|
|
|
781
1001
|
describe("modifying enabled features", () => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { useNavigate } from "react-router-dom";
|
|
1
|
+
import React from "react";
|
|
3
2
|
import { Header, Content } from "~/components";
|
|
4
3
|
import { BasicModelInstance, ModelForm } from "~/generators";
|
|
5
4
|
import { HeaderLayout } from "../../../layouts";
|
|
6
|
-
import {
|
|
5
|
+
import { useNotifyWhenValueChanges } from "../../../providers";
|
|
7
6
|
import { RequestState } from "../model-router.types";
|
|
8
7
|
import { BaseScreenProps } from "./screens.types";
|
|
8
|
+
import { useNavigateWhenValueChanges } from "../../../hooks";
|
|
9
9
|
|
|
10
10
|
export interface AddScreenProps<T extends BasicModelInstance> extends BaseScreenProps {
|
|
11
11
|
/**
|
|
@@ -28,21 +28,17 @@ export const AddScreen = <T extends BasicModelInstance>({
|
|
|
28
28
|
onSubmitNewItem,
|
|
29
29
|
newItemRequest,
|
|
30
30
|
}: AddScreenProps<T>) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (newItemRequest.error) {
|
|
43
|
-
show({ title: "We had an error", message: newItemRequest.error, severity: "error" });
|
|
44
|
-
}
|
|
45
|
-
}, [newItemRequest.error]);
|
|
31
|
+
useNotifyWhenValueChanges(
|
|
32
|
+
{ message: "Item added successfully", severity: "success" },
|
|
33
|
+
!!newItemRequest.success,
|
|
34
|
+
{ from: false, to: true },
|
|
35
|
+
);
|
|
36
|
+
useNavigateWhenValueChanges(`${basePath}/`, !!newItemRequest.success, { from: false, to: true });
|
|
37
|
+
useNotifyWhenValueChanges(
|
|
38
|
+
{ title: "We had an error", message: newItemRequest.error || "", severity: "error" },
|
|
39
|
+
!!newItemRequest.error,
|
|
40
|
+
{ from: false, to: true },
|
|
41
|
+
);
|
|
46
42
|
|
|
47
43
|
return (
|
|
48
44
|
<HeaderLayout loading={newItemRequest.loading}>
|
|
@@ -53,12 +49,12 @@ export const AddScreen = <T extends BasicModelInstance>({
|
|
|
53
49
|
{
|
|
54
50
|
id: "list",
|
|
55
51
|
text: modelName,
|
|
56
|
-
link:
|
|
52
|
+
link: `${basePath}/`,
|
|
57
53
|
},
|
|
58
54
|
{
|
|
59
55
|
id: "add",
|
|
60
56
|
text: `Add new ${modelName}`,
|
|
61
|
-
link:
|
|
57
|
+
link: `${basePath}/add`,
|
|
62
58
|
},
|
|
63
59
|
]}
|
|
64
60
|
/>
|
|
@@ -28,6 +28,7 @@ export interface DetailsScreenProps<T extends BasicModelInstance> extends BaseSc
|
|
|
28
28
|
export const DetailsScreen = <T extends BasicModelInstance>({
|
|
29
29
|
model,
|
|
30
30
|
modelName,
|
|
31
|
+
basePath = "",
|
|
31
32
|
onRequestItem,
|
|
32
33
|
itemRequest,
|
|
33
34
|
detailsItem,
|
|
@@ -47,12 +48,12 @@ export const DetailsScreen = <T extends BasicModelInstance>({
|
|
|
47
48
|
{
|
|
48
49
|
id: "list",
|
|
49
50
|
text: modelName,
|
|
50
|
-
link:
|
|
51
|
+
link: `${basePath}/`,
|
|
51
52
|
},
|
|
52
53
|
{
|
|
53
54
|
id: "detail",
|
|
54
55
|
text: id,
|
|
55
|
-
link:
|
|
56
|
+
link: `${basePath}/${id}`,
|
|
56
57
|
},
|
|
57
58
|
]}
|
|
58
59
|
/>
|
|
@@ -2,6 +2,7 @@ import React, { useEffect } from "react";
|
|
|
2
2
|
import { useNavigate } from "react-router-dom";
|
|
3
3
|
import { Content, Header, HeaderAction, TableList, TableRowOption } from "~/components";
|
|
4
4
|
import { BasicModelInstance } from "~/generators";
|
|
5
|
+
import { useNotifyWhenValueChanges } from "~/providers";
|
|
5
6
|
import { HeaderLayout } from "../../../layouts";
|
|
6
7
|
import { RequestState } from "../model-router.types";
|
|
7
8
|
import { BaseScreenProps } from "./screens.types";
|
|
@@ -57,6 +58,22 @@ export const ListScreen = <T extends BasicModelInstance>({
|
|
|
57
58
|
onRequestList();
|
|
58
59
|
}, []);
|
|
59
60
|
|
|
61
|
+
useNotifyWhenValueChanges(
|
|
62
|
+
{
|
|
63
|
+
title: "Item deleted",
|
|
64
|
+
message: "The item has been deleted successfully",
|
|
65
|
+
severity: "success",
|
|
66
|
+
},
|
|
67
|
+
!!deleteRequest.success,
|
|
68
|
+
{ from: false, to: true },
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
useNotifyWhenValueChanges(
|
|
72
|
+
{ title: "We had an error", message: deleteRequest.error || "", severity: "error" },
|
|
73
|
+
!!deleteRequest.error,
|
|
74
|
+
{ from: false, to: true },
|
|
75
|
+
);
|
|
76
|
+
|
|
60
77
|
const handleClickListItem = detailsFeature
|
|
61
78
|
? (item: T) => {
|
|
62
79
|
navigate(`${basePath}/${item.id}`);
|
|
@@ -2,8 +2,9 @@ import React, { useEffect } from "react";
|
|
|
2
2
|
import { useNavigate, useParams } from "react-router-dom";
|
|
3
3
|
import { Content, Header } from "~/components";
|
|
4
4
|
import { BasicModelInstance, ModelForm } from "~/generators";
|
|
5
|
+
import { useNavigateWhenValueChanges } from "~/hooks";
|
|
5
6
|
import { HeaderLayout } from "../../../layouts";
|
|
6
|
-
import { useNotificationCenter } from "../../../providers";
|
|
7
|
+
import { useNotificationCenter, useNotifyWhenValueChanges } from "../../../providers";
|
|
7
8
|
import { RequestState } from "../model-router.types";
|
|
8
9
|
import { BaseScreenProps } from "./screens.types";
|
|
9
10
|
|
|
@@ -55,16 +56,24 @@ export const UpdateScreen = <T extends BasicModelInstance>({
|
|
|
55
56
|
onRequestUpdateItem(id);
|
|
56
57
|
}, [id]);
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
59
|
+
useNotifyWhenValueChanges(
|
|
60
|
+
{
|
|
61
|
+
title: "Item updated",
|
|
62
|
+
message: `The item ${id} has been updated successfully`,
|
|
63
|
+
severity: "success",
|
|
64
|
+
},
|
|
65
|
+
!!submitUpdateItemRequest.success,
|
|
66
|
+
{ from: false, to: true },
|
|
67
|
+
);
|
|
68
|
+
useNavigateWhenValueChanges(`${basePath}/`, !!submitUpdateItemRequest.success, {
|
|
69
|
+
from: false,
|
|
70
|
+
to: true,
|
|
71
|
+
});
|
|
72
|
+
useNotifyWhenValueChanges(
|
|
73
|
+
{ title: "We had an error", message: submitUpdateItemRequest.error || "", severity: "error" },
|
|
74
|
+
!!submitUpdateItemRequest.error,
|
|
75
|
+
{ from: false, to: true },
|
|
76
|
+
);
|
|
68
77
|
|
|
69
78
|
return (
|
|
70
79
|
<HeaderLayout loading={loading}>
|
|
@@ -75,12 +84,12 @@ export const UpdateScreen = <T extends BasicModelInstance>({
|
|
|
75
84
|
{
|
|
76
85
|
id: "list",
|
|
77
86
|
text: modelName,
|
|
78
|
-
link:
|
|
87
|
+
link: `${basePath}/`,
|
|
79
88
|
},
|
|
80
89
|
{
|
|
81
90
|
id: "update",
|
|
82
91
|
text: `Edit ${id}`,
|
|
83
|
-
link:
|
|
92
|
+
link: `${basePath}/${id}/update`,
|
|
84
93
|
},
|
|
85
94
|
]}
|
|
86
95
|
/>
|