@axinom/mosaic-ui 0.52.0-rc.6 → 0.52.0-rc.8

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 (22) hide show
  1. package/dist/components/Explorer/Explorer.d.ts.map +1 -1
  2. package/dist/components/Explorer/QuickEdit/QuickEditContext.d.ts +1 -1
  3. package/dist/components/Explorer/QuickEdit/QuickEditContext.d.ts.map +1 -1
  4. package/dist/components/Explorer/QuickEdit/useQuickEdit.d.ts +2 -1
  5. package/dist/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
  6. package/dist/components/Explorer/helpers/useActions.d.ts.map +1 -1
  7. package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts +2 -2
  8. package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts.map +1 -1
  9. package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts.map +1 -1
  10. package/dist/index.es.js +2 -2
  11. package/dist/index.es.js.map +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/index.js.map +1 -1
  14. package/package.json +3 -3
  15. package/src/components/Explorer/Explorer.tsx +1 -0
  16. package/src/components/Explorer/QuickEdit/QuickEditContext.tsx +1 -1
  17. package/src/components/Explorer/QuickEdit/useQuickEdit.spec.tsx +49 -86
  18. package/src/components/Explorer/QuickEdit/useQuickEdit.tsx +15 -15
  19. package/src/components/Explorer/helpers/useActions.ts +6 -3
  20. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.tsx +7 -7
  21. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.ts +5 -3
  22. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.tsx +12 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-ui",
3
- "version": "0.52.0-rc.6",
3
+ "version": "0.52.0-rc.8",
4
4
  "description": "UI components for building Axinom Mosaic applications",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -32,7 +32,7 @@
32
32
  "build-storybook": "storybook build"
33
33
  },
34
34
  "dependencies": {
35
- "@axinom/mosaic-core": "^0.4.25-rc.6",
35
+ "@axinom/mosaic-core": "^0.4.25-rc.8",
36
36
  "@faker-js/faker": "^7.4.0",
37
37
  "@geoffcox/react-splitter": "^2.1.2",
38
38
  "@popperjs/core": "^2.11.8",
@@ -106,5 +106,5 @@
106
106
  "publishConfig": {
107
107
  "access": "public"
108
108
  },
109
- "gitHead": "62061b8cddd421c5b734a0fddedb1bdd0919c8ba"
109
+ "gitHead": "f307889eea9c4a4e4f196af828da5549f1a723c8"
110
110
  }
@@ -289,6 +289,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
289
289
  listRef,
290
290
  registrations: quickEditRegistrations,
291
291
  hasData: data.length > 0,
292
+ defaultItem: data[0],
292
293
  collapseFilters,
293
294
  generateItemLink,
294
295
  });
@@ -3,7 +3,7 @@ import { LocationDescriptor } from 'history';
3
3
  import React from 'react';
4
4
 
5
5
  export interface QuickEditContextType<T> {
6
- selectedItem?: T;
6
+ selectedItem: T;
7
7
  detailsLink?: LocationDescriptor<unknown>;
8
8
  isQuickEditMode?: boolean;
9
9
  registerSaveCallback?: (callback: () => Promise<void>) => void;
@@ -70,15 +70,18 @@ const mockRegistrations: QuickEditRegistration<unknown>[] = [
70
70
  },
71
71
  ];
72
72
 
73
+ const defaultProps: useQuickEditProps<any> = {
74
+ listRef: mockListRef,
75
+ registrations: mockRegistrations,
76
+ collapseFilters: jest.fn(),
77
+ hasData: true,
78
+ defaultItem: { id: 0 },
79
+ generateItemLink: jest.fn(),
80
+ };
81
+
73
82
  describe('useQuickEdit', () => {
74
83
  it('should return the quickEdit action if registrations are defined', () => {
75
- const { TestWrapper, ref } = getTestWrapper({
76
- listRef: mockListRef,
77
- registrations: mockRegistrations,
78
- collapseFilters: jest.fn(),
79
- hasData: true,
80
- generateItemLink: jest.fn(),
81
- });
84
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
82
85
 
83
86
  mount(<TestWrapper quickEditValuesRef={ref} />);
84
87
 
@@ -87,11 +90,8 @@ describe('useQuickEdit', () => {
87
90
 
88
91
  it('should not return the quickEdit action if registrations are not defined', () => {
89
92
  const { TestWrapper, ref } = getTestWrapper({
90
- listRef: mockListRef,
93
+ ...defaultProps,
91
94
  registrations: undefined,
92
- collapseFilters: jest.fn(),
93
- hasData: true,
94
- generateItemLink: jest.fn(),
95
95
  });
96
96
 
97
97
  mount(<TestWrapper quickEditValuesRef={ref} />);
@@ -100,13 +100,7 @@ describe('useQuickEdit', () => {
100
100
  });
101
101
 
102
102
  it('should generate actions for each registration', () => {
103
- const { TestWrapper, ref } = getTestWrapper({
104
- listRef: mockListRef,
105
- registrations: mockRegistrations,
106
- collapseFilters: jest.fn(),
107
- hasData: true,
108
- generateItemLink: jest.fn(),
109
- });
103
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
110
104
 
111
105
  mount(<TestWrapper quickEditValuesRef={ref} />);
112
106
 
@@ -120,11 +114,8 @@ describe('useQuickEdit', () => {
120
114
  it('should set isQuickEditMode to true and collapse filters when quickEditAction is opened', async () => {
121
115
  const collapseFiltersSpy = jest.fn();
122
116
  const { TestWrapper, ref } = getTestWrapper({
123
- listRef: mockListRef,
124
- registrations: mockRegistrations,
117
+ ...defaultProps,
125
118
  collapseFilters: collapseFiltersSpy,
126
- hasData: true,
127
- generateItemLink: jest.fn(),
128
119
  });
129
120
 
130
121
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
@@ -141,13 +132,7 @@ describe('useQuickEdit', () => {
141
132
  });
142
133
 
143
134
  it('should set the first registration as the current registration when quickEditAction is clicked', async () => {
144
- const { TestWrapper, ref } = getTestWrapper({
145
- listRef: mockListRef,
146
- registrations: mockRegistrations,
147
- collapseFilters: jest.fn(),
148
- hasData: true,
149
- generateItemLink: jest.fn(),
150
- });
135
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
151
136
 
152
137
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
153
138
 
@@ -162,13 +147,7 @@ describe('useQuickEdit', () => {
162
147
  });
163
148
 
164
149
  it('should update the registration when a registration action is clicked', async () => {
165
- const { TestWrapper, ref } = getTestWrapper({
166
- listRef: mockListRef,
167
- registrations: mockRegistrations,
168
- collapseFilters: jest.fn(),
169
- hasData: true,
170
- generateItemLink: jest.fn(),
171
- });
150
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
172
151
 
173
152
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
174
153
 
@@ -189,11 +168,8 @@ describe('useQuickEdit', () => {
189
168
 
190
169
  it('should disable the quickEditAction when there is no data', () => {
191
170
  const { TestWrapper, ref } = getTestWrapper({
192
- listRef: mockListRef,
193
- registrations: mockRegistrations,
194
- collapseFilters: jest.fn(),
171
+ ...defaultProps,
195
172
  hasData: false,
196
- generateItemLink: jest.fn(),
197
173
  });
198
174
 
199
175
  mount(<TestWrapper quickEditValuesRef={ref} />);
@@ -206,13 +182,7 @@ describe('useQuickEdit', () => {
206
182
 
207
183
  describe('List Actions', () => {
208
184
  it('should select the first item when quick edit is opened', async () => {
209
- const { TestWrapper, ref } = getTestWrapper({
210
- listRef: mockListRef,
211
- registrations: mockRegistrations,
212
- collapseFilters: jest.fn(),
213
- hasData: true,
214
- generateItemLink: jest.fn(),
215
- });
185
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
216
186
 
217
187
  mount(<TestWrapper quickEditValuesRef={ref} />);
218
188
 
@@ -225,13 +195,7 @@ describe('useQuickEdit', () => {
225
195
  });
226
196
 
227
197
  it('should reset the selection when quick edit is closed', async () => {
228
- const { TestWrapper, ref } = getTestWrapper({
229
- listRef: mockListRef,
230
- registrations: mockRegistrations,
231
- collapseFilters: jest.fn(),
232
- hasData: true,
233
- generateItemLink: jest.fn(),
234
- });
198
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
235
199
 
236
200
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
237
201
 
@@ -252,15 +216,34 @@ describe('useQuickEdit', () => {
252
216
  });
253
217
 
254
218
  describe('QuickEditContextProvider', () => {
255
- it('should provide the correct context values', async () => {
256
- const { TestWrapper, ref } = getTestWrapper({
257
- listRef: mockListRef,
258
- registrations: mockRegistrations,
259
- collapseFilters: jest.fn(),
260
- hasData: true,
261
- generateItemLink: jest.fn(),
219
+ it('should select the default item when quick edit is opened', async () => {
220
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
221
+
222
+ const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
223
+
224
+ await act(async () => {
225
+ ref.current.quickEditAction?.kind === 'group' &&
226
+ (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
262
227
  });
263
228
 
229
+ wrapper.update();
230
+
231
+ const { ContextConsumer, ref: contextRef } = getContextConsumer();
232
+
233
+ mount(
234
+ <ref.current.QuickEditContextProvider>
235
+ <ContextConsumer contextRef={contextRef} />
236
+ </ref.current.QuickEditContextProvider>,
237
+ );
238
+
239
+ expect(contextRef.current.isQuickEditMode).toBe(true);
240
+ expect(contextRef.current.selectedItem).toEqual({ id: 0 });
241
+ expect(contextRef.current.detailsLink).toBe('/test');
242
+ });
243
+
244
+ it('should provide the correct context values', async () => {
245
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
246
+
264
247
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
265
248
 
266
249
  await act(async () => {
@@ -286,13 +269,7 @@ describe('useQuickEdit', () => {
286
269
  });
287
270
 
288
271
  it('should call the save callback when changeSelectedItem is called', async () => {
289
- const { TestWrapper, ref } = getTestWrapper({
290
- listRef: mockListRef,
291
- registrations: mockRegistrations,
292
- collapseFilters: jest.fn(),
293
- hasData: true,
294
- generateItemLink: jest.fn(),
295
- });
272
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
296
273
 
297
274
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
298
275
 
@@ -325,13 +302,7 @@ describe('useQuickEdit', () => {
325
302
  });
326
303
 
327
304
  it('should call the save callback when refresh is called', async () => {
328
- const { TestWrapper, ref } = getTestWrapper({
329
- listRef: mockListRef,
330
- registrations: mockRegistrations,
331
- collapseFilters: jest.fn(),
332
- hasData: true,
333
- generateItemLink: jest.fn(),
334
- });
305
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
335
306
 
336
307
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
337
308
 
@@ -365,11 +336,8 @@ describe('useQuickEdit', () => {
365
336
 
366
337
  it('should generate the correct details link using registration if provided', async () => {
367
338
  const { TestWrapper, ref } = getTestWrapper({
368
- listRef: mockListRef,
339
+ ...defaultProps,
369
340
  registrations: [mockRegistrations[1]],
370
- collapseFilters: jest.fn(),
371
- hasData: true,
372
- generateItemLink: jest.fn(),
373
341
  });
374
342
 
375
343
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
@@ -398,10 +366,8 @@ describe('useQuickEdit', () => {
398
366
  const generateDetailsLinkSpy = jest.fn();
399
367
 
400
368
  const { TestWrapper, ref } = getTestWrapper({
401
- listRef: mockListRef,
369
+ ...defaultProps,
402
370
  registrations: [mockRegistrations[2]],
403
- collapseFilters: jest.fn(),
404
- hasData: true,
405
371
  generateItemLink: generateDetailsLinkSpy,
406
372
  });
407
373
 
@@ -429,11 +395,8 @@ describe('useQuickEdit', () => {
429
395
 
430
396
  it('should not generate a details link if generateDetailsLink in registration is false', async () => {
431
397
  const { TestWrapper, ref } = getTestWrapper({
432
- listRef: mockListRef,
398
+ ...defaultProps,
433
399
  registrations: [mockRegistrations[3]],
434
- collapseFilters: jest.fn(),
435
- hasData: true,
436
- generateItemLink: jest.fn(),
437
400
  });
438
401
 
439
402
  const wrapper = mount(<TestWrapper quickEditValuesRef={ref} />);
@@ -1,5 +1,5 @@
1
1
  import { LocationDescriptor } from 'history';
2
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import React, { useCallback, useMemo, useState } from 'react';
3
3
  import { Data } from '../../../types';
4
4
  import { IconName } from '../../Icons';
5
5
  import { ListElement } from '../../List';
@@ -14,6 +14,7 @@ export interface useQuickEditProps<T extends Data> {
14
14
  listRef: React.RefObject<ListElement>;
15
15
  registrations: QuickEditRegistration<T>[] | undefined;
16
16
  hasData: boolean;
17
+ defaultItem: T;
17
18
  collapseFilters: () => void;
18
19
  generateItemLink?: (data: T) => LocationDescriptor<unknown>;
19
20
  }
@@ -32,6 +33,7 @@ export interface useQuickEditReturnType<T extends Data> {
32
33
  export const useQuickEdit = <T extends Data>({
33
34
  hasData,
34
35
  listRef,
36
+ defaultItem,
35
37
  registrations,
36
38
  collapseFilters,
37
39
  generateItemLink,
@@ -39,7 +41,7 @@ export const useQuickEdit = <T extends Data>({
39
41
  const [isQuickEditMode, setIsQuickEditMode] = useState<boolean>(false);
40
42
  const [currentRegistration, setCurrentRegistration] =
41
43
  useState<QuickEditRegistration<T>>();
42
- const [selectedItem, setSelectedItem] = useState<T>();
44
+ const [selectedItem, setSelectedItem] = useState<T>(defaultItem);
43
45
  const [detailsLink, setDetailsLink] = useState<LocationDescriptor<unknown>>();
44
46
 
45
47
  const updateDetailsLink = useCallback(
@@ -66,18 +68,6 @@ export const useQuickEdit = <T extends Data>({
66
68
  [currentRegistration, selectedItem, updateDetailsLink],
67
69
  );
68
70
 
69
- useEffect(() => {
70
- if (!isQuickEditMode) {
71
- listRef.current?.resetSelection();
72
- }
73
- }, [isQuickEditMode, listRef]);
74
-
75
- useEffect(() => {
76
- if (isQuickEditMode) {
77
- listRef.current?.selectIndex(0);
78
- }
79
- }, [isQuickEditMode, listRef]);
80
-
81
71
  const saveCallbackRef = React.useRef<() => Promise<void>>();
82
72
 
83
73
  const registerSaveCallback = useCallback(
@@ -123,13 +113,21 @@ export const useQuickEdit = <T extends Data>({
123
113
  onActionsGroupToggled: async (isOpen) => {
124
114
  if (!isOpen && selectedItem) {
125
115
  await saveCallbackRef.current?.();
126
- setSelectedItem(undefined);
116
+ setCurrentRegistration(undefined);
117
+ setSelectedItem(defaultItem);
118
+ updateDetailsLink(defaultItem, registrations[0]);
127
119
  setDetailsLink(undefined);
128
120
  setIsQuickEditMode(false);
121
+ listRef.current?.resetSelection();
129
122
  } else {
123
+ if (defaultItem) {
124
+ setSelectedItem(defaultItem);
125
+ updateDetailsLink(defaultItem, registrations[0]);
126
+ }
130
127
  setIsQuickEditMode(isOpen);
131
128
  setCurrentRegistration(isOpen ? registrations[0] : undefined);
132
129
  collapseFilters();
130
+ listRef.current?.selectIndex(0);
133
131
  }
134
132
  },
135
133
  actions: registrations.map((registration) => ({
@@ -151,8 +149,10 @@ export const useQuickEdit = <T extends Data>({
151
149
  [
152
150
  collapseFilters,
153
151
  currentRegistration?.component,
152
+ defaultItem,
154
153
  hasData,
155
154
  isQuickEditMode,
155
+ listRef,
156
156
  registrations,
157
157
  selectedItem,
158
158
  updateDetailsLink,
@@ -104,6 +104,8 @@ export const useActions = <T extends Data>({
104
104
  const headerActions: PageHeaderActionItemProps[] = [];
105
105
 
106
106
  if (hasFilters) {
107
+ headerActions.push({ kind: 'spacer' });
108
+
107
109
  headerActions.push({
108
110
  label:
109
111
  activeFilterCount > 0 ? `Filters (${activeFilterCount})` : 'Filters',
@@ -116,11 +118,11 @@ export const useActions = <T extends Data>({
116
118
  toggleFiltersVisible();
117
119
  },
118
120
  });
119
-
120
- headerActions.push({ kind: 'spacer' });
121
121
  }
122
122
 
123
123
  if (bulkActions && bulkActions.length > 0) {
124
+ headerActions.push({ kind: 'spacer' });
125
+
124
126
  headerActions.push({
125
127
  label: 'Bulk Actions',
126
128
  icon: IconName.Bulk,
@@ -134,10 +136,11 @@ export const useActions = <T extends Data>({
134
136
  groupActionsDisabled:
135
137
  itemSelection.items?.length === 0 || resultCount?.filtered === 0,
136
138
  });
137
- headerActions.push({ kind: 'spacer' });
138
139
  }
139
140
 
140
141
  if (quickEditAction) {
142
+ headerActions.push({ kind: 'spacer' });
143
+
141
144
  headerActions.push(quickEditAction);
142
145
  }
143
146
 
@@ -67,10 +67,10 @@ export const PageHeaderActionsGroup: React.FC<PageHeaderActionsGroupProps> = ({
67
67
  PageHeaderActionsGroupContext,
68
68
  );
69
69
 
70
- const customCollapse = useCallback(() => {
71
- collapse();
72
- onActionsGroupToggled(false);
73
- }, [collapse, onActionsGroupToggled]);
70
+ const customCollapse = useCallback(
71
+ () => Promise.resolve(onActionsGroupToggled(false)).then(() => collapse()),
72
+ [collapse, onActionsGroupToggled],
73
+ );
74
74
 
75
75
  useEffect(() => {
76
76
  registerCollapse(customCollapse, uniqueId);
@@ -139,10 +139,10 @@ export const PageHeaderActionsGroup: React.FC<PageHeaderActionsGroupProps> = ({
139
139
  }
140
140
  icon={icon}
141
141
  onClick={async () => {
142
- if (!isExpanded) {
143
- collapseAll();
144
- }
145
142
  try {
143
+ if (!isExpanded) {
144
+ await collapseAll(uniqueId);
145
+ }
146
146
  await onActionsGroupToggled(!isExpanded);
147
147
  toggleExpanded();
148
148
  } catch (e) {
@@ -2,12 +2,14 @@ import { createContext } from 'react';
2
2
  import { noop } from '../../../helpers/utils';
3
3
 
4
4
  export interface PageHeaderActionsGroupContextType {
5
- registerCollapse: (readonly: () => void, uuid: string) => void;
6
- collapseAll: () => void;
5
+ registerCollapse: (readonly: () => Promise<void>, uuid: string) => void;
6
+ collapseAll: (uuid: string) => Promise<void[]>;
7
7
  }
8
8
 
9
9
  export const PageHeaderActionsGroupContext =
10
10
  createContext<PageHeaderActionsGroupContextType>({
11
11
  registerCollapse: noop,
12
- collapseAll: noop,
12
+ collapseAll: async () => {
13
+ return [];
14
+ },
13
15
  });
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useState } from 'react';
2
2
  import { PageHeaderActionsGroupContext } from './PageHeaderActionsGroupsContext';
3
3
 
4
- type CollapseAction = Record<string, () => void>;
4
+ type CollapseAction = Record<string, () => Promise<void>>;
5
5
 
6
6
  export const PageHeaderActionsGroupContextProvider: React.FC = ({
7
7
  children,
@@ -10,15 +10,22 @@ export const PageHeaderActionsGroupContextProvider: React.FC = ({
10
10
  useState<CollapseAction>({});
11
11
 
12
12
  const registerCollapse = useCallback(
13
- (collapse: () => void, uuid: string): void => {
13
+ (collapse: () => Promise<void>, uuid: string): void => {
14
14
  setRegisteredCollapses((prev) => ({ ...prev, [uuid]: collapse }));
15
15
  },
16
16
  [],
17
17
  );
18
18
 
19
- const collapseAll = useCallback(() => {
20
- Object.values(registeredCollapses).forEach((collapse) => collapse());
21
- }, [registeredCollapses]);
19
+ const collapseAll = useCallback(
20
+ (uuid: string) => {
21
+ return Promise.all(
22
+ Object.keys(registeredCollapses)
23
+ .filter((key) => key !== uuid)
24
+ .map((key) => registeredCollapses[key]()),
25
+ );
26
+ },
27
+ [registeredCollapses],
28
+ );
22
29
 
23
30
  return (
24
31
  <PageHeaderActionsGroupContext.Provider