@axinom/mosaic-ui 0.75.0-rc.5 → 0.77.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.
Files changed (183) hide show
  1. package/dist/THIRD-PARTY-LICENSES.txt +1296 -0
  2. package/dist/cjs/components/ActionBar/ActionBar.d.ts +26 -0
  3. package/dist/cjs/components/ActionBar/ActionBar.d.ts.map +1 -0
  4. package/dist/cjs/components/ActionBar/ActionBar.model.d.ts +49 -0
  5. package/dist/cjs/components/ActionBar/ActionBar.model.d.ts.map +1 -0
  6. package/dist/cjs/components/ActionBar/ActionBarButton/ActionBarButton.d.ts +22 -0
  7. package/dist/cjs/components/ActionBar/ActionBarButton/ActionBarButton.d.ts.map +1 -0
  8. package/dist/cjs/components/ActionBar/helpers.d.ts +22 -0
  9. package/dist/cjs/components/ActionBar/helpers.d.ts.map +1 -0
  10. package/dist/cjs/components/ActionBar/index.d.ts +3 -0
  11. package/dist/cjs/components/ActionBar/index.d.ts.map +1 -0
  12. package/dist/cjs/components/Actions/Action/Action.d.ts.map +1 -1
  13. package/dist/cjs/components/ConfirmDialog/index.d.ts +1 -0
  14. package/dist/cjs/components/ConfirmDialog/index.d.ts.map +1 -1
  15. package/dist/cjs/components/ConfirmDialog/useConfirmableAction.d.ts +55 -0
  16. package/dist/cjs/components/ConfirmDialog/useConfirmableAction.d.ts.map +1 -0
  17. package/dist/cjs/components/Explorer/BulkEdit/useBulkEdit.d.ts.map +1 -1
  18. package/dist/cjs/components/Explorer/Explorer.d.ts.map +1 -1
  19. package/dist/cjs/components/Explorer/QuickEdit/useQuickEdit.d.ts +14 -3
  20. package/dist/cjs/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
  21. package/dist/cjs/components/Explorer/helpers/useActions.d.ts +15 -4
  22. package/dist/cjs/components/Explorer/helpers/useActions.d.ts.map +1 -1
  23. package/dist/cjs/components/Explorer/helpers/useSubtitle.d.ts.map +1 -1
  24. package/dist/cjs/components/List/List.d.ts +17 -3
  25. package/dist/cjs/components/List/List.d.ts.map +1 -1
  26. package/dist/cjs/components/List/List.model.d.ts +5 -1
  27. package/dist/cjs/components/List/List.model.d.ts.map +1 -1
  28. package/dist/cjs/components/List/ListHeader/ListHeader.d.ts +10 -1
  29. package/dist/cjs/components/List/ListHeader/ListHeader.d.ts.map +1 -1
  30. package/dist/cjs/components/List/ListRow/ListRow.d.ts +16 -5
  31. package/dist/cjs/components/List/ListRow/ListRow.d.ts.map +1 -1
  32. package/dist/cjs/components/List/ListRowRenderer/ListRowRenderer.d.ts.map +1 -1
  33. package/dist/cjs/components/List/helpers.d.ts +11 -3
  34. package/dist/cjs/components/List/helpers.d.ts.map +1 -1
  35. package/dist/cjs/components/List/useColumnsSize.d.ts +4 -2
  36. package/dist/cjs/components/List/useColumnsSize.d.ts.map +1 -1
  37. package/dist/cjs/components/PageHeader/PageHeader.d.ts +4 -1
  38. package/dist/cjs/components/PageHeader/PageHeader.d.ts.map +1 -1
  39. package/dist/cjs/components/PageHeader/PageHeader.model.d.ts +37 -1
  40. package/dist/cjs/components/PageHeader/PageHeader.model.d.ts.map +1 -1
  41. package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts +3 -0
  42. package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
  43. package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts +8 -0
  44. package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts.map +1 -1
  45. package/dist/cjs/components/PageHeader/helpers.d.ts +9 -0
  46. package/dist/cjs/components/PageHeader/helpers.d.ts.map +1 -0
  47. package/dist/cjs/components/index.d.ts +1 -0
  48. package/dist/cjs/components/index.d.ts.map +1 -1
  49. package/dist/cjs/hooks/index.d.ts +1 -0
  50. package/dist/cjs/hooks/index.d.ts.map +1 -1
  51. package/dist/cjs/hooks/useElementWidthObserver/useElementWidthObserver.d.ts.map +1 -0
  52. package/dist/cjs/index.js +4 -4
  53. package/dist/cjs/index.js.map +1 -1
  54. package/dist/esm/components/ActionBar/ActionBar.d.ts +26 -0
  55. package/dist/esm/components/ActionBar/ActionBar.d.ts.map +1 -0
  56. package/dist/esm/components/ActionBar/ActionBar.model.d.ts +49 -0
  57. package/dist/esm/components/ActionBar/ActionBar.model.d.ts.map +1 -0
  58. package/dist/esm/components/ActionBar/ActionBarButton/ActionBarButton.d.ts +22 -0
  59. package/dist/esm/components/ActionBar/ActionBarButton/ActionBarButton.d.ts.map +1 -0
  60. package/dist/esm/components/ActionBar/helpers.d.ts +22 -0
  61. package/dist/esm/components/ActionBar/helpers.d.ts.map +1 -0
  62. package/dist/esm/components/ActionBar/index.d.ts +3 -0
  63. package/dist/esm/components/ActionBar/index.d.ts.map +1 -0
  64. package/dist/esm/components/Actions/Action/Action.d.ts.map +1 -1
  65. package/dist/esm/components/ConfirmDialog/index.d.ts +1 -0
  66. package/dist/esm/components/ConfirmDialog/index.d.ts.map +1 -1
  67. package/dist/esm/components/ConfirmDialog/useConfirmableAction.d.ts +55 -0
  68. package/dist/esm/components/ConfirmDialog/useConfirmableAction.d.ts.map +1 -0
  69. package/dist/esm/components/Explorer/BulkEdit/useBulkEdit.d.ts.map +1 -1
  70. package/dist/esm/components/Explorer/Explorer.d.ts.map +1 -1
  71. package/dist/esm/components/Explorer/QuickEdit/useQuickEdit.d.ts +14 -3
  72. package/dist/esm/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
  73. package/dist/esm/components/Explorer/helpers/useActions.d.ts +15 -4
  74. package/dist/esm/components/Explorer/helpers/useActions.d.ts.map +1 -1
  75. package/dist/esm/components/Explorer/helpers/useSubtitle.d.ts.map +1 -1
  76. package/dist/esm/components/List/List.d.ts +17 -3
  77. package/dist/esm/components/List/List.d.ts.map +1 -1
  78. package/dist/esm/components/List/List.model.d.ts +5 -1
  79. package/dist/esm/components/List/List.model.d.ts.map +1 -1
  80. package/dist/esm/components/List/ListHeader/ListHeader.d.ts +10 -1
  81. package/dist/esm/components/List/ListHeader/ListHeader.d.ts.map +1 -1
  82. package/dist/esm/components/List/ListRow/ListRow.d.ts +16 -5
  83. package/dist/esm/components/List/ListRow/ListRow.d.ts.map +1 -1
  84. package/dist/esm/components/List/ListRowRenderer/ListRowRenderer.d.ts.map +1 -1
  85. package/dist/esm/components/List/helpers.d.ts +11 -3
  86. package/dist/esm/components/List/helpers.d.ts.map +1 -1
  87. package/dist/esm/components/List/useColumnsSize.d.ts +4 -2
  88. package/dist/esm/components/List/useColumnsSize.d.ts.map +1 -1
  89. package/dist/esm/components/PageHeader/PageHeader.d.ts +4 -1
  90. package/dist/esm/components/PageHeader/PageHeader.d.ts.map +1 -1
  91. package/dist/esm/components/PageHeader/PageHeader.model.d.ts +37 -1
  92. package/dist/esm/components/PageHeader/PageHeader.model.d.ts.map +1 -1
  93. package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts +3 -0
  94. package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
  95. package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts +8 -0
  96. package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts.map +1 -1
  97. package/dist/esm/components/PageHeader/helpers.d.ts +9 -0
  98. package/dist/esm/components/PageHeader/helpers.d.ts.map +1 -0
  99. package/dist/esm/components/index.d.ts +1 -0
  100. package/dist/esm/components/index.d.ts.map +1 -1
  101. package/dist/esm/hooks/index.d.ts +1 -0
  102. package/dist/esm/hooks/index.d.ts.map +1 -1
  103. package/dist/esm/hooks/useElementWidthObserver/useElementWidthObserver.d.ts.map +1 -0
  104. package/dist/esm/index.js +4 -4
  105. package/dist/esm/index.js.map +1 -1
  106. package/package.json +7 -3
  107. package/src/components/ActionBar/ActionBar.model.ts +61 -0
  108. package/src/components/ActionBar/ActionBar.module.scss +64 -0
  109. package/src/components/ActionBar/ActionBar.spec.tsx +398 -0
  110. package/src/components/ActionBar/ActionBar.stories.tsx +205 -0
  111. package/src/components/ActionBar/ActionBar.tsx +170 -0
  112. package/src/components/ActionBar/ActionBarButton/ActionBarButton.module.scss +140 -0
  113. package/src/components/ActionBar/ActionBarButton/ActionBarButton.tsx +193 -0
  114. package/src/components/ActionBar/helpers.spec.ts +41 -0
  115. package/src/components/ActionBar/helpers.ts +54 -0
  116. package/src/components/ActionBar/index.ts +2 -0
  117. package/src/components/Actions/Action/Action.spec.tsx +64 -2
  118. package/src/components/Actions/Action/Action.tsx +6 -1
  119. package/src/components/ConfirmDialog/index.ts +1 -0
  120. package/src/components/ConfirmDialog/useConfirmableAction.tsx +159 -0
  121. package/src/components/EmptyStation/EmptyStation.stories.tsx +97 -0
  122. package/src/components/Explorer/BulkEdit/useBulkEdit.tsx +2 -0
  123. package/src/components/Explorer/Explorer.spec.tsx +97 -8
  124. package/src/components/Explorer/Explorer.tsx +26 -13
  125. package/src/components/Explorer/NavigationExplorer/NavigationExplorer.stories.tsx +124 -1
  126. package/src/components/Explorer/QuickEdit/useQuickEdit.spec.tsx +94 -50
  127. package/src/components/Explorer/QuickEdit/useQuickEdit.tsx +101 -42
  128. package/src/components/Explorer/SelectionExplorer/SelectionExplorer.spec.tsx +46 -13
  129. package/src/components/Explorer/helpers/useActions.ts +125 -73
  130. package/src/components/Explorer/helpers/useSubtitle.spec.tsx +33 -23
  131. package/src/components/Explorer/helpers/useSubtitle.tsx +18 -26
  132. package/src/components/List/List.model.ts +5 -1
  133. package/src/components/List/List.module.scss +25 -6
  134. package/src/components/List/List.spec.tsx +498 -1
  135. package/src/components/List/List.stories.tsx +137 -8
  136. package/src/components/List/List.tsx +305 -194
  137. package/src/components/List/ListHeader/ListHeader.module.scss +14 -0
  138. package/src/components/List/ListHeader/ListHeader.spec.tsx +1 -0
  139. package/src/components/List/ListHeader/ListHeader.tsx +25 -8
  140. package/src/components/List/ListRow/ListRow.module.scss +1 -1
  141. package/src/components/List/ListRow/ListRow.spec.tsx +109 -22
  142. package/src/components/List/ListRow/ListRow.tsx +52 -13
  143. package/src/components/List/ListRowRenderer/ListRowRenderer.tsx +28 -22
  144. package/src/components/List/helpers.spec.ts +29 -0
  145. package/src/components/List/helpers.ts +17 -9
  146. package/src/components/List/useColumnsSize.ts +17 -7
  147. package/src/components/PageHeader/PageHeader.model.ts +38 -1
  148. package/src/components/PageHeader/PageHeader.module.scss +39 -18
  149. package/src/components/PageHeader/PageHeader.spec.tsx +407 -18
  150. package/src/components/PageHeader/PageHeader.stories.tsx +95 -12
  151. package/src/components/PageHeader/PageHeader.tsx +89 -38
  152. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.model.ts +8 -0
  153. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.module.scss +50 -42
  154. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.spec.tsx +193 -1
  155. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.stories.tsx +36 -0
  156. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +81 -130
  157. package/src/components/PageHeader/helpers.spec.ts +60 -0
  158. package/src/components/PageHeader/helpers.ts +36 -0
  159. package/src/components/index.ts +1 -0
  160. package/src/hooks/index.ts +1 -0
  161. package/src/styles/variables.scss +41 -7
  162. package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts +0 -35
  163. package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts.map +0 -1
  164. package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts +0 -6
  165. package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts.map +0 -1
  166. package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts +0 -3
  167. package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts.map +0 -1
  168. package/dist/cjs/components/PageHeader/helpers/useElementWidthObserver.d.ts.map +0 -1
  169. package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts +0 -35
  170. package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts.map +0 -1
  171. package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts +0 -6
  172. package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts.map +0 -1
  173. package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts +0 -3
  174. package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts.map +0 -1
  175. package/dist/esm/components/PageHeader/helpers/useElementWidthObserver.d.ts.map +0 -1
  176. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.module.scss +0 -77
  177. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.spec.tsx +0 -122
  178. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.tsx +0 -235
  179. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.ts +0 -15
  180. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.tsx +0 -37
  181. /package/dist/cjs/{components/PageHeader/helpers → hooks/useElementWidthObserver}/useElementWidthObserver.d.ts +0 -0
  182. /package/dist/esm/{components/PageHeader/helpers → hooks/useElementWidthObserver}/useElementWidthObserver.d.ts +0 -0
  183. /package/src/{components/PageHeader/helpers → hooks/useElementWidthObserver}/useElementWidthObserver.tsx +0 -0
@@ -315,12 +315,26 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
315
315
  onForcedReload: resetSelection,
316
316
  });
317
317
 
318
+ // Only one set of actions can be on display at a time, so opening quick edit
319
+ // puts the bulk actions away.
320
+ const closeBulkActions = useCallback((): void => {
321
+ if (!isBulkOpen) {
322
+ return;
323
+ }
324
+
325
+ setIsBulkOpen(false);
326
+ closeBulkEdit();
327
+ onBulkActionsToggled(false);
328
+ }, [closeBulkEdit, isBulkOpen, onBulkActionsToggled]);
329
+
318
330
  const {
319
331
  isQuickEditMode,
320
- quickEditAction,
332
+ quickEditToggleAction,
333
+ quickEditActionBar,
321
334
  QuickEditContextProvider,
322
335
  quickEditStation,
323
336
  changeSelectedItem,
337
+ closeQuickEdit,
324
338
  } = useQuickEdit({
325
339
  listRef,
326
340
  registrations: quickEditRegistrations,
@@ -329,6 +343,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
329
343
  quickEditConfig,
330
344
  collapseFilters,
331
345
  generateItemLink,
346
+ onBeforeOpen: closeBulkActions,
332
347
  });
333
348
 
334
349
  const listSelectionMode = getListSelectionMode(
@@ -381,14 +396,15 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
381
396
  [inlineMenuActions, setStationMessage],
382
397
  );
383
398
 
384
- const { actions: pageHeaderActions } = useActions<T>({
399
+ const { actions: pageHeaderActions, actionBar } = useActions<T>({
385
400
  actions,
386
401
  bulkActions,
387
- quickEditAction,
402
+ quickEditToggleAction,
403
+ quickEditActionBar,
404
+ actionBarLabel: resultsTitle,
388
405
  bulkEditAction,
389
- openBulkActionsOnStart,
406
+ isBulkOpen,
390
407
  resultCount,
391
- activeFilterCount: Object.keys(activeFilters).length,
392
408
  itemSelection,
393
409
  getBulkActionSelection,
394
410
  onReloadData,
@@ -396,14 +412,9 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
396
412
  onBulkActionsToggled,
397
413
  setIsBulkOpen,
398
414
  closeBulkEdit,
415
+ closeQuickEdit,
399
416
  });
400
417
 
401
- useEffect(() => {
402
- if (listSelectionMode !== ListSelectMode.Multi) {
403
- resetSelection();
404
- }
405
- }, [listSelectionMode, resetSelection]);
406
-
407
418
  return (
408
419
  <ConditionalSplit
409
420
  shouldSplit={isQuickEditMode || isBulkEditMode}
@@ -422,7 +433,6 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
422
433
  >
423
434
  <PageHeader
424
435
  title={title}
425
- subtitle={resultsTitle}
426
436
  actions={pageHeaderActions}
427
437
  setTabTitle={setTabTitle}
428
438
  />
@@ -453,11 +463,14 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
453
463
  listSelectionMode !== ListSelectMode.None
454
464
  } // or hard code to `true`?
455
465
  selectionMode={listSelectionMode}
466
+ actionBar={actionBar}
456
467
  enableSelectAll={enableSelectAll}
457
468
  enableSelectAllDeselect={enableSelectAll && !hasMoreData}
458
469
  loadingTriggerOffset={loadingTriggerOffset}
459
470
  defaultSortOrder={sortOrder}
460
- generateItemLink={!isQuickEditMode ? generateItemLink : undefined}
471
+ // Kept set while quick edit is open: the List suppresses the
472
+ // navigation itself, and dropping the link would remount every row.
473
+ generateItemLink={generateItemLink}
461
474
  onItemClicked={onItemClickedHandler}
462
475
  onItemSelected={itemSelectedHandler}
463
476
  onRequestMoreData={onRequestMoreData}
@@ -1,6 +1,7 @@
1
1
  import { faker } from '@faker-js/faker';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
- import React, { useMemo } from 'react';
3
+ import { Field } from 'formik';
4
+ import React, { useContext, useMemo } from 'react';
4
5
  import { MemoryRouter } from 'react-router-dom';
5
6
  import { action } from 'storybook/actions';
6
7
  import {
@@ -8,14 +9,19 @@ import {
8
9
  generateItemArray,
9
10
  randomDate,
10
11
  } from '../../../helpers/storybook';
12
+ import { initializeUi } from '../../../initialize';
11
13
  import type { Data } from '../../../types/data';
12
14
  import { type FilterType, FilterTypes } from '../../Filters/Filters.model';
15
+ import { ReadOnlyField, SingleLineTextField } from '../../FormElements';
16
+ import { FormStation } from '../../FormStation';
13
17
  import { IconName } from '../../Icons';
18
+ import { type BulkEditConfig, generateBulkEditMutation } from '../BulkEdit';
14
19
  import type {
15
20
  ExplorerBulkAction,
16
21
  ExplorerDataProvider,
17
22
  } from '../Explorer.model';
18
23
  import { NavigationExplorer } from '../NavigationExplorer/NavigationExplorer';
24
+ import { QuickEditContext } from '../QuickEdit/QuickEditContext';
19
25
  import {
20
26
  createInMemoryDataProvider,
21
27
  findAnywhereInStringCaseInsensitive,
@@ -44,6 +50,9 @@ const groups = createGroups({
44
50
  'inlineMenuActions',
45
51
  'keyProperty',
46
52
  'calculateNavigateUrl',
53
+ 'quickEditRegistrations',
54
+ 'quickEditConfig',
55
+ 'bulkEditRegistration',
47
56
  ],
48
57
  Behavior: [
49
58
  'selectionMode',
@@ -135,8 +144,84 @@ const slowFunc = (): Promise<unknown> => {
135
144
  return new Promise((resolve) => setTimeout(resolve, 1000));
136
145
  };
137
146
 
147
+ // Reports the notifications the stations raise, e.g. when a bulk action starts,
148
+ // on the actions panel.
149
+ initializeUi({
150
+ showNotification: (args) => {
151
+ action('showNotification')(args);
152
+ return faker.random.numeric();
153
+ },
154
+ addIndicator: (args) => {
155
+ action('addIndicator')(args);
156
+ return Math.random();
157
+ },
158
+ removeIndicator: (args) => {
159
+ action('removeIndicator')(args);
160
+ },
161
+ on: (event, callback) => {
162
+ action('on')(event, callback);
163
+ },
164
+ setTitle: (args) => {
165
+ action('setTitle')(args);
166
+ },
167
+ setSaveIndicator: (args) => {
168
+ action('setSaveIndicator')(args);
169
+ },
170
+ });
171
+
172
+ /** A quick edit station, editing a single property of the selected item. */
173
+ const QuickEditForm: React.FC<{
174
+ property: keyof NavExplorerStoryData;
175
+ label: string;
176
+ }> = ({ property, label }) => {
177
+ const { selectedItem } = useContext(QuickEditContext);
178
+
179
+ return (
180
+ <FormStation<NavExplorerStoryData>
181
+ initialData={{ loading: false, data: selectedItem }}
182
+ titleProperty={'title'}
183
+ saveData={(values) => {
184
+ action('quickEditSaveData')(values);
185
+ }}
186
+ >
187
+ <>
188
+ <Field name="id" label="Id" as={ReadOnlyField} />
189
+ <Field name={property} label={label} as={SingleLineTextField} />
190
+ </>
191
+ </FormStation>
192
+ );
193
+ };
194
+
195
+ const bulkEditConfig: BulkEditConfig = {
196
+ mutation: 'bulkEditItems',
197
+ fields: {
198
+ title: {
199
+ type: 'String',
200
+ label: 'Title',
201
+ originalFieldName: 'title',
202
+ action: 'set',
203
+ },
204
+ desc: {
205
+ type: 'String',
206
+ label: 'Description',
207
+ originalFieldName: 'desc',
208
+ action: 'set',
209
+ clearable: true,
210
+ clearValue: '',
211
+ },
212
+ },
213
+ };
214
+
138
215
  export const NavExplorer: StoryObj<NavExplorerStoryType> = {
139
216
  name: 'Navigation Explorer',
217
+ parameters: {
218
+ docs: {
219
+ description: {
220
+ story: `A Navigation Explorer that offers bulk actions, including bulk edit, and quick edit.
221
+ Bulk actions apply to the selected items, while quick edit opens a station for the item that is clicked.`,
222
+ },
223
+ },
224
+ },
140
225
  decorators: [
141
226
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
142
227
  (Story) => (
@@ -152,6 +237,7 @@ export const NavExplorer: StoryObj<NavExplorerStoryType> = {
152
237
  propertyName: 'id',
153
238
  label: 'Id',
154
239
  size: '50px',
240
+ horizontalColumnAlign: 'center',
155
241
  },
156
242
  {
157
243
  propertyName: 'title',
@@ -165,7 +251,44 @@ export const NavExplorer: StoryObj<NavExplorerStoryType> = {
165
251
  stationKey: 'StoryBookExplorer_NavigationExplorer',
166
252
  filterOptions: [freeTextFilter, optionFilter],
167
253
  bulkActions: generateBulkActions(4),
254
+ inlineMenuActions: (rowData) => [
255
+ {
256
+ label: 'Open Details',
257
+ path: `/details/${rowData.id}`,
258
+ icon: IconName.NavigateRight,
259
+ },
260
+ {
261
+ label: 'Duplicate',
262
+ onActionSelected: action('onDuplicateSelected'),
263
+ icon: IconName.Copy,
264
+ },
265
+ {
266
+ label: 'Delete',
267
+ onActionSelected: action('onDeleteSelected'),
268
+ icon: IconName.Delete,
269
+ confirmationMode: 'Simple',
270
+ },
271
+ ],
168
272
  calculateNavigateUrl: (rowData) => `/details/${rowData.id}`,
273
+ quickEditRegistrations: [
274
+ {
275
+ label: 'Title',
276
+ component: <QuickEditForm property="title" label="Title" />,
277
+ },
278
+ {
279
+ label: 'Description',
280
+ component: <QuickEditForm property="desc" label="Description" />,
281
+ },
282
+ ],
283
+ bulkEditRegistration: {
284
+ config: bulkEditConfig,
285
+ saveData: async (data, items) => {
286
+ action('bulkEditSaveData')(
287
+ generateBulkEditMutation(bulkEditConfig, data),
288
+ items,
289
+ );
290
+ },
291
+ },
169
292
  },
170
293
  render: (args) =>
171
294
  React.createElement(() => {
@@ -89,7 +89,7 @@ describe('useQuickEdit', () => {
89
89
 
90
90
  render(<TestWrapper quickEditValuesRef={ref} />);
91
91
 
92
- expect(ref.current.quickEditAction).toBeDefined();
92
+ expect(ref.current.quickEditToggleAction).toBeDefined();
93
93
  });
94
94
 
95
95
  it('should not return the quickEdit action if registrations are not defined', () => {
@@ -100,22 +100,89 @@ describe('useQuickEdit', () => {
100
100
 
101
101
  render(<TestWrapper quickEditValuesRef={ref} />);
102
102
 
103
- expect(ref.current.quickEditAction).toBeUndefined();
103
+ expect(ref.current.quickEditToggleAction).toBeUndefined();
104
104
  });
105
105
 
106
- it('should generate actions for each registration', () => {
106
+ it('should not generate an action bar while quick edit is closed', () => {
107
107
  const { TestWrapper, ref } = getTestWrapper(defaultProps);
108
108
 
109
109
  render(<TestWrapper quickEditValuesRef={ref} />);
110
110
 
111
- expect(ref.current.quickEditAction?.kind).toBe('group');
111
+ expect(ref.current.quickEditActionBar).toBeUndefined();
112
+ });
113
+
114
+ it('should generate an action bar action for each registration once opened', async () => {
115
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
116
+
117
+ render(<TestWrapper quickEditValuesRef={ref} />);
118
+
119
+ await act(async () => {
120
+ await ref.current.quickEditToggleAction?.onClick?.();
121
+ });
122
+
123
+ expect(ref.current.quickEditActionBar?.actions).toHaveLength(
124
+ mockRegistrations.length,
125
+ );
126
+ });
127
+
128
+ it('should mark the current registration as the toggled action', async () => {
129
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
130
+
131
+ render(<TestWrapper quickEditValuesRef={ref} />);
132
+
133
+ await act(async () => {
134
+ await ref.current.quickEditToggleAction?.onClick?.();
135
+ });
136
+
112
137
  expect(
113
- ref.current.quickEditAction?.kind === 'group' &&
114
- ref.current.quickEditAction.actions,
115
- ).toHaveLength(mockRegistrations.length);
138
+ ref.current.quickEditActionBar?.actions.map((action) => action.toggled),
139
+ ).toEqual([true, false, false, false]);
116
140
  });
117
141
 
118
- it('should set isQuickEditMode to true and collapse filters when quickEditAction is opened', async () => {
142
+ it('should toggle the page header action while quick edit is open', async () => {
143
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
144
+
145
+ render(<TestWrapper quickEditValuesRef={ref} />);
146
+
147
+ expect(ref.current.quickEditToggleAction?.toggled).toBe(false);
148
+
149
+ await act(async () => {
150
+ await ref.current.quickEditToggleAction?.onClick?.();
151
+ });
152
+
153
+ expect(ref.current.quickEditToggleAction?.toggled).toBe(true);
154
+ });
155
+
156
+ it('should put away what is on display before quick edit is opened', async () => {
157
+ const onBeforeOpenSpy = vi.fn();
158
+ const { TestWrapper, ref } = getTestWrapper({
159
+ ...defaultProps,
160
+ onBeforeOpen: onBeforeOpenSpy,
161
+ });
162
+
163
+ render(<TestWrapper quickEditValuesRef={ref} />);
164
+
165
+ await act(async () => {
166
+ await ref.current.quickEditToggleAction?.onClick?.();
167
+ });
168
+
169
+ expect(onBeforeOpenSpy).toHaveBeenCalled();
170
+ });
171
+
172
+ it('should do nothing when closing quick edit that is not open', async () => {
173
+ const { TestWrapper, ref } = getTestWrapper(defaultProps);
174
+
175
+ render(<TestWrapper quickEditValuesRef={ref} />);
176
+
177
+ await act(async () => {
178
+ await ref.current.closeQuickEdit();
179
+ });
180
+
181
+ expect(ref.current.isQuickEditMode).toBe(false);
182
+ expect(ref.current.quickEditStation).toBeUndefined();
183
+ });
184
+
185
+ it('should set isQuickEditMode to true and collapse filters when quick edit is opened', async () => {
119
186
  const collapseFiltersSpy = vi.fn();
120
187
  const { TestWrapper, ref } = getTestWrapper({
121
188
  ...defaultProps,
@@ -125,22 +192,20 @@ describe('useQuickEdit', () => {
125
192
  render(<TestWrapper quickEditValuesRef={ref} />);
126
193
 
127
194
  await act(async () => {
128
- ref.current.quickEditAction?.kind === 'group' &&
129
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
195
+ await ref.current.quickEditToggleAction?.onClick?.();
130
196
  });
131
197
 
132
198
  expect(ref.current.isQuickEditMode).toBe(true);
133
199
  expect(collapseFiltersSpy).toHaveBeenCalled();
134
200
  });
135
201
 
136
- it('should set the first registration as the current registration when quickEditAction is clicked', async () => {
202
+ it('should set the first registration as the current registration when quick edit is opened', async () => {
137
203
  const { TestWrapper, ref } = getTestWrapper(defaultProps);
138
204
 
139
205
  render(<TestWrapper quickEditValuesRef={ref} />);
140
206
 
141
207
  await act(async () => {
142
- ref.current.quickEditAction?.kind === 'group' &&
143
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
208
+ await ref.current.quickEditToggleAction?.onClick?.();
144
209
  });
145
210
 
146
211
  expect(ref.current.quickEditStation).toBe(mockRegistrations[0].component);
@@ -152,19 +217,17 @@ describe('useQuickEdit', () => {
152
217
  render(<TestWrapper quickEditValuesRef={ref} />);
153
218
 
154
219
  await act(async () => {
155
- ref.current.quickEditAction?.kind === 'group' &&
156
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
220
+ await ref.current.quickEditToggleAction?.onClick?.();
157
221
  });
158
222
 
159
223
  await act(async () => {
160
- ref.current.quickEditAction?.kind === 'group' &&
161
- ref.current.quickEditAction.actions[1].onClick?.();
224
+ await ref.current.quickEditActionBar?.actions[1].onClick?.();
162
225
  });
163
226
 
164
227
  expect(ref.current.quickEditStation).toBe(mockRegistrations[1].component);
165
228
  });
166
229
 
167
- it('should disable the quickEditAction when there is no data', () => {
230
+ it('should disable the quick edit toggle when there is no data', () => {
168
231
  const { TestWrapper, ref } = getTestWrapper({
169
232
  ...defaultProps,
170
233
  hasData: false,
@@ -172,10 +235,7 @@ describe('useQuickEdit', () => {
172
235
 
173
236
  render(<TestWrapper quickEditValuesRef={ref} />);
174
237
 
175
- expect(
176
- ref.current.quickEditAction?.kind === 'group' &&
177
- ref.current.quickEditAction.disabled,
178
- ).toBe(true);
238
+ expect(ref.current.quickEditToggleAction?.disabled).toBe(true);
179
239
  });
180
240
 
181
241
  it('should use "Quick View" as the default label when no quickEditConfig is provided', () => {
@@ -183,10 +243,7 @@ describe('useQuickEdit', () => {
183
243
 
184
244
  render(<TestWrapper quickEditValuesRef={ref} />);
185
245
 
186
- expect(
187
- ref.current.quickEditAction?.kind === 'group' &&
188
- ref.current.quickEditAction.label,
189
- ).toBe('Quick View');
246
+ expect(ref.current.quickEditToggleAction?.label).toBe('Quick View');
190
247
  });
191
248
 
192
249
  it('should use the label from quickEditConfig when provided', () => {
@@ -198,10 +255,7 @@ describe('useQuickEdit', () => {
198
255
 
199
256
  render(<TestWrapper quickEditValuesRef={ref} />);
200
257
 
201
- expect(
202
- ref.current.quickEditAction?.kind === 'group' &&
203
- ref.current.quickEditAction.label,
204
- ).toBe('Custom Label');
258
+ expect(ref.current.quickEditToggleAction?.label).toBe('Custom Label');
205
259
  });
206
260
 
207
261
  describe('List Actions', () => {
@@ -211,8 +265,7 @@ describe('useQuickEdit', () => {
211
265
  render(<TestWrapper quickEditValuesRef={ref} />);
212
266
 
213
267
  await act(async () => {
214
- ref.current.quickEditAction?.kind === 'group' &&
215
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
268
+ await ref.current.quickEditToggleAction?.onClick?.();
216
269
  });
217
270
 
218
271
  expect(mockListRef.current.selectIndex).toHaveBeenCalledWith(0);
@@ -224,13 +277,11 @@ describe('useQuickEdit', () => {
224
277
  render(<TestWrapper quickEditValuesRef={ref} />);
225
278
 
226
279
  await act(async () => {
227
- ref.current.quickEditAction?.kind === 'group' &&
228
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
280
+ await ref.current.quickEditToggleAction?.onClick?.();
229
281
  });
230
282
 
231
283
  await act(async () => {
232
- ref.current.quickEditAction?.kind === 'group' &&
233
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(false));
284
+ await ref.current.quickEditToggleAction?.onClick?.();
234
285
  });
235
286
 
236
287
  expect(mockListRef.current.resetSelection).toHaveBeenCalled();
@@ -244,8 +295,7 @@ describe('useQuickEdit', () => {
244
295
  render(<TestWrapper quickEditValuesRef={ref} />);
245
296
 
246
297
  await act(async () => {
247
- ref.current.quickEditAction?.kind === 'group' &&
248
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
298
+ await ref.current.quickEditToggleAction?.onClick?.();
249
299
  });
250
300
 
251
301
  const { ContextConsumer, ref: contextRef } = getContextConsumer();
@@ -266,8 +316,7 @@ describe('useQuickEdit', () => {
266
316
  render(<TestWrapper quickEditValuesRef={ref} />);
267
317
 
268
318
  await act(async () => {
269
- ref.current.quickEditAction?.kind === 'group' &&
270
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
319
+ await ref.current.quickEditToggleAction?.onClick?.();
271
320
  ref.current.changeSelectedItem({ id: 1 });
272
321
  });
273
322
 
@@ -289,8 +338,7 @@ describe('useQuickEdit', () => {
289
338
  render(<TestWrapper quickEditValuesRef={ref} />);
290
339
 
291
340
  await act(async () => {
292
- ref.current.quickEditAction?.kind === 'group' &&
293
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
341
+ await ref.current.quickEditToggleAction?.onClick?.();
294
342
  ref.current.changeSelectedItem({ id: 1 });
295
343
  });
296
344
 
@@ -317,8 +365,7 @@ describe('useQuickEdit', () => {
317
365
  render(<TestWrapper quickEditValuesRef={ref} />);
318
366
 
319
367
  await act(async () => {
320
- ref.current.quickEditAction?.kind === 'group' &&
321
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
368
+ await ref.current.quickEditToggleAction?.onClick?.();
322
369
  ref.current.changeSelectedItem({ id: 1 });
323
370
  });
324
371
 
@@ -348,8 +395,7 @@ describe('useQuickEdit', () => {
348
395
  render(<TestWrapper quickEditValuesRef={ref} />);
349
396
 
350
397
  await act(async () => {
351
- ref.current.quickEditAction?.kind === 'group' &&
352
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
398
+ await ref.current.quickEditToggleAction?.onClick?.();
353
399
 
354
400
  ref.current.changeSelectedItem({ id: 1 });
355
401
  });
@@ -376,8 +422,7 @@ describe('useQuickEdit', () => {
376
422
  render(<TestWrapper quickEditValuesRef={ref} />);
377
423
 
378
424
  await act(async () => {
379
- ref.current.quickEditAction?.kind === 'group' &&
380
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
425
+ await ref.current.quickEditToggleAction?.onClick?.();
381
426
 
382
427
  ref.current.changeSelectedItem({ id: 1 });
383
428
  });
@@ -402,8 +447,7 @@ describe('useQuickEdit', () => {
402
447
  render(<TestWrapper quickEditValuesRef={ref} />);
403
448
 
404
449
  await act(async () => {
405
- ref.current.quickEditAction?.kind === 'group' &&
406
- (await ref.current.quickEditAction?.onActionsGroupToggled?.(true));
450
+ await ref.current.quickEditToggleAction?.onClick?.();
407
451
 
408
452
  ref.current.changeSelectedItem({ id: 1 });
409
453
  });