richie-education 2.25.0-b2.dev138 → 2.25.0-b2.dev141
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/js/components/SaleTunnel/components/StepBreadcrumb/index.spec.tsx +8 -12
- package/js/components/SaleTunnel/components/StepBreadcrumb/index.tsx +5 -11
- package/js/components/SaleTunnel/index.tsx +1 -1
- package/js/hooks/useStepManager/index.spec.ts +8 -12
- package/js/hooks/useStepManager/index.ts +10 -24
- package/js/hooks/useUnionResource/index.spec.tsx +50 -0
- package/js/hooks/useUnionResource/index.ts +11 -14
- package/package.json +17 -17
|
@@ -19,9 +19,8 @@ describe('StepBreadcrumb', () => {
|
|
|
19
19
|
it('renders visually a minimal manifest', () => {
|
|
20
20
|
// If manifest's steps does not have `label` and `icon` property,
|
|
21
21
|
// only a breadcrumb with the step index is displayed.
|
|
22
|
-
type
|
|
23
|
-
|
|
24
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
22
|
+
type Steps = 'step0' | 'step1';
|
|
23
|
+
const manifest: Manifest<Steps> = {
|
|
25
24
|
start: 'step0',
|
|
26
25
|
steps: {
|
|
27
26
|
step0: {
|
|
@@ -78,9 +77,8 @@ describe('StepBreadcrumb', () => {
|
|
|
78
77
|
it('renders visually a complete manifest', () => {
|
|
79
78
|
// If manifest's steps has `label` and `icon` property,
|
|
80
79
|
// this information was shown.
|
|
81
|
-
type
|
|
82
|
-
|
|
83
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
80
|
+
type Steps = 'step0' | 'step1';
|
|
81
|
+
const manifest: Manifest<Steps> = {
|
|
84
82
|
start: 'step0',
|
|
85
83
|
steps: {
|
|
86
84
|
step0: {
|
|
@@ -155,9 +153,8 @@ describe('StepBreadcrumb', () => {
|
|
|
155
153
|
});
|
|
156
154
|
|
|
157
155
|
it('sorts manifest steps to guarantee the display order of steps', () => {
|
|
158
|
-
type
|
|
159
|
-
|
|
160
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
156
|
+
type Steps = 'step1' | 'step0';
|
|
157
|
+
const manifest: Manifest<Steps> = {
|
|
161
158
|
start: 'step1',
|
|
162
159
|
steps: {
|
|
163
160
|
step0: {
|
|
@@ -189,9 +186,8 @@ describe('StepBreadcrumb', () => {
|
|
|
189
186
|
});
|
|
190
187
|
|
|
191
188
|
it('displays all active steps on mount if manifest does not start to the first step', () => {
|
|
192
|
-
type
|
|
193
|
-
|
|
194
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
189
|
+
type Steps = 'step0' | 'step1';
|
|
190
|
+
const manifest: Manifest<Steps> = {
|
|
195
191
|
start: 'step1',
|
|
196
192
|
steps: {
|
|
197
193
|
step0: {
|
|
@@ -7,8 +7,8 @@ import { Nullable } from 'types/utils';
|
|
|
7
7
|
import { Icon } from 'components/Icon';
|
|
8
8
|
import { Manifest, Step, useStepManager } from 'hooks/useStepManager';
|
|
9
9
|
|
|
10
|
-
interface Props<Keys extends PropertyKey
|
|
11
|
-
manifest: Manifest<Keys
|
|
10
|
+
interface Props<Keys extends PropertyKey> {
|
|
11
|
+
manifest: Manifest<Keys>;
|
|
12
12
|
step: Nullable<PropertyKey>;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -33,12 +33,9 @@ function getActiveStepIndex(steps: FlattenStep[], step: Nullable<PropertyKey>) {
|
|
|
33
33
|
* // MARK When IE 11 supports will be dropped, we can use a `Map`
|
|
34
34
|
* to define `manifest.steps` then remove this sort function
|
|
35
35
|
*/
|
|
36
|
-
function sortSteps<Keys extends PropertyKey,
|
|
37
|
-
firstStep: Keys,
|
|
38
|
-
manifest: Manifest<Keys, LastKey>,
|
|
39
|
-
) {
|
|
36
|
+
function sortSteps<Keys extends PropertyKey>(firstStep: Keys, manifest: Manifest<Keys>) {
|
|
40
37
|
const steps: FlattenStep[] = [];
|
|
41
|
-
let step = firstStep;
|
|
38
|
+
let step: Nullable<Keys> = firstStep;
|
|
42
39
|
|
|
43
40
|
while (step !== null) {
|
|
44
41
|
steps.push([step, manifest.steps[step]]);
|
|
@@ -57,10 +54,7 @@ const messages = defineMessages({
|
|
|
57
54
|
},
|
|
58
55
|
});
|
|
59
56
|
|
|
60
|
-
export const StepBreadcrumb = <Keys extends PropertyKey,
|
|
61
|
-
step,
|
|
62
|
-
manifest,
|
|
63
|
-
}: Props<Keys, LastKey>) => {
|
|
57
|
+
export const StepBreadcrumb = <Keys extends PropertyKey>({ step, manifest }: Props<Keys>) => {
|
|
64
58
|
const { firstStep } = useStepManager(manifest);
|
|
65
59
|
const orderedSteps = sortSteps(firstStep, manifest);
|
|
66
60
|
const activeIndex = getActiveStepIndex(orderedSteps, step);
|
|
@@ -3,9 +3,8 @@ import { Manifest, useStepManager } from '.';
|
|
|
3
3
|
|
|
4
4
|
describe('useStepManager', () => {
|
|
5
5
|
it('reads the manifest', () => {
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
6
|
+
type Steps = 'step0' | 'step1';
|
|
7
|
+
const manifest: Manifest<Steps> = {
|
|
9
8
|
start: 'step0',
|
|
10
9
|
steps: {
|
|
11
10
|
step0: {
|
|
@@ -35,9 +34,8 @@ describe('useStepManager', () => {
|
|
|
35
34
|
});
|
|
36
35
|
|
|
37
36
|
it('is able to reset the step process', () => {
|
|
38
|
-
type
|
|
39
|
-
|
|
40
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
37
|
+
type Steps = 'step0' | 'step1';
|
|
38
|
+
const manifest: Manifest<Steps> = {
|
|
41
39
|
start: 'step0',
|
|
42
40
|
steps: {
|
|
43
41
|
step0: {
|
|
@@ -63,9 +61,8 @@ describe('useStepManager', () => {
|
|
|
63
61
|
});
|
|
64
62
|
|
|
65
63
|
it('is able to find the first step through steps if manifest does not have a start property', () => {
|
|
66
|
-
type
|
|
67
|
-
|
|
68
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
64
|
+
type Steps = 'first' | 'then' | 'finally';
|
|
65
|
+
const manifest: Manifest<Steps> = {
|
|
69
66
|
steps: {
|
|
70
67
|
then: {
|
|
71
68
|
next: 'finally',
|
|
@@ -100,9 +97,8 @@ describe('useStepManager', () => {
|
|
|
100
97
|
});
|
|
101
98
|
|
|
102
99
|
it('triggers onEnter and onExit hooks on step transition', () => {
|
|
103
|
-
type
|
|
104
|
-
|
|
105
|
-
const manifest: Manifest<Steps, LastStep> = {
|
|
100
|
+
type Steps = 'step0' | 'step1';
|
|
101
|
+
const manifest: Manifest<Steps> = {
|
|
106
102
|
start: 'step0',
|
|
107
103
|
steps: {
|
|
108
104
|
step0: {
|
|
@@ -10,20 +10,10 @@ export interface Step<Keys extends PropertyKey = PropertyKey> {
|
|
|
10
10
|
onExit?: Function;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
next: Keys;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface LastStep extends Step {
|
|
18
|
-
next: null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type Steps<Keys extends PropertyKey, LastKey extends Keys> = Record<Keys, Step<Keys>> &
|
|
22
|
-
Record<Exclude<Keys, LastKey>, InStep<Keys>> &
|
|
23
|
-
Record<LastKey, LastStep>;
|
|
13
|
+
type Steps<Keys extends PropertyKey> = Record<Keys, Step<Keys>>;
|
|
24
14
|
|
|
25
|
-
export interface Manifest<Keys extends PropertyKey
|
|
26
|
-
steps: Steps<Keys
|
|
15
|
+
export interface Manifest<Keys extends PropertyKey> {
|
|
16
|
+
steps: Steps<Keys>;
|
|
27
17
|
start?: Keys;
|
|
28
18
|
}
|
|
29
19
|
|
|
@@ -34,9 +24,7 @@ export interface Manifest<Keys extends PropertyKey, LastKey extends Keys = Keys>
|
|
|
34
24
|
* @param {Manifest} manifest
|
|
35
25
|
* @returns {Manifest.step} step
|
|
36
26
|
*/
|
|
37
|
-
function findFirstStep<Keys extends PropertyKey
|
|
38
|
-
manifest: Manifest<Keys, LastKey>,
|
|
39
|
-
): Keys {
|
|
27
|
+
function findFirstStep<Keys extends PropertyKey>(manifest: Manifest<Keys>): Keys {
|
|
40
28
|
const steps = Object.keys(manifest.steps) as Keys[];
|
|
41
29
|
const nextSteps = Object.values<Step<Keys>>(manifest.steps).map(({ next }) => next);
|
|
42
30
|
|
|
@@ -52,12 +40,10 @@ function findFirstStep<Keys extends PropertyKey, LastKey extends Keys>(
|
|
|
52
40
|
*
|
|
53
41
|
* Process terminates when step is null
|
|
54
42
|
*/
|
|
55
|
-
export const useStepManager = <Keys extends PropertyKey
|
|
56
|
-
|
|
57
|
-
) => {
|
|
58
|
-
const firstStep = findFirstStep<Keys, LastKey>(manifest);
|
|
43
|
+
export const useStepManager = <Keys extends PropertyKey>(manifest: Manifest<Keys>) => {
|
|
44
|
+
const firstStep = findFirstStep<Keys>(manifest);
|
|
59
45
|
const [step, setStep] = useState<Nullable<Keys>>(manifest.start || firstStep);
|
|
60
|
-
const
|
|
46
|
+
const currentStep = useMemo(() => (step ? manifest.steps[step] : null), [step]);
|
|
61
47
|
|
|
62
48
|
const next = () => {
|
|
63
49
|
if (step !== null) {
|
|
@@ -67,12 +53,12 @@ export const useStepManager = <Keys extends PropertyKey, LastKey extends Keys>(
|
|
|
67
53
|
};
|
|
68
54
|
|
|
69
55
|
useEffect(() => {
|
|
70
|
-
if (
|
|
56
|
+
if (currentStep?.onEnter) currentStep.onEnter();
|
|
71
57
|
|
|
72
58
|
return () => {
|
|
73
|
-
if (
|
|
59
|
+
if (currentStep?.onExit) currentStep.onExit();
|
|
74
60
|
};
|
|
75
|
-
}, [
|
|
61
|
+
}, [currentStep]);
|
|
76
62
|
|
|
77
63
|
const reset = () => {
|
|
78
64
|
setStep(manifest.start || firstStep);
|
|
@@ -8,6 +8,7 @@ import { mockPaginatedResponse } from 'utils/test/mockPaginatedResponse';
|
|
|
8
8
|
import { PER_PAGE } from 'settings';
|
|
9
9
|
import { HttpError, HttpStatusCode } from 'utils/errors/HttpError';
|
|
10
10
|
import { BaseAppWrapper } from 'utils/test/wrappers/BaseAppWrapper';
|
|
11
|
+
import { createTestQueryClient } from 'utils/test/createTestQueryClient';
|
|
11
12
|
import { QueryConfig, FetchDataFunction } from './utils/fetchEntity';
|
|
12
13
|
import useUnionResource from '.';
|
|
13
14
|
|
|
@@ -314,4 +315,53 @@ describe('useUnionResource', () => {
|
|
|
314
315
|
expect(calledUrls).toHaveLength(4);
|
|
315
316
|
expect(calledUrls).toContain('http://data.a/?isFiltered=true&page=1');
|
|
316
317
|
});
|
|
318
|
+
|
|
319
|
+
it.each([
|
|
320
|
+
{
|
|
321
|
+
testLabel: 'with some results',
|
|
322
|
+
testDataAList: [{ name: 'TestDataA', id: '1', created_on: '2022-01-01' }],
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
testLabel: 'without results',
|
|
326
|
+
testDataAList: [],
|
|
327
|
+
},
|
|
328
|
+
])('should refetch data when a query $testLabel is invalidate', async ({ testDataAList }) => {
|
|
329
|
+
fetchMock.get(
|
|
330
|
+
'http://data.a/?page=1',
|
|
331
|
+
mockPaginatedResponse(testDataAList, testDataAList.length, false),
|
|
332
|
+
);
|
|
333
|
+
fetchMock.get('http://data.b/?page=1', mockPaginatedResponse([], 0, false));
|
|
334
|
+
|
|
335
|
+
const queryClient = createTestQueryClient({ user: true });
|
|
336
|
+
const { result, rerender } = renderHook(
|
|
337
|
+
(queries: {
|
|
338
|
+
queryA?: QueryConfig<TestDataA, TestDataAFilters>;
|
|
339
|
+
queryB?: QueryConfig<TestDataB, PaginatedResourceQuery>;
|
|
340
|
+
}) =>
|
|
341
|
+
useUnionResource<TestDataA, TestDataB, TestDataAFilters, PaginatedResourceQuery>({
|
|
342
|
+
queryAConfig: queries?.queryA || queryAConfig,
|
|
343
|
+
queryBConfig: queries?.queryB || queryBConfig,
|
|
344
|
+
}),
|
|
345
|
+
{
|
|
346
|
+
wrapper: ({ children }) => (
|
|
347
|
+
<BaseAppWrapper queryOptions={{ client: queryClient }}>{children}</BaseAppWrapper>
|
|
348
|
+
),
|
|
349
|
+
},
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
353
|
+
let calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
354
|
+
expect(calledUrls).toHaveLength(2);
|
|
355
|
+
expect(calledUrls).toContain('http://data.a/?page=1');
|
|
356
|
+
expect(calledUrls).toContain('http://data.b/?page=1');
|
|
357
|
+
|
|
358
|
+
queryClient.invalidateQueries({ queryKey: queryAConfig.queryKey });
|
|
359
|
+
rerender({ queryA: { ...queryAConfig }, queryB: queryBConfig });
|
|
360
|
+
await waitFor(() => {
|
|
361
|
+
calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
362
|
+
expect(calledUrls).toHaveLength(3);
|
|
363
|
+
});
|
|
364
|
+
expect(calledUrls.filter((url) => url === 'http://data.a/?page=1')).toHaveLength(2);
|
|
365
|
+
expect(calledUrls.filter((url) => url === 'http://data.b/?page=1')).toHaveLength(1);
|
|
366
|
+
});
|
|
317
367
|
});
|
|
@@ -90,8 +90,10 @@ const useUnionResource = <
|
|
|
90
90
|
const eofRef = useRef<Record<string, number>>(queryClient.getQueryData(eofQueryKey) ?? {});
|
|
91
91
|
log('eof', eofRef.current);
|
|
92
92
|
|
|
93
|
-
const [
|
|
93
|
+
const [update, setForceUpdate] = useState<number>();
|
|
94
94
|
|
|
95
|
+
// to force execution of useEffect::fetchNewPage(),
|
|
96
|
+
// reset need to generate a uniq key that is part of it's dependencies.
|
|
95
97
|
const reset = () => {
|
|
96
98
|
setStack([]);
|
|
97
99
|
setPage(0);
|
|
@@ -99,23 +101,21 @@ const useUnionResource = <
|
|
|
99
101
|
setError(undefined);
|
|
100
102
|
setCursor(perPage);
|
|
101
103
|
setIntegrityCount(0);
|
|
104
|
+
setForceUpdate(new Date().getTime());
|
|
102
105
|
};
|
|
103
106
|
|
|
107
|
+
// we manualy observe key invalidation to trigger new search
|
|
108
|
+
// by generating a new update key
|
|
104
109
|
if (refetchOnInvalidation) {
|
|
105
110
|
useQueryKeyInvalidateListener(queryAConfig.queryKey, reset);
|
|
106
111
|
useQueryKeyInvalidateListener(queryBConfig.queryKey, reset);
|
|
107
112
|
}
|
|
108
113
|
|
|
114
|
+
// filters have changes, new results will be fetch.
|
|
115
|
+
// let's reset every previous fetches states
|
|
116
|
+
// and re-generate update
|
|
109
117
|
useEffect(() => {
|
|
110
|
-
// filters have changes, new results will be fetch.
|
|
111
|
-
// let's reset every previous fetches states
|
|
112
118
|
reset();
|
|
113
|
-
|
|
114
|
-
// We need to fetch new results.
|
|
115
|
-
// reset all states isn't enought. If we've a research without results
|
|
116
|
-
// then the next reset would do nothing.
|
|
117
|
-
// to force execution of useEffect::fetchNewPage(), we use a uniq key build with current queries filters.
|
|
118
|
-
setUnionQueryKey(JSON.stringify(queryAConfig.filters) + JSON.stringify(queryBConfig.filters));
|
|
119
119
|
}, [JSON.stringify(queryAConfig.filters), JSON.stringify(queryBConfig.filters)]);
|
|
120
120
|
|
|
121
121
|
useEffect(() => {
|
|
@@ -165,11 +165,8 @@ const useUnionResource = <
|
|
|
165
165
|
}
|
|
166
166
|
}, [
|
|
167
167
|
cursor,
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
stack.length,
|
|
171
|
-
// unionQueryKey assure that we refetch data when query filters change.
|
|
172
|
-
unionQueryKey,
|
|
168
|
+
// update assure that we refetch data on reset
|
|
169
|
+
update,
|
|
173
170
|
]);
|
|
174
171
|
|
|
175
172
|
const cursorToUse = Math.min(cursor, integrityCount);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "richie-education",
|
|
3
|
-
"version": "2.25.0-b2.
|
|
3
|
+
"version": "2.25.0-b2.dev141",
|
|
4
4
|
"description": "A CMS to build learning portals for Open Education",
|
|
5
5
|
"main": "sandbox/manage.py",
|
|
6
6
|
"scripts": {
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"@hookform/resolvers": "3.3.4",
|
|
51
51
|
"@openfun/cunningham-react": "2.6.0",
|
|
52
52
|
"@openfun/cunningham-tokens": "2.1.1",
|
|
53
|
-
"@sentry/browser": "7.
|
|
54
|
-
"@sentry/types": "7.
|
|
53
|
+
"@sentry/browser": "7.106.0",
|
|
54
|
+
"@sentry/types": "7.106.0",
|
|
55
55
|
"@storybook/addon-actions": "7.6.17",
|
|
56
56
|
"@storybook/addon-essentials": "7.6.17",
|
|
57
57
|
"@storybook/addon-interactions": "7.6.17",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"@storybook/react": "7.6.17",
|
|
60
60
|
"@storybook/react-webpack5": "7.6.17",
|
|
61
61
|
"@storybook/testing-library": "0.2.2",
|
|
62
|
-
"@tanstack/query-core": "5.
|
|
63
|
-
"@tanstack/query-sync-storage-persister": "5.
|
|
64
|
-
"@tanstack/react-query": "5.
|
|
65
|
-
"@tanstack/react-query-persist-client": "5.
|
|
62
|
+
"@tanstack/query-core": "5.25.0",
|
|
63
|
+
"@tanstack/query-sync-storage-persister": "5.25.0",
|
|
64
|
+
"@tanstack/react-query": "5.25.0",
|
|
65
|
+
"@tanstack/react-query-persist-client": "5.25.0",
|
|
66
66
|
"@testing-library/dom": "9.3.4",
|
|
67
67
|
"@testing-library/jest-dom": "6.4.2",
|
|
68
68
|
"@testing-library/react": "14.2.1",
|
|
@@ -74,13 +74,13 @@
|
|
|
74
74
|
"@types/lodash-es": "4.17.12",
|
|
75
75
|
"@types/node-fetch": "2.6.11",
|
|
76
76
|
"@types/query-string": "6.3.0",
|
|
77
|
-
"@types/react": "18.2.
|
|
77
|
+
"@types/react": "18.2.64",
|
|
78
78
|
"@types/react-autosuggest": "10.1.11",
|
|
79
|
-
"@types/react-dom": "18.2.
|
|
79
|
+
"@types/react-dom": "18.2.21",
|
|
80
80
|
"@types/react-modal": "3.16.3",
|
|
81
81
|
"@types/uuid": "9.0.8",
|
|
82
|
-
"@typescript-eslint/eslint-plugin": "7.1.
|
|
83
|
-
"@typescript-eslint/parser": "7.1.
|
|
82
|
+
"@typescript-eslint/eslint-plugin": "7.1.1",
|
|
83
|
+
"@typescript-eslint/parser": "7.1.1",
|
|
84
84
|
"babel-jest": "29.7.0",
|
|
85
85
|
"babel-loader": "9.1.3",
|
|
86
86
|
"babel-plugin-react-intl": "8.2.25",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"js-cookie": "3.0.5",
|
|
114
114
|
"lodash-es": "4.17.21",
|
|
115
115
|
"mdn-polyfills": "5.20.0",
|
|
116
|
-
"msw": "2.2.
|
|
116
|
+
"msw": "2.2.3",
|
|
117
117
|
"node-fetch": ">2.6.6 <3",
|
|
118
118
|
"nodemon": "3.1.0",
|
|
119
119
|
"prettier": "3.2.5",
|
|
@@ -124,24 +124,24 @@
|
|
|
124
124
|
"react-hook-form": "7.51.0",
|
|
125
125
|
"react-intl": "6.6.2",
|
|
126
126
|
"react-modal": "3.16.1",
|
|
127
|
-
"react-router-dom": "6.22.
|
|
127
|
+
"react-router-dom": "6.22.3",
|
|
128
128
|
"sass": "1.71.1",
|
|
129
129
|
"source-map-loader": "5.0.0",
|
|
130
130
|
"storybook": "7.6.17",
|
|
131
131
|
"tsconfig-paths-webpack-plugin": "4.1.0",
|
|
132
|
-
"typescript": "5.
|
|
132
|
+
"typescript": "5.4.2",
|
|
133
133
|
"uuid": "9.0.1",
|
|
134
134
|
"webpack": "5.90.3",
|
|
135
135
|
"webpack-cli": "5.1.4",
|
|
136
136
|
"whatwg-fetch": "3.6.20",
|
|
137
137
|
"xhr-mock": "2.5.1",
|
|
138
138
|
"yargs": "17.7.2",
|
|
139
|
-
"yup": "1.
|
|
139
|
+
"yup": "1.4.0"
|
|
140
140
|
},
|
|
141
141
|
"resolutions": {
|
|
142
142
|
"@testing-library/dom": "9.3.4",
|
|
143
|
-
"@types/react": "18.2.
|
|
144
|
-
"@types/react-dom": "18.2.
|
|
143
|
+
"@types/react": "18.2.64",
|
|
144
|
+
"@types/react-dom": "18.2.21"
|
|
145
145
|
},
|
|
146
146
|
"msw": {
|
|
147
147
|
"workerDirectory": "../richie/static/richie/js"
|