@axinom/mosaic-ui 0.35.0-rc.9 → 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/Buttons/Button/Button.d.ts.map +1 -1
- package/dist/components/Buttons/Button/Button.model.d.ts +2 -2
- package/dist/components/Buttons/Button/Button.model.d.ts.map +1 -1
- package/dist/components/Buttons/Button.model.d.ts +5 -0
- package/dist/components/Buttons/Button.model.d.ts.map +1 -1
- package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts.map +1 -1
- 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/Buttons/Button/Button.model.ts +2 -2
- package/src/components/Buttons/Button/Button.spec.tsx +56 -0
- package/src/components/Buttons/Button/Button.tsx +6 -2
- package/src/components/Buttons/Button.model.ts +5 -0
- package/src/components/Buttons/CompositeButton/CompositeButton.spec.tsx +56 -1
- package/src/components/Buttons/CompositeButton/CompositeButton.tsx +9 -2
- 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/components/FormStation/FormStation.scss +4 -0
- package/src/components/FormStation/FormStation.tsx +2 -2
- 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
|
}
|
|
@@ -20,11 +20,11 @@ export type ButtonProps =
|
|
|
20
20
|
*/
|
|
21
21
|
export interface NavigationButtonProps
|
|
22
22
|
extends CommonNavigationButtonOptions,
|
|
23
|
-
Pick<ButtonIconOptions, 'icon'> {}
|
|
23
|
+
Pick<ButtonIconOptions, 'icon' | 'customIcon'> {}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Button options for icon buttons with JS handlers
|
|
27
27
|
*/
|
|
28
28
|
export interface ContextButtonProps
|
|
29
29
|
extends CommonJsButtonOptions,
|
|
30
|
-
Pick<ButtonIconOptions, 'icon'> {}
|
|
30
|
+
Pick<ButtonIconOptions, 'icon' | 'customIcon'> {}
|
|
@@ -9,6 +9,14 @@ const navigationButtonProps = {
|
|
|
9
9
|
path: '/test',
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
const CustomIcon: React.FC = () => (
|
|
13
|
+
<svg
|
|
14
|
+
version="1.1"
|
|
15
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
16
|
+
viewBox="0 0 40 40"
|
|
17
|
+
></svg>
|
|
18
|
+
);
|
|
19
|
+
|
|
12
20
|
describe('Button (with icon)', () => {
|
|
13
21
|
describe('ContextButtonElement', () => {
|
|
14
22
|
it('renders the component without crashing', () => {
|
|
@@ -139,6 +147,25 @@ describe('Button (with icon)', () => {
|
|
|
139
147
|
|
|
140
148
|
expect(icon.exists()).toBe(true);
|
|
141
149
|
});
|
|
150
|
+
|
|
151
|
+
it('renders `customIcon` when provided', () => {
|
|
152
|
+
const wrapper = mount(<Button customIcon={<CustomIcon />} />);
|
|
153
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
154
|
+
|
|
155
|
+
expect(customIcon.exists()).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('renders `customIcon` instead of `icon` when both are provided', () => {
|
|
159
|
+
const wrapper = mount(
|
|
160
|
+
<Button icon={IconName.ChevronRight} customIcon={<CustomIcon />} />,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
164
|
+
const icon = wrapper.find(Icons);
|
|
165
|
+
|
|
166
|
+
expect(customIcon.exists()).toBe(true);
|
|
167
|
+
expect(icon.exists()).toBe(false);
|
|
168
|
+
});
|
|
142
169
|
});
|
|
143
170
|
|
|
144
171
|
describe('NavigationButtonElement', () => {
|
|
@@ -297,5 +324,34 @@ describe('Button (with icon)', () => {
|
|
|
297
324
|
|
|
298
325
|
expect(icons.prop('icon')).toBe(mockIcon);
|
|
299
326
|
});
|
|
327
|
+
|
|
328
|
+
it('renders `customIcon` when provided', () => {
|
|
329
|
+
const wrapper = mount(
|
|
330
|
+
<Router>
|
|
331
|
+
<Button {...navigationButtonProps} customIcon={<CustomIcon />} />
|
|
332
|
+
</Router>,
|
|
333
|
+
);
|
|
334
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
335
|
+
|
|
336
|
+
expect(customIcon.exists()).toBe(true);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('renders `customIcon` instead of `icon` when both are provided', () => {
|
|
340
|
+
const wrapper = mount(
|
|
341
|
+
<Router>
|
|
342
|
+
<Button
|
|
343
|
+
{...navigationButtonProps}
|
|
344
|
+
icon={IconName.ChevronRight}
|
|
345
|
+
customIcon={<CustomIcon />}
|
|
346
|
+
/>
|
|
347
|
+
</Router>,
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
351
|
+
const icon = wrapper.find(Icons);
|
|
352
|
+
|
|
353
|
+
expect(customIcon.exists()).toBe(true);
|
|
354
|
+
expect(icon.exists()).toBe(false);
|
|
355
|
+
});
|
|
300
356
|
});
|
|
301
357
|
});
|
|
@@ -17,6 +17,7 @@ const ButtonElement: React.ForwardRefRenderFunction<
|
|
|
17
17
|
> = (
|
|
18
18
|
{
|
|
19
19
|
className,
|
|
20
|
+
customIcon,
|
|
20
21
|
dataTestId,
|
|
21
22
|
disabled = false,
|
|
22
23
|
height,
|
|
@@ -30,6 +31,7 @@ const ButtonElement: React.ForwardRefRenderFunction<
|
|
|
30
31
|
) => {
|
|
31
32
|
const commonProps = {
|
|
32
33
|
className,
|
|
34
|
+
customIcon,
|
|
33
35
|
dataTestId,
|
|
34
36
|
disabled,
|
|
35
37
|
height,
|
|
@@ -65,6 +67,7 @@ const ContextButtonElement = React.forwardRef<
|
|
|
65
67
|
disabled = false,
|
|
66
68
|
icon,
|
|
67
69
|
className = '',
|
|
70
|
+
customIcon,
|
|
68
71
|
dataTestId,
|
|
69
72
|
onButtonClicked = noop,
|
|
70
73
|
onBlur,
|
|
@@ -91,7 +94,7 @@ const ContextButtonElement = React.forwardRef<
|
|
|
91
94
|
onBlur={onBlur}
|
|
92
95
|
data-test-id={dataTestId ?? 'button'}
|
|
93
96
|
>
|
|
94
|
-
<Icons icon={icon} />
|
|
97
|
+
{customIcon ? customIcon : <Icons icon={icon} />}
|
|
95
98
|
</button>
|
|
96
99
|
);
|
|
97
100
|
});
|
|
@@ -105,6 +108,7 @@ const NavigationButtonElement = React.forwardRef<
|
|
|
105
108
|
height,
|
|
106
109
|
width,
|
|
107
110
|
icon,
|
|
111
|
+
customIcon,
|
|
108
112
|
className = '',
|
|
109
113
|
dataTestId = undefined,
|
|
110
114
|
disabled = false,
|
|
@@ -129,7 +133,7 @@ const NavigationButtonElement = React.forwardRef<
|
|
|
129
133
|
data-test-id={dataTestId ?? 'button'}
|
|
130
134
|
target={openInNewTab ? '_blank' : undefined}
|
|
131
135
|
>
|
|
132
|
-
<Icons icon={icon ? icon : defaultIcon} />
|
|
136
|
+
{customIcon ? customIcon : <Icons icon={icon ? icon : defaultIcon} />}
|
|
133
137
|
</Link>
|
|
134
138
|
);
|
|
135
139
|
});
|
|
@@ -66,6 +66,11 @@ export interface ButtonIconOptions {
|
|
|
66
66
|
icon?: IconName;
|
|
67
67
|
/** Optional Icon Position */
|
|
68
68
|
iconPosition?: 'left' | 'right';
|
|
69
|
+
/**
|
|
70
|
+
* Optional custom icon.
|
|
71
|
+
* When provided, `icon` property is ignored.
|
|
72
|
+
*/
|
|
73
|
+
customIcon?: React.ReactNode;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
/**
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { mount, shallow } from 'enzyme';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { BrowserRouter as Router } from 'react-router-dom';
|
|
4
|
-
import { IconName } from '../../Icons';
|
|
4
|
+
import { IconName, Icons } from '../../Icons';
|
|
5
5
|
import { ButtonContext } from '../Button.model';
|
|
6
6
|
import { CompositeButton } from './CompositeButton';
|
|
7
7
|
|
|
8
|
+
const CustomIcon: React.FC = () => (
|
|
9
|
+
<svg
|
|
10
|
+
version="1.1"
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
viewBox="0 0 40 40"
|
|
13
|
+
></svg>
|
|
14
|
+
);
|
|
15
|
+
|
|
8
16
|
describe('CompositeButton', () => {
|
|
9
17
|
describe('Composite context button', () => {
|
|
10
18
|
it('renders the component without crashing', () => {
|
|
@@ -143,6 +151,26 @@ describe('CompositeButton', () => {
|
|
|
143
151
|
|
|
144
152
|
expect(button.text()).toBe('');
|
|
145
153
|
});
|
|
154
|
+
|
|
155
|
+
it('renders `customIcon` when provided', () => {
|
|
156
|
+
const wrapper = mount(<CompositeButton customIcon={<CustomIcon />} />);
|
|
157
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
158
|
+
|
|
159
|
+
expect(customIcon.exists()).toBeTruthy();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('renders `customIcon` instead of `icon` when both provided', () => {
|
|
163
|
+
const wrapper = mount(
|
|
164
|
+
<CompositeButton
|
|
165
|
+
customIcon={<CustomIcon />}
|
|
166
|
+
icon={IconName.ChevronRight}
|
|
167
|
+
/>,
|
|
168
|
+
);
|
|
169
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
170
|
+
|
|
171
|
+
expect(customIcon.exists()).toBeTruthy();
|
|
172
|
+
expect(wrapper.find(Icons).exists()).toBeFalsy();
|
|
173
|
+
});
|
|
146
174
|
});
|
|
147
175
|
|
|
148
176
|
describe('Composite navigation button', () => {
|
|
@@ -298,6 +326,33 @@ describe('CompositeButton', () => {
|
|
|
298
326
|
expect(iconDiv.find('Icons').prop('icon')).toBe(IconName.External);
|
|
299
327
|
});
|
|
300
328
|
|
|
329
|
+
it('renders `customIcon` when provided', () => {
|
|
330
|
+
const wrapper = mount(
|
|
331
|
+
<Router>
|
|
332
|
+
<CompositeButton customIcon={<CustomIcon />} path="/example" />
|
|
333
|
+
</Router>,
|
|
334
|
+
);
|
|
335
|
+
const customIcon = wrapper.find(CustomIcon);
|
|
336
|
+
|
|
337
|
+
expect(customIcon.exists()).toBeTruthy();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('renders `customIcon` instead of `icon` when both provided', () => {
|
|
341
|
+
const wrapper = mount(
|
|
342
|
+
<Router>
|
|
343
|
+
<CompositeButton
|
|
344
|
+
customIcon={<CustomIcon />}
|
|
345
|
+
icon={IconName.ChevronRight}
|
|
346
|
+
path="/example"
|
|
347
|
+
/>
|
|
348
|
+
</Router>,
|
|
349
|
+
);
|
|
350
|
+
const iconDiv = wrapper.find('div').at(1);
|
|
351
|
+
|
|
352
|
+
expect(iconDiv.find('CustomIcon').exists()).toBeTruthy();
|
|
353
|
+
expect(iconDiv.find('Icons').exists()).toBeFalsy();
|
|
354
|
+
});
|
|
355
|
+
|
|
301
356
|
it('renders the default data-test-id when dataTestId prop is not provided', () => {
|
|
302
357
|
const wrapper = mount(
|
|
303
358
|
<Router>
|
|
@@ -22,6 +22,7 @@ import classes from './CompositeButton.scss';
|
|
|
22
22
|
*/
|
|
23
23
|
export const CompositeButton: React.FC<CompositeButtonProps> = ({
|
|
24
24
|
className,
|
|
25
|
+
customIcon,
|
|
25
26
|
dataTestId,
|
|
26
27
|
disabled,
|
|
27
28
|
height,
|
|
@@ -35,6 +36,7 @@ export const CompositeButton: React.FC<CompositeButtonProps> = ({
|
|
|
35
36
|
}) => {
|
|
36
37
|
const commonProps = {
|
|
37
38
|
className,
|
|
39
|
+
customIcon,
|
|
38
40
|
dataTestId,
|
|
39
41
|
disabled,
|
|
40
42
|
height,
|
|
@@ -57,6 +59,7 @@ export const CompositeButton: React.FC<CompositeButtonProps> = ({
|
|
|
57
59
|
const CompositeContextButton: React.FC<CompositeJsButtonProps> = ({
|
|
58
60
|
buttonContext = ButtonContext.Context,
|
|
59
61
|
className,
|
|
62
|
+
customIcon,
|
|
60
63
|
dataTestId = undefined,
|
|
61
64
|
disabled = false,
|
|
62
65
|
height,
|
|
@@ -90,7 +93,10 @@ const CompositeContextButton: React.FC<CompositeJsButtonProps> = ({
|
|
|
90
93
|
>
|
|
91
94
|
<div>{text}</div>
|
|
92
95
|
<div className={classes.icon}>
|
|
93
|
-
{
|
|
96
|
+
{customIcon ||
|
|
97
|
+
(icon !== null && icon !== undefined ? (
|
|
98
|
+
<Icons icon={icon} />
|
|
99
|
+
) : undefined)}
|
|
94
100
|
</div>
|
|
95
101
|
</button>
|
|
96
102
|
);
|
|
@@ -98,6 +104,7 @@ const CompositeContextButton: React.FC<CompositeJsButtonProps> = ({
|
|
|
98
104
|
|
|
99
105
|
const CompositeNavigationButton: React.FC<CompositeNavigationButtonProps> = ({
|
|
100
106
|
className,
|
|
107
|
+
customIcon,
|
|
101
108
|
dataTestId = undefined,
|
|
102
109
|
disabled = false,
|
|
103
110
|
height,
|
|
@@ -129,7 +136,7 @@ const CompositeNavigationButton: React.FC<CompositeNavigationButtonProps> = ({
|
|
|
129
136
|
>
|
|
130
137
|
<div>{text}</div>
|
|
131
138
|
<div className={classes.icon}>
|
|
132
|
-
<Icons icon={icon ? icon : defaultIcon} />
|
|
139
|
+
{customIcon ? customIcon : <Icons icon={icon ? icon : defaultIcon} />}
|
|
133
140
|
</div>
|
|
134
141
|
</Link>
|
|
135
142
|
);
|
|
@@ -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) => (
|