@axinom/mosaic-ui 0.35.0 → 0.36.0-rc.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/components/DynamicDataList/DynamicDataList.d.ts +3 -1
- package/dist/components/DynamicDataList/DynamicDataList.d.ts.map +1 -1
- package/dist/components/DynamicDataList/DynamicDataList.model.d.ts +6 -1
- package/dist/components/DynamicDataList/DynamicDataList.model.d.ts.map +1 -1
- package/dist/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.d.ts +7 -3
- package/dist/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.d.ts.map +1 -1
- package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts +4 -1
- package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts.map +1 -1
- package/dist/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.d.ts.map +1 -1
- package/dist/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.types.d.ts +7 -1
- package/dist/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.types.d.ts.map +1 -1
- package/dist/components/DynamicDataList/helpers/useDataHandler.d.ts +1 -0
- package/dist/components/DynamicDataList/helpers/useDataHandler.d.ts.map +1 -1
- package/dist/components/DynamicDataList/helpers/useRowEditing.d.ts +8 -0
- package/dist/components/DynamicDataList/helpers/useRowEditing.d.ts.map +1 -0
- package/dist/index.es.js +4 -4
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/DynamicDataList/DynamicDataList.model.ts +7 -2
- package/src/components/DynamicDataList/DynamicDataList.spec.tsx +5 -0
- package/src/components/DynamicDataList/DynamicDataList.stories.tsx +37 -0
- package/src/components/DynamicDataList/DynamicDataList.tsx +58 -28
- package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.scss +0 -1
- package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.spec.tsx +2 -44
- package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.tsx +36 -28
- package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.scss +12 -0
- package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.tsx +23 -12
- package/src/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.spec.ts +26 -1
- package/src/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.ts +8 -0
- package/src/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.types.ts +8 -0
- package/src/components/DynamicDataList/helpers/useDataHandler.ts +11 -1
- package/src/components/DynamicDataList/helpers/useRowEditing.ts +30 -0
- package/src/styles/variables.scss +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0-rc.0",
|
|
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.
|
|
35
|
+
"@axinom/mosaic-core": "^0.4.9-rc.0",
|
|
36
36
|
"@faker-js/faker": "^7.4.0",
|
|
37
37
|
"@popperjs/core": "^2.9.2",
|
|
38
38
|
"clsx": "^1.1.0",
|
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"publishConfig": {
|
|
105
105
|
"access": "public"
|
|
106
106
|
},
|
|
107
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "b232d67a1f8a7b0b696fb1e962cf353e09445af0"
|
|
108
108
|
}
|
|
@@ -25,12 +25,17 @@ export interface DynamicListColumn<T extends Data> {
|
|
|
25
25
|
*/
|
|
26
26
|
dataEntryRender?: DynamicListDataEntryRenderer;
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Set if data editing is allowed
|
|
30
|
+
* Setting fieldType to either 'input' or 'select' will generate an element appropriate to that type.
|
|
31
|
+
* If not set, will use the dataEntryRender value.
|
|
32
|
+
*/
|
|
33
|
+
dataEditRender?: DynamicListDataEntryRenderer;
|
|
34
|
+
|
|
28
35
|
/** Performs a transformation on the value before being added to the list */
|
|
29
36
|
onAddTransformer?: (value: unknown, data: T) => unknown;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
export type DynamicListElevationOptions = 'above' | 'below';
|
|
33
|
-
|
|
34
39
|
export type DynamicListDataEntryRenderer = (
|
|
35
40
|
/** Value to render. */
|
|
36
41
|
currentValue: unknown,
|
|
@@ -84,6 +84,7 @@ describe('DynamicDataList', () => {
|
|
|
84
84
|
mockUseDataHandler.mockReturnValue({
|
|
85
85
|
removeItem: jest.fn(),
|
|
86
86
|
addItem: jest.fn(),
|
|
87
|
+
updateItem: jest.fn(),
|
|
87
88
|
canAddItems: false,
|
|
88
89
|
items: defaultData,
|
|
89
90
|
nextPosition: 2,
|
|
@@ -302,6 +303,7 @@ describe('DynamicDataList', () => {
|
|
|
302
303
|
mockUseDataHandler.mockReturnValue({
|
|
303
304
|
removeItem: jest.fn(),
|
|
304
305
|
addItem: jest.fn(),
|
|
306
|
+
updateItem: jest.fn(),
|
|
305
307
|
canAddItems: true,
|
|
306
308
|
items: defaultData,
|
|
307
309
|
nextPosition: 2,
|
|
@@ -322,6 +324,7 @@ describe('DynamicDataList', () => {
|
|
|
322
324
|
mockUseDataHandler.mockReturnValue({
|
|
323
325
|
removeItem: jest.fn(),
|
|
324
326
|
addItem: jest.fn(),
|
|
327
|
+
updateItem: jest.fn(),
|
|
325
328
|
canAddItems: true,
|
|
326
329
|
items: defaultData,
|
|
327
330
|
nextPosition: mockLimit,
|
|
@@ -340,6 +343,7 @@ describe('DynamicDataList', () => {
|
|
|
340
343
|
mockUseDataHandler.mockReturnValue({
|
|
341
344
|
removeItem: jest.fn(),
|
|
342
345
|
addItem: addItemSpy,
|
|
346
|
+
updateItem: jest.fn(),
|
|
343
347
|
canAddItems: true,
|
|
344
348
|
items: defaultData,
|
|
345
349
|
nextPosition: 2,
|
|
@@ -369,6 +373,7 @@ describe('DynamicDataList', () => {
|
|
|
369
373
|
mockUseDataHandler.mockReturnValue({
|
|
370
374
|
removeItem: removeItemSpy,
|
|
371
375
|
addItem: jest.fn(),
|
|
376
|
+
updateItem: jest.fn(),
|
|
372
377
|
canAddItems: true,
|
|
373
378
|
items: defaultData,
|
|
374
379
|
nextPosition: 2,
|
|
@@ -66,6 +66,7 @@ const groups = createGroups({
|
|
|
66
66
|
Behavior: [
|
|
67
67
|
'allowRowDragging',
|
|
68
68
|
'allowNewData',
|
|
69
|
+
'allowEditing',
|
|
69
70
|
'showHeader',
|
|
70
71
|
'allowAddAndRemove',
|
|
71
72
|
'allowReordering',
|
|
@@ -373,3 +374,39 @@ export const WithInlineMenu: StoryObj<StoryDDLType> = {
|
|
|
373
374
|
],
|
|
374
375
|
},
|
|
375
376
|
};
|
|
377
|
+
|
|
378
|
+
export const Editable: StoryObj<StoryDDLType> = {
|
|
379
|
+
name: 'Editable',
|
|
380
|
+
args: {
|
|
381
|
+
...defaultProps,
|
|
382
|
+
columns: [
|
|
383
|
+
{
|
|
384
|
+
propertyName: 'id',
|
|
385
|
+
label: 'Id',
|
|
386
|
+
size: '50px',
|
|
387
|
+
dataEntryRender: createInputRenderer({
|
|
388
|
+
placeholder: 'Enter Id',
|
|
389
|
+
}),
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
propertyName: 'title',
|
|
393
|
+
label: 'Title',
|
|
394
|
+
dataEntryRender: createInputRenderer({
|
|
395
|
+
placeholder: 'Enter Title',
|
|
396
|
+
}),
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
propertyName: 'desc',
|
|
400
|
+
label: 'Description',
|
|
401
|
+
dataEntryRender: createInputRenderer(),
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
allowNewData: true,
|
|
405
|
+
allowEditing: true,
|
|
406
|
+
defaultValuesForNewData: { desc: 'Description' },
|
|
407
|
+
rowValidationSchema: Yup.object({
|
|
408
|
+
id: Yup.number().required(),
|
|
409
|
+
title: Yup.string().required().max(25).label('Title'),
|
|
410
|
+
}),
|
|
411
|
+
},
|
|
412
|
+
};
|
|
@@ -16,6 +16,7 @@ import { DynamicListColumn } from './DynamicDataList.model';
|
|
|
16
16
|
import classes from './DynamicDataList.scss';
|
|
17
17
|
import {
|
|
18
18
|
DynamicListDataEntry,
|
|
19
|
+
DynamicListDataEntryMode,
|
|
19
20
|
DynamicListDataEntryProps,
|
|
20
21
|
} from './DynamicListDataEntry/DynamicListDataEntry';
|
|
21
22
|
import { DynamicListHeader } from './DynamicListHeader/DynamicListHeader';
|
|
@@ -24,6 +25,7 @@ import { uuid } from './helpers/generateId';
|
|
|
24
25
|
import { useColumnDefs } from './helpers/useColumnDefs';
|
|
25
26
|
import { useDataHandler } from './helpers/useDataHandler';
|
|
26
27
|
import { useRowAnimation } from './helpers/useRowAnimation';
|
|
28
|
+
import { useRowEditing } from './helpers/useRowEditing';
|
|
27
29
|
|
|
28
30
|
export interface DynamicDataListProps<T extends Data> {
|
|
29
31
|
/**
|
|
@@ -63,6 +65,8 @@ export interface DynamicDataListProps<T extends Data> {
|
|
|
63
65
|
allowRowDragging?: boolean;
|
|
64
66
|
/** Whether or not new data can be added (default: false) */
|
|
65
67
|
allowNewData?: boolean;
|
|
68
|
+
/** Whether or not existing data can be edited (default: false) */
|
|
69
|
+
allowEditing?: boolean;
|
|
66
70
|
/** Custom DynamicListDataEntry component which will be rendered instead of the default component */
|
|
67
71
|
customDataEntry?: React.FC<DynamicListDataEntryProps<T>>;
|
|
68
72
|
/** Optional Yup validation object for validating new data */
|
|
@@ -115,6 +119,7 @@ export const DynamicDataList = <T extends Data>({
|
|
|
115
119
|
allowReordering = true,
|
|
116
120
|
allowRowDragging = true,
|
|
117
121
|
allowNewData = false,
|
|
122
|
+
allowEditing = false,
|
|
118
123
|
customDataEntry: CustomDataEntry,
|
|
119
124
|
maxItemLimit,
|
|
120
125
|
rowValidationSchema,
|
|
@@ -137,7 +142,7 @@ export const DynamicDataList = <T extends Data>({
|
|
|
137
142
|
!!inlineMenuActions,
|
|
138
143
|
);
|
|
139
144
|
|
|
140
|
-
const { addItem, removeItem, canAddItems, items, nextPosition } =
|
|
145
|
+
const { addItem, removeItem, updateItem, canAddItems, items, nextPosition } =
|
|
141
146
|
useDataHandler(
|
|
142
147
|
value,
|
|
143
148
|
positionPropertyName,
|
|
@@ -147,6 +152,11 @@ export const DynamicDataList = <T extends Data>({
|
|
|
147
152
|
onAddTransformData,
|
|
148
153
|
);
|
|
149
154
|
|
|
155
|
+
const { editingRow, isEditing, startEditing, finishEditing } = useRowEditing(
|
|
156
|
+
allowEditing,
|
|
157
|
+
updateItem,
|
|
158
|
+
);
|
|
159
|
+
|
|
150
160
|
const customStyles = {
|
|
151
161
|
gridRowGap: rowGap,
|
|
152
162
|
minWidth: minimumWidth,
|
|
@@ -204,33 +214,54 @@ export const DynamicDataList = <T extends Data>({
|
|
|
204
214
|
idx: number,
|
|
205
215
|
provided?: DraggableProvided,
|
|
206
216
|
isDragging = false,
|
|
207
|
-
): JSX.Element =>
|
|
208
|
-
|
|
209
|
-
<
|
|
217
|
+
): JSX.Element =>
|
|
218
|
+
editingRow !== data ? (
|
|
219
|
+
<AnimatedRow data={data} key={idx}>
|
|
220
|
+
<DynamicListRow<T>
|
|
221
|
+
key={idx}
|
|
222
|
+
columns={columns}
|
|
223
|
+
data={data}
|
|
224
|
+
columnSizes={columnSizes}
|
|
225
|
+
columnGap={columnGap}
|
|
226
|
+
rowHeight={listRowHeight}
|
|
227
|
+
actionSize={listRowActionSize}
|
|
228
|
+
horizontalTextAlign={horizontalTextAlign}
|
|
229
|
+
verticalTextAlign={verticalTextAlign}
|
|
230
|
+
allowRemove={allowNewData}
|
|
231
|
+
positionKey={positionPropertyName}
|
|
232
|
+
allowDragging={allowRowDragging}
|
|
233
|
+
onActionClicked={removeItem}
|
|
234
|
+
onPositionInputChanged={onPositionInputChangedHandler}
|
|
235
|
+
inlineMenuActions={inlineMenuActions}
|
|
236
|
+
rowClassNameProvider={rowClassNameProvider}
|
|
237
|
+
disabled={disabled}
|
|
238
|
+
provided={provided}
|
|
239
|
+
dragging={isDragging}
|
|
240
|
+
showActionColumn={showActionColumn}
|
|
241
|
+
showPositionColumn={showPositionColumn}
|
|
242
|
+
allowEditing={allowEditing}
|
|
243
|
+
onRowClicked={startEditing}
|
|
244
|
+
/>
|
|
245
|
+
</AnimatedRow>
|
|
246
|
+
) : CustomDataEntry ? (
|
|
247
|
+
<CustomDataEntry
|
|
210
248
|
key={idx}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
actionSize={listRowActionSize}
|
|
217
|
-
horizontalTextAlign={horizontalTextAlign}
|
|
218
|
-
verticalTextAlign={verticalTextAlign}
|
|
219
|
-
allowRemove={allowNewData}
|
|
220
|
-
positionKey={positionPropertyName}
|
|
221
|
-
allowDragging={allowRowDragging}
|
|
222
|
-
onActionClicked={(data) => removeItem(data)}
|
|
223
|
-
onPositionInputChanged={onPositionInputChangedHandler}
|
|
224
|
-
inlineMenuActions={inlineMenuActions}
|
|
225
|
-
rowClassNameProvider={rowClassNameProvider}
|
|
226
|
-
disabled={disabled}
|
|
227
|
-
provided={provided}
|
|
228
|
-
dragging={isDragging}
|
|
229
|
-
showActionColumn={showActionColumn}
|
|
230
|
-
showPositionColumn={showPositionColumn}
|
|
249
|
+
{...DynamicListDataEntryProps}
|
|
250
|
+
defaultValuesForNewData={data}
|
|
251
|
+
newDataPosition={undefined}
|
|
252
|
+
onActionClicked={finishEditing}
|
|
253
|
+
mode={DynamicListDataEntryMode.Edit}
|
|
231
254
|
/>
|
|
232
|
-
|
|
233
|
-
|
|
255
|
+
) : (
|
|
256
|
+
<DynamicListDataEntry<T>
|
|
257
|
+
key={idx}
|
|
258
|
+
{...DynamicListDataEntryProps}
|
|
259
|
+
defaultValuesForNewData={data}
|
|
260
|
+
newDataPosition={undefined}
|
|
261
|
+
onActionClicked={finishEditing}
|
|
262
|
+
mode={DynamicListDataEntryMode.Edit}
|
|
263
|
+
/>
|
|
264
|
+
);
|
|
234
265
|
|
|
235
266
|
const DynamicListDataEntryProps: DynamicListDataEntryProps<T> = {
|
|
236
267
|
columns,
|
|
@@ -241,7 +272,6 @@ export const DynamicDataList = <T extends Data>({
|
|
|
241
272
|
actionSize: listRowActionSize,
|
|
242
273
|
horizontalTextAlign,
|
|
243
274
|
verticalTextAlign,
|
|
244
|
-
allowAdd: allowNewData,
|
|
245
275
|
allowReordering,
|
|
246
276
|
allowDragging: allowRowDragging,
|
|
247
277
|
newDataPosition: nextPosition,
|
|
@@ -287,7 +317,7 @@ export const DynamicDataList = <T extends Data>({
|
|
|
287
317
|
) : (
|
|
288
318
|
<DynamicListDataEntry<T> {...DynamicListDataEntryProps} />
|
|
289
319
|
))}
|
|
290
|
-
{allowReordering ? (
|
|
320
|
+
{allowReordering && !isEditing ? (
|
|
291
321
|
<DragDropContext onDragEnd={onDragEndHandler}>
|
|
292
322
|
<Droppable droppableId={uuid()}>
|
|
293
323
|
{(provided) => (
|
|
@@ -45,7 +45,6 @@ const defaultProps: DynamicListDataEntryProps<TestData> = {
|
|
|
45
45
|
verticalTextAlign: 'center',
|
|
46
46
|
positionKey: 'position',
|
|
47
47
|
allowReordering: false,
|
|
48
|
-
allowAdd: false,
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
const MockRenderer: DynamicListDataEntryRenderer = (
|
|
@@ -139,9 +138,7 @@ describe('DynamicListDataEntry', () => {
|
|
|
139
138
|
});
|
|
140
139
|
|
|
141
140
|
it(`Button component should have type of 'button'`, () => {
|
|
142
|
-
const wrapper = mount(
|
|
143
|
-
<DynamicListDataEntry {...defaultProps} allowAdd={true} />,
|
|
144
|
-
);
|
|
141
|
+
const wrapper = mount(<DynamicListDataEntry {...defaultProps} />);
|
|
145
142
|
|
|
146
143
|
const button = wrapper.find(Button);
|
|
147
144
|
|
|
@@ -220,40 +217,9 @@ describe('DynamicListDataEntry', () => {
|
|
|
220
217
|
expect(dragIcon.exists()).toBe(false);
|
|
221
218
|
});
|
|
222
219
|
|
|
223
|
-
it(`doesn't display an action button by default`, () => {
|
|
224
|
-
const wrapper = shallow(
|
|
225
|
-
<DynamicListDataEntry
|
|
226
|
-
columns={defaultColumns}
|
|
227
|
-
columnSizes={defaultProps.columnSizes}
|
|
228
|
-
/>,
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
const button = wrapper.find(Button);
|
|
232
|
-
|
|
233
|
-
expect(button.exists()).toBe(false);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it(`displays an action button if 'allowAdd' is set to true`, () => {
|
|
237
|
-
const wrapper = shallow(
|
|
238
|
-
<DynamicListDataEntry
|
|
239
|
-
columns={defaultColumns}
|
|
240
|
-
columnSizes={defaultProps.columnSizes}
|
|
241
|
-
allowAdd={true}
|
|
242
|
-
/>,
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
const button = wrapper.find(Button);
|
|
246
|
-
|
|
247
|
-
expect(button.exists()).toBe(true);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
220
|
it(`renders "disabled" attribute for action button when set`, () => {
|
|
251
221
|
const wrapper = mount(
|
|
252
|
-
<DynamicListDataEntry
|
|
253
|
-
{...defaultProps}
|
|
254
|
-
allowAdd={true}
|
|
255
|
-
disabled={true}
|
|
256
|
-
/>,
|
|
222
|
+
<DynamicListDataEntry {...defaultProps} disabled={true} />,
|
|
257
223
|
);
|
|
258
224
|
|
|
259
225
|
const button = wrapper.find(Button);
|
|
@@ -307,7 +273,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
307
273
|
<DynamicListDataEntry
|
|
308
274
|
columns={defaultColumns}
|
|
309
275
|
columnSizes={defaultProps.columnSizes}
|
|
310
|
-
allowAdd={true}
|
|
311
276
|
defaultValuesForNewData={defaultData}
|
|
312
277
|
onActionClicked={spy}
|
|
313
278
|
/>,
|
|
@@ -332,7 +297,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
332
297
|
<DynamicListDataEntry
|
|
333
298
|
columns={defaultColumns}
|
|
334
299
|
columnSizes={defaultProps.columnSizes}
|
|
335
|
-
allowAdd={true}
|
|
336
300
|
onActionClicked={spy}
|
|
337
301
|
rowValidationSchema={Yup.object<ObjectSchemaDefinition<TestData>>({
|
|
338
302
|
id: Yup.number().required(),
|
|
@@ -364,7 +328,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
364
328
|
<DynamicListDataEntry
|
|
365
329
|
columns={columnWithRenderer}
|
|
366
330
|
columnSizes={defaultProps.columnSizes}
|
|
367
|
-
allowAdd={true}
|
|
368
331
|
onActionClicked={spy}
|
|
369
332
|
/>,
|
|
370
333
|
);
|
|
@@ -401,7 +364,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
401
364
|
<DynamicListDataEntry
|
|
402
365
|
columns={[...columnWithRenderer, { propertyName: mockPosKey }]}
|
|
403
366
|
columnSizes={defaultProps.columnSizes}
|
|
404
|
-
allowAdd={true}
|
|
405
367
|
onActionClicked={spy}
|
|
406
368
|
positionKey={mockPosKey}
|
|
407
369
|
allowReordering={true}
|
|
@@ -445,7 +407,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
445
407
|
<DynamicListDataEntry
|
|
446
408
|
columns={[...columnWithRenderer, { propertyName: mockPosKey }]}
|
|
447
409
|
columnSizes={defaultProps.columnSizes}
|
|
448
|
-
allowAdd={true}
|
|
449
410
|
onActionClicked={spy}
|
|
450
411
|
positionKey={mockPosKey}
|
|
451
412
|
allowReordering={false}
|
|
@@ -486,7 +447,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
486
447
|
<DynamicListDataEntry
|
|
487
448
|
columns={[...columnWithRenderer, { propertyName: mockPosKey }]}
|
|
488
449
|
columnSizes={defaultProps.columnSizes}
|
|
489
|
-
allowAdd={true}
|
|
490
450
|
onActionClicked={spy}
|
|
491
451
|
positionKey={mockPosKey}
|
|
492
452
|
allowReordering={true}
|
|
@@ -529,7 +489,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
529
489
|
<DynamicListDataEntry
|
|
530
490
|
columns={[...columnWithRenderer, { propertyName: mockPosKey }]}
|
|
531
491
|
columnSizes={defaultProps.columnSizes}
|
|
532
|
-
allowAdd={true}
|
|
533
492
|
onActionClicked={spy}
|
|
534
493
|
allowReordering={true}
|
|
535
494
|
newDataPosition={mockNewPos}
|
|
@@ -578,7 +537,6 @@ describe('DynamicListDataEntry', () => {
|
|
|
578
537
|
<DynamicListDataEntry
|
|
579
538
|
columns={mockColumns}
|
|
580
539
|
columnSizes={defaultProps.columnSizes}
|
|
581
|
-
allowAdd={true}
|
|
582
540
|
onActionClicked={spy}
|
|
583
541
|
/>,
|
|
584
542
|
);
|
|
@@ -15,7 +15,10 @@ import { IconName, Icons } from '../../Icons';
|
|
|
15
15
|
import { DynamicListColumn } from '../DynamicDataList.model';
|
|
16
16
|
import classes from './DynamicListDataEntry.scss';
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
export enum DynamicListDataEntryMode {
|
|
19
|
+
Add,
|
|
20
|
+
Edit,
|
|
21
|
+
}
|
|
19
22
|
|
|
20
23
|
export interface DynamicListDataEntryProps<T extends Data> {
|
|
21
24
|
/** The column definition */
|
|
@@ -34,8 +37,6 @@ export interface DynamicListDataEntryProps<T extends Data> {
|
|
|
34
37
|
verticalTextAlign?: 'start' | 'center' | 'end';
|
|
35
38
|
/** Property name that is used to determine data position (default: undefined) */
|
|
36
39
|
positionKey?: keyof T;
|
|
37
|
-
/** If set to true, the add action button will be rendered (default: undefined) */
|
|
38
|
-
allowAdd?: boolean;
|
|
39
40
|
/** Whether add new item button should be disabled */
|
|
40
41
|
disabled?: boolean;
|
|
41
42
|
/** If set to true, rows can be be repositioned using the input field (default: undefined) */
|
|
@@ -58,6 +59,8 @@ export interface DynamicListDataEntryProps<T extends Data> {
|
|
|
58
59
|
showHeader?: boolean;
|
|
59
60
|
/** If set to true, the position column will be shown (default: false) */
|
|
60
61
|
showPositionColumn?: boolean;
|
|
62
|
+
/** Determines if the component is in Add or Edit mode (default: Add) */
|
|
63
|
+
mode?: DynamicListDataEntryMode;
|
|
61
64
|
/** Emits when the action button is clicked. Data is supplied as a parameter */
|
|
62
65
|
onActionClicked?: (data: T) => void;
|
|
63
66
|
/** override default add button (+) */
|
|
@@ -73,7 +76,6 @@ export const DynamicListDataEntry = <T extends Data>({
|
|
|
73
76
|
horizontalTextAlign,
|
|
74
77
|
verticalTextAlign,
|
|
75
78
|
positionKey,
|
|
76
|
-
allowAdd,
|
|
77
79
|
disabled = false,
|
|
78
80
|
allowReordering,
|
|
79
81
|
allowDragging,
|
|
@@ -85,6 +87,7 @@ export const DynamicListDataEntry = <T extends Data>({
|
|
|
85
87
|
sticky = true,
|
|
86
88
|
showHeader,
|
|
87
89
|
showPositionColumn = false,
|
|
90
|
+
mode = DynamicListDataEntryMode.Add,
|
|
88
91
|
onActionClicked = noop,
|
|
89
92
|
customAddButton,
|
|
90
93
|
}: PropsWithChildren<DynamicListDataEntryProps<T>>): JSX.Element => {
|
|
@@ -221,29 +224,31 @@ export const DynamicListDataEntry = <T extends Data>({
|
|
|
221
224
|
)}
|
|
222
225
|
</div>
|
|
223
226
|
))}
|
|
224
|
-
{
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
227
|
+
<div className={clsx(classes.actionButtonContainer)}>
|
|
228
|
+
{(customAddButton && customAddButton(onAddItemHandler)) || (
|
|
229
|
+
<Button
|
|
230
|
+
type="button"
|
|
231
|
+
className={clsx(classes.actionButton)}
|
|
232
|
+
icon={
|
|
233
|
+
mode === DynamicListDataEntryMode.Add
|
|
234
|
+
? IconName.Plus
|
|
235
|
+
: IconName.Checkmark
|
|
236
|
+
}
|
|
237
|
+
buttonContext={
|
|
238
|
+
actionButtonContext === undefined
|
|
239
|
+
? isDirty
|
|
240
|
+
? ButtonContext.Active
|
|
241
|
+
: ButtonContext.Icon
|
|
242
|
+
: actionButtonContext
|
|
243
|
+
}
|
|
244
|
+
height={actionSize}
|
|
245
|
+
width={actionSize}
|
|
246
|
+
onButtonClicked={onAddItemHandler}
|
|
247
|
+
dataTestId="dynamic-list-add-button"
|
|
248
|
+
disabled={disabled}
|
|
249
|
+
/>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
247
252
|
</div>
|
|
248
253
|
);
|
|
249
254
|
};
|
|
@@ -254,8 +259,11 @@ const renderField = function <T extends Data>(
|
|
|
254
259
|
error: string | undefined,
|
|
255
260
|
onValueChanged: (value: unknown) => void,
|
|
256
261
|
disabled = false,
|
|
262
|
+
mode: DynamicListDataEntryMode = DynamicListDataEntryMode.Add,
|
|
257
263
|
): React.ReactNode {
|
|
258
|
-
if (column.
|
|
264
|
+
if (mode === DynamicListDataEntryMode.Edit && column.dataEditRender) {
|
|
265
|
+
return column.dataEditRender(currentValue, error, onValueChanged, disabled);
|
|
266
|
+
} else if (column.dataEntryRender) {
|
|
259
267
|
return column.dataEntryRender(
|
|
260
268
|
currentValue,
|
|
261
269
|
error,
|
|
@@ -21,6 +21,18 @@
|
|
|
21
21
|
justify-items: left;
|
|
22
22
|
align-items: center;
|
|
23
23
|
|
|
24
|
+
.wrapper {
|
|
25
|
+
min-height: 50px;
|
|
26
|
+
min-width: 100%;
|
|
27
|
+
display: grid;
|
|
28
|
+
align-items: center;
|
|
29
|
+
border: 1px solid transparent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:hover .editable {
|
|
33
|
+
border: 1px solid var(--input-hover-color, $input-hover-color);
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
.column {
|
|
25
37
|
max-width: 100%;
|
|
26
38
|
max-height: 100%;
|
|
@@ -11,8 +11,6 @@ import { InlineMenu } from '../../InlineMenu';
|
|
|
11
11
|
import { DynamicListColumn } from '../DynamicDataList.model';
|
|
12
12
|
import classes from './DynamicListRow.scss';
|
|
13
13
|
|
|
14
|
-
// TODO: Add sizing for DragIcon and Input container. Similar to ActionButton sizing
|
|
15
|
-
|
|
16
14
|
export interface DynamicListRowProps<T extends Data> {
|
|
17
15
|
/** The column definition */
|
|
18
16
|
columns: DynamicListColumn<T>[];
|
|
@@ -32,6 +30,8 @@ export interface DynamicListRowProps<T extends Data> {
|
|
|
32
30
|
verticalTextAlign?: 'start' | 'center' | 'end';
|
|
33
31
|
/** If set to true, the remove action button will be rendered (default: undefined) */
|
|
34
32
|
allowRemove?: boolean;
|
|
33
|
+
/** If set to true, editable fields will be highlighted and row click events will be fired (default: false) */
|
|
34
|
+
allowEditing?: boolean;
|
|
35
35
|
/** Property name that is used to determine data position (default: undefined) */
|
|
36
36
|
positionKey?: keyof T;
|
|
37
37
|
/** If set to true, this component can be dragged (default: undefined) */
|
|
@@ -49,6 +49,7 @@ export interface DynamicListRowProps<T extends Data> {
|
|
|
49
49
|
currenPosition: number,
|
|
50
50
|
newPosition: number,
|
|
51
51
|
) => void;
|
|
52
|
+
onRowClicked?: (data: T) => void;
|
|
52
53
|
/** Emits when the action button is clicked. Data is supplied as a parameter */
|
|
53
54
|
onActionClicked?: (data: T) => void;
|
|
54
55
|
/** CSS Class name for additional styles */
|
|
@@ -75,6 +76,7 @@ export const DynamicListRow = <T extends Data>({
|
|
|
75
76
|
disabled,
|
|
76
77
|
provided,
|
|
77
78
|
onPositionInputChanged = noop,
|
|
79
|
+
onRowClicked = noop,
|
|
78
80
|
onActionClicked = noop,
|
|
79
81
|
className = '',
|
|
80
82
|
rowClassNameProvider,
|
|
@@ -82,6 +84,7 @@ export const DynamicListRow = <T extends Data>({
|
|
|
82
84
|
dragging = false,
|
|
83
85
|
showPositionColumn = false,
|
|
84
86
|
showActionColumn = false,
|
|
87
|
+
allowEditing = false,
|
|
85
88
|
}: PropsWithChildren<DynamicListRowProps<T>>): JSX.Element => {
|
|
86
89
|
const customStyles = {
|
|
87
90
|
gridAutoRows: `minmax(50px, ${rowHeight})`,
|
|
@@ -130,6 +133,7 @@ export const DynamicListRow = <T extends Data>({
|
|
|
130
133
|
ref={provided?.innerRef}
|
|
131
134
|
{...provided?.draggableProps}
|
|
132
135
|
style={customStyles}
|
|
136
|
+
onClick={() => allowEditing && onRowClicked(data)}
|
|
133
137
|
>
|
|
134
138
|
{showPositionColumn && (
|
|
135
139
|
<div className={classes.position}>
|
|
@@ -169,18 +173,25 @@ export const DynamicListRow = <T extends Data>({
|
|
|
169
173
|
|
|
170
174
|
return (
|
|
171
175
|
<div
|
|
176
|
+
className={clsx(classes.wrapper, {
|
|
177
|
+
[classes.editable]:
|
|
178
|
+
column.dataEntryRender !== undefined && allowEditing,
|
|
179
|
+
})}
|
|
172
180
|
key={column.propertyName as string}
|
|
173
|
-
className={classes.column}
|
|
174
|
-
title={
|
|
175
|
-
column.tooltip !== false
|
|
176
|
-
? getTooltipText(data[column.propertyName])
|
|
177
|
-
: undefined
|
|
178
|
-
}
|
|
179
|
-
data-test-id={`dynamic-list-property:${
|
|
180
|
-
column.propertyName as string
|
|
181
|
-
}`}
|
|
182
181
|
>
|
|
183
|
-
|
|
182
|
+
<div
|
|
183
|
+
className={classes.column}
|
|
184
|
+
title={
|
|
185
|
+
column.tooltip !== false
|
|
186
|
+
? getTooltipText(data[column.propertyName])
|
|
187
|
+
: undefined
|
|
188
|
+
}
|
|
189
|
+
data-test-id={`dynamic-list-property:${
|
|
190
|
+
column.propertyName as string
|
|
191
|
+
}`}
|
|
192
|
+
>
|
|
193
|
+
{columnData}
|
|
194
|
+
</div>
|
|
184
195
|
</div>
|
|
185
196
|
);
|
|
186
197
|
})}
|
package/src/components/DynamicDataList/helpers/DynamicListReducer/DynamicListReducer.spec.ts
CHANGED
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
} from './DynamicListReducer.types';
|
|
8
8
|
|
|
9
9
|
jest.mock('./DynamicListReducer.init');
|
|
10
|
-
jest.mock('./DynamicListReducer.actions')
|
|
10
|
+
jest.mock('./DynamicListReducer.actions', () => ({
|
|
11
|
+
addItem: jest.fn(),
|
|
12
|
+
removeItem: jest.fn().mockReturnValue([]),
|
|
13
|
+
}));
|
|
11
14
|
|
|
12
15
|
interface Data {
|
|
13
16
|
position?: number;
|
|
@@ -69,6 +72,28 @@ describe('Dynamic List Reducer', () => {
|
|
|
69
72
|
expect(DynamicListReducerInit).toHaveBeenCalledTimes(1);
|
|
70
73
|
});
|
|
71
74
|
|
|
75
|
+
it('calls removeItem, addItem and DynamicListReducerInit when action is update', () => {
|
|
76
|
+
DynamicListReducer(initialState, {
|
|
77
|
+
type: DynamicListReducerActionType.Update,
|
|
78
|
+
currentItem: { position: 20, id: 20 },
|
|
79
|
+
newItem: { id: 10, position: 11 },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(removeItem).toHaveBeenCalledTimes(1);
|
|
83
|
+
expect(removeItem).toHaveBeenCalledWith(
|
|
84
|
+
{ position: 20, id: 20 },
|
|
85
|
+
dataSet,
|
|
86
|
+
'position',
|
|
87
|
+
);
|
|
88
|
+
expect(addItem).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(addItem).toHaveBeenCalledWith(
|
|
90
|
+
{ id: 10, position: 11 },
|
|
91
|
+
[],
|
|
92
|
+
'position',
|
|
93
|
+
);
|
|
94
|
+
expect(DynamicListReducerInit).toHaveBeenCalledTimes(1);
|
|
95
|
+
});
|
|
96
|
+
|
|
72
97
|
it('calls DynamicListReducerInit with new items when action is reset', () => {
|
|
73
98
|
DynamicListReducer(initialState, {
|
|
74
99
|
type: DynamicListReducerActionType.Reset,
|
|
@@ -22,6 +22,14 @@ export const DynamicListReducer = <T extends Data>(
|
|
|
22
22
|
case DynamicListReducerActionType.Remove:
|
|
23
23
|
newItems = removeItem(action.item, state.items, positionPropertyName);
|
|
24
24
|
break;
|
|
25
|
+
case DynamicListReducerActionType.Update:
|
|
26
|
+
newItems = removeItem(
|
|
27
|
+
action.currentItem,
|
|
28
|
+
state.items,
|
|
29
|
+
positionPropertyName,
|
|
30
|
+
);
|
|
31
|
+
newItems = addItem(action.newItem, newItems, positionPropertyName);
|
|
32
|
+
break;
|
|
25
33
|
case DynamicListReducerActionType.Reset:
|
|
26
34
|
newItems = action.items;
|
|
27
35
|
positionPropertyName = action.positionPropertyName;
|