@axinom/mosaic-ui 0.49.0-rc.1 → 0.49.0-rc.11
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/DynamicListRow/DynamicListRow.d.ts +3 -1
- package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts.map +1 -1
- package/dist/components/Explorer/Explorer.d.ts +2 -0
- package/dist/components/Explorer/Explorer.d.ts.map +1 -1
- package/dist/components/Filters/Filter/Filter.d.ts +2 -1
- package/dist/components/Filters/Filter/Filter.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts.map +1 -1
- package/dist/components/FormStation/FormStationHeader/FormStationHeader.d.ts.map +1 -1
- package/dist/components/List/List.d.ts +2 -0
- package/dist/components/List/List.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRow.d.ts +4 -2
- package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRowLoader.d.ts +1 -1
- package/dist/components/List/ListRow/ListRowLoader.d.ts.map +1 -1
- package/dist/components/PageHeader/PageHeader.d.ts +9 -2
- package/dist/components/PageHeader/PageHeader.d.ts.map +1 -1
- package/dist/components/PageHeader/PageHeader.model.d.ts +11 -12
- package/dist/components/PageHeader/PageHeader.model.d.ts.map +1 -1
- package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts +35 -0
- package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts.map +1 -0
- package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts +7 -0
- package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts.map +1 -0
- package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts +3 -0
- package/dist/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts.map +1 -0
- package/dist/components/PageHeader/index.d.ts +1 -1
- package/dist/components/PageHeader/index.d.ts.map +1 -1
- package/dist/index.es.js +5 -4
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/{components/DynamicDataList/helpers/generateId.d.ts → utils/GenerateId.d.ts} +1 -1
- package/dist/utils/GenerateId.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/components/DynamicDataList/DynamicDataList.spec.tsx +2 -1
- package/src/components/DynamicDataList/DynamicDataList.stories.tsx +2 -1
- package/src/components/DynamicDataList/DynamicDataList.tsx +5 -1
- package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.scss +11 -5
- package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.spec.tsx +37 -0
- package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.tsx +23 -14
- package/src/components/Explorer/Explorer.spec.tsx +26 -16
- package/src/components/Explorer/Explorer.stories.tsx +1 -0
- package/src/components/Explorer/Explorer.tsx +52 -28
- package/src/components/Explorer/NavigationExplorer/NavigationExplorer.spec.tsx +2 -2
- package/src/components/Explorer/SelectionExplorer/SelectionExplorer.spec.tsx +8 -32
- package/src/components/Filters/Filter/Filter.tsx +3 -0
- package/src/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.spec.tsx +16 -1
- package/src/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.tsx +13 -1
- package/src/components/FormStation/FormStationHeader/FormStationHeader.tsx +34 -30
- package/src/components/List/List.stories.tsx +2 -1
- package/src/components/List/List.tsx +5 -1
- package/src/components/List/ListRow/ListRow.scss +11 -4
- package/src/components/List/ListRow/ListRow.spec.tsx +35 -0
- package/src/components/List/ListRow/ListRow.tsx +44 -17
- package/src/components/List/ListRow/ListRowLoader.tsx +2 -2
- package/src/components/PageHeader/PageHeader.model.ts +10 -12
- package/src/components/PageHeader/PageHeader.scss +7 -3
- package/src/components/PageHeader/PageHeader.spec.tsx +28 -86
- package/src/components/PageHeader/PageHeader.stories.tsx +32 -7
- package/src/components/PageHeader/PageHeader.tsx +50 -77
- package/src/components/PageHeader/{PageHeaderBulkActions/PageHeaderBulkActions.scss → PageHeaderActionsGroup/PageHeaderActionsGroup.scss} +21 -21
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.spec.tsx +105 -0
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.tsx +224 -0
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.ts +13 -0
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.tsx +30 -0
- package/src/components/PageHeader/index.ts +1 -1
- package/dist/components/DynamicDataList/helpers/generateId.d.ts.map +0 -1
- package/dist/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.d.ts +0 -22
- package/dist/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.d.ts.map +0 -1
- package/src/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.spec.tsx +0 -369
- package/src/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.tsx +0 -188
- /package/src/{components/DynamicDataList/helpers/generateId.ts → utils/GenerateId.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenerateId.d.ts","sourceRoot":"","sources":["../../src/utils/GenerateId.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,IAAI,QAAO,MAKvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.49.0-rc.
|
|
3
|
+
"version": "0.49.0-rc.11",
|
|
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.22-rc.
|
|
35
|
+
"@axinom/mosaic-core": "^0.4.22-rc.11",
|
|
36
36
|
"@faker-js/faker": "^7.4.0",
|
|
37
37
|
"@popperjs/core": "^2.11.8",
|
|
38
38
|
"clsx": "^1.1.0",
|
|
@@ -105,5 +105,5 @@
|
|
|
105
105
|
"publishConfig": {
|
|
106
106
|
"access": "public"
|
|
107
107
|
},
|
|
108
|
-
"gitHead": "
|
|
108
|
+
"gitHead": "9af527f2f2a4e4442b81530d50f4787ea5b9e4db"
|
|
109
109
|
}
|
|
@@ -9,7 +9,8 @@ import { DynamicListRow } from './DynamicListRow/DynamicListRow';
|
|
|
9
9
|
import { useDataHandler } from './helpers/useDataHandler';
|
|
10
10
|
|
|
11
11
|
jest.mock('./helpers/useDataHandler');
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
jest.mock('../../utils/GenerateId', () => ({
|
|
13
14
|
uuid: jest.fn().mockReturnValue('test-uuid'),
|
|
14
15
|
}));
|
|
15
16
|
|
|
@@ -44,7 +44,7 @@ const generateData = (amount: number): DynamicListStoryData[] =>
|
|
|
44
44
|
position: amount - index, // Position and ID is jumbled for demonstration purposes
|
|
45
45
|
id: index,
|
|
46
46
|
desc: `Description ${index}: ${faker.lorem.words(
|
|
47
|
-
faker.datatype.number({ min: 10, max:
|
|
47
|
+
faker.datatype.number({ min: 10, max: 50 }),
|
|
48
48
|
)}`,
|
|
49
49
|
title: `Item ${index}: ${faker.random.words(
|
|
50
50
|
faker.datatype.number({ min: 1, max: 3 }),
|
|
@@ -87,6 +87,7 @@ const groups = createGroups({
|
|
|
87
87
|
'headerRowActionSize',
|
|
88
88
|
'horizontalTextAlign',
|
|
89
89
|
'verticalTextAlign',
|
|
90
|
+
'textWrap',
|
|
90
91
|
'rowClassNameProvider',
|
|
91
92
|
],
|
|
92
93
|
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { OptionalObjectSchema } from 'yup/lib/object';
|
|
11
11
|
import { noop } from '../../helpers/utils';
|
|
12
12
|
import { Data } from '../../types/data';
|
|
13
|
+
import { uuid } from '../../utils/GenerateId';
|
|
13
14
|
import { ActionData } from '../Actions';
|
|
14
15
|
import { ObjectSchemaDefinition } from '../FormStation';
|
|
15
16
|
import { DynamicListColumn } from './DynamicDataList.model';
|
|
@@ -21,7 +22,6 @@ import {
|
|
|
21
22
|
} from './DynamicListDataEntry/DynamicListDataEntry';
|
|
22
23
|
import { DynamicListHeader } from './DynamicListHeader/DynamicListHeader';
|
|
23
24
|
import { DynamicListRow } from './DynamicListRow/DynamicListRow';
|
|
24
|
-
import { uuid } from './helpers/generateId';
|
|
25
25
|
import { useColumnDefs } from './helpers/useColumnDefs';
|
|
26
26
|
import { useDataHandler } from './helpers/useDataHandler';
|
|
27
27
|
import { useRowAnimation } from './helpers/useRowAnimation';
|
|
@@ -53,6 +53,8 @@ export interface DynamicDataListProps<T extends Data> {
|
|
|
53
53
|
horizontalTextAlign?: 'left' | 'right' | 'center';
|
|
54
54
|
/** Vertical alignment of text */
|
|
55
55
|
verticalTextAlign?: 'start' | 'center' | 'end';
|
|
56
|
+
/** If set to true, column text overflow will be wrapped to a new line. Otherwise, it will be truncated with an ellipsis (default: false) */
|
|
57
|
+
textWrap?: boolean;
|
|
56
58
|
/** Property that contains the value used in reordering the list (default: undefined) */
|
|
57
59
|
positionPropertyName?: keyof T;
|
|
58
60
|
/** If sets, sets the label for the position column (default: 'Position') */
|
|
@@ -127,6 +129,7 @@ export const DynamicDataList = <T extends Data>({
|
|
|
127
129
|
stickyHeader = true,
|
|
128
130
|
disabled = false,
|
|
129
131
|
className = '',
|
|
132
|
+
textWrap = false,
|
|
130
133
|
onChange = noop,
|
|
131
134
|
onAddTransformData = (data) => data as T,
|
|
132
135
|
rowClassNameProvider,
|
|
@@ -233,6 +236,7 @@ export const DynamicDataList = <T extends Data>({
|
|
|
233
236
|
actionSize={listRowActionSize}
|
|
234
237
|
horizontalTextAlign={horizontalTextAlign}
|
|
235
238
|
verticalTextAlign={verticalTextAlign}
|
|
239
|
+
textWrap={textWrap}
|
|
236
240
|
allowRemove={allowNewData}
|
|
237
241
|
positionKey={positionPropertyName}
|
|
238
242
|
allowDragging={allowRowDragging}
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
display: grid;
|
|
11
11
|
padding: 1px 0px 1px 0px;
|
|
12
|
-
grid-auto-rows: var(--dynamic-list-row-height, $dynamic-list-row-height);
|
|
13
12
|
column-gap: var(--dynamic-list-column-gap, $dynamic-list-column-gap);
|
|
14
13
|
|
|
15
14
|
border-bottom: var(--dynamic-list-row-border, $dynamic-list-row-border);
|
|
@@ -26,7 +25,7 @@
|
|
|
26
25
|
align-items: center;
|
|
27
26
|
|
|
28
27
|
.wrapper {
|
|
29
|
-
min-height:
|
|
28
|
+
min-height: var(--dynamic-list-row-height, $dynamic-list-row-height);
|
|
30
29
|
min-width: 100%;
|
|
31
30
|
display: grid;
|
|
32
31
|
align-items: center;
|
|
@@ -41,9 +40,16 @@
|
|
|
41
40
|
max-width: 100%;
|
|
42
41
|
max-height: 100%;
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
span {
|
|
44
|
+
padding: 5px 0;
|
|
45
|
+
display: grid;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&.nowrap {
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
text-overflow: ellipsis;
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
}
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
.position,
|
|
@@ -407,6 +407,43 @@ describe('DynamicListRow', () => {
|
|
|
407
407
|
expect(input.prop('value')).toBe(dataWithPosition.position);
|
|
408
408
|
});
|
|
409
409
|
|
|
410
|
+
describe('DynamicListRow column text alignments', () => {
|
|
411
|
+
const alignments: {
|
|
412
|
+
horizontal: 'left' | 'right' | 'center' | undefined;
|
|
413
|
+
vertical: 'center' | 'start' | 'end' | undefined;
|
|
414
|
+
}[] = [
|
|
415
|
+
{ horizontal: 'center', vertical: 'center' },
|
|
416
|
+
{ horizontal: 'left', vertical: 'start' },
|
|
417
|
+
{ horizontal: 'right', vertical: 'end' },
|
|
418
|
+
{ horizontal: undefined, vertical: undefined },
|
|
419
|
+
];
|
|
420
|
+
|
|
421
|
+
alignments.forEach(({ horizontal, vertical }) => {
|
|
422
|
+
it(`should apply the correct styles for justify-content: ${horizontal}, align-items: ${vertical}`, () => {
|
|
423
|
+
const wrapper = mount(
|
|
424
|
+
<DynamicListRow
|
|
425
|
+
columns={defaultColumns}
|
|
426
|
+
columnSizes={defaultProps.columnSizes}
|
|
427
|
+
data={dataWithPosition}
|
|
428
|
+
positionKey={'position'}
|
|
429
|
+
showPositionColumn={true}
|
|
430
|
+
horizontalTextAlign={horizontal}
|
|
431
|
+
verticalTextAlign={vertical}
|
|
432
|
+
/>,
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
const wrapperDivs = wrapper.find('.wrapper');
|
|
436
|
+
|
|
437
|
+
wrapperDivs.forEach((node) => {
|
|
438
|
+
const style = node.prop('style');
|
|
439
|
+
|
|
440
|
+
expect(style).toHaveProperty('justifyContent', horizontal);
|
|
441
|
+
expect(style).toHaveProperty('alignItems', vertical);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
410
447
|
describe('tooltip', () => {
|
|
411
448
|
it(`renders a tooltip using the 'title' html attribute by default`, () => {
|
|
412
449
|
const wrapper = shallow(
|
|
@@ -28,6 +28,8 @@ export interface DynamicListRowProps<T extends Data> {
|
|
|
28
28
|
horizontalTextAlign?: 'left' | 'right' | 'center';
|
|
29
29
|
/** Vertical alignment of text */
|
|
30
30
|
verticalTextAlign?: 'start' | 'center' | 'end';
|
|
31
|
+
/** If set to true, column text overflow will be wrapped to a new line. Otherwise, it will be truncated with an ellipsis (default: false) */
|
|
32
|
+
textWrap?: boolean;
|
|
31
33
|
/** If set to true, the remove action button will be rendered (default: undefined) */
|
|
32
34
|
allowRemove?: boolean;
|
|
33
35
|
/** If set to true, editable fields will be highlighted and row click events will be fired (default: false) */
|
|
@@ -85,6 +87,7 @@ export const DynamicListRow = <T extends Data>({
|
|
|
85
87
|
showPositionColumn = false,
|
|
86
88
|
showActionColumn = false,
|
|
87
89
|
allowEditing = false,
|
|
90
|
+
textWrap = false,
|
|
88
91
|
}: PropsWithChildren<DynamicListRowProps<T>>): JSX.Element => {
|
|
89
92
|
const customStyles = {
|
|
90
93
|
gridAutoRows: `minmax(50px, ${rowHeight})`,
|
|
@@ -170,7 +173,7 @@ export const DynamicListRow = <T extends Data>({
|
|
|
170
173
|
</div>
|
|
171
174
|
)}
|
|
172
175
|
{columns.map((column: DynamicListColumn<T>) => {
|
|
173
|
-
const columnData
|
|
176
|
+
const { columnData, tooltip } = renderData<T>(column, data);
|
|
174
177
|
|
|
175
178
|
return (
|
|
176
179
|
<div
|
|
@@ -179,14 +182,16 @@ export const DynamicListRow = <T extends Data>({
|
|
|
179
182
|
column.dataEntryRender !== undefined && allowEditing,
|
|
180
183
|
})}
|
|
181
184
|
key={column.key ?? (column.propertyName as string)}
|
|
185
|
+
style={{
|
|
186
|
+
justifyContent: horizontalTextAlign,
|
|
187
|
+
alignItems: verticalTextAlign,
|
|
188
|
+
}}
|
|
182
189
|
>
|
|
183
190
|
<div
|
|
184
|
-
className={classes.column
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
: undefined
|
|
189
|
-
}
|
|
191
|
+
className={clsx(classes.column, {
|
|
192
|
+
[classes.nowrap]: !textWrap,
|
|
193
|
+
})}
|
|
194
|
+
title={tooltip}
|
|
190
195
|
data-test-id={`dynamic-list-property:${
|
|
191
196
|
column.propertyName as string
|
|
192
197
|
}`}
|
|
@@ -226,22 +231,26 @@ export const DynamicListRow = <T extends Data>({
|
|
|
226
231
|
</div>
|
|
227
232
|
);
|
|
228
233
|
};
|
|
229
|
-
|
|
230
|
-
const renderData = function <T extends Data>(
|
|
234
|
+
const renderData = <T extends Data>(
|
|
231
235
|
column: DynamicListColumn<T>,
|
|
232
236
|
data: T,
|
|
233
|
-
): React.ReactNode {
|
|
237
|
+
): { columnData: React.ReactNode; tooltip: string | undefined } => {
|
|
238
|
+
const getTooltip = (value: unknown): string | undefined =>
|
|
239
|
+
column.tooltip !== false ? getTooltipText(value) : undefined;
|
|
240
|
+
|
|
234
241
|
if (!column.propertyName) {
|
|
235
|
-
|
|
242
|
+
const columnData = column.render?.(undefined, data);
|
|
243
|
+
return { columnData, tooltip: getTooltip(columnData) };
|
|
236
244
|
}
|
|
237
245
|
const value: unknown = data[column.propertyName];
|
|
238
246
|
if (column.render) {
|
|
239
|
-
|
|
247
|
+
const columnData = column.render(value, data);
|
|
248
|
+
return { columnData, tooltip: getTooltip(columnData) };
|
|
240
249
|
}
|
|
241
250
|
|
|
242
251
|
if (value === null || value === undefined) {
|
|
243
|
-
return
|
|
252
|
+
return { columnData: <span />, tooltip: undefined };
|
|
244
253
|
}
|
|
245
254
|
|
|
246
|
-
return String(value);
|
|
255
|
+
return { columnData: String(value), tooltip: getTooltip(String(value)) };
|
|
247
256
|
};
|
|
@@ -24,7 +24,7 @@ import { MessageBar } from '../MessageBar';
|
|
|
24
24
|
import { StationError } from '../models';
|
|
25
25
|
import { PageHeaderAction } from '../PageHeader';
|
|
26
26
|
import { PageHeader } from '../PageHeader/PageHeader';
|
|
27
|
-
import {
|
|
27
|
+
import { PageHeaderActionsGroup } from '../PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup';
|
|
28
28
|
import * as GS from '../Utils/State/GlobalState';
|
|
29
29
|
import { Explorer, ExplorerProps } from './Explorer';
|
|
30
30
|
import {
|
|
@@ -34,6 +34,10 @@ import {
|
|
|
34
34
|
} from './Explorer.model';
|
|
35
35
|
import { StationMessage } from './useStationMessage';
|
|
36
36
|
|
|
37
|
+
jest.mock('../../utils/GenerateId', () => ({
|
|
38
|
+
uuid: jest.fn().mockReturnValue('test-uuid'),
|
|
39
|
+
}));
|
|
40
|
+
|
|
37
41
|
interface ExplorerTestData {
|
|
38
42
|
id: number;
|
|
39
43
|
title: string;
|
|
@@ -152,7 +156,6 @@ describe('Explorer', () => {
|
|
|
152
156
|
columns={[{ propertyName: 'id' }]}
|
|
153
157
|
dataProvider={provider}
|
|
154
158
|
stationKey="mock-key"
|
|
155
|
-
openBulkActionsOnStart={true}
|
|
156
159
|
bulkActions={[{ label: 'Something', onClick: jest.fn() }]}
|
|
157
160
|
/>,
|
|
158
161
|
);
|
|
@@ -166,8 +169,9 @@ describe('Explorer', () => {
|
|
|
166
169
|
await wrapper.update();
|
|
167
170
|
});
|
|
168
171
|
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
expect(
|
|
173
|
+
wrapper.find(PageHeaderActionsGroup).prop('groupActionsDisabled'),
|
|
174
|
+
).toBe(true);
|
|
171
175
|
});
|
|
172
176
|
|
|
173
177
|
it('enables bulk actions if filtered results are returned', async () => {
|
|
@@ -203,7 +207,10 @@ describe('Explorer', () => {
|
|
|
203
207
|
});
|
|
204
208
|
|
|
205
209
|
wrapper.update();
|
|
206
|
-
|
|
210
|
+
|
|
211
|
+
expect(
|
|
212
|
+
wrapper.find(PageHeaderActionsGroup).prop('groupActionsDisabled'),
|
|
213
|
+
).toBe(false);
|
|
207
214
|
});
|
|
208
215
|
|
|
209
216
|
it('disables bulk actions if no filtered results are returned', async () => {
|
|
@@ -239,7 +246,9 @@ describe('Explorer', () => {
|
|
|
239
246
|
});
|
|
240
247
|
|
|
241
248
|
wrapper.update();
|
|
242
|
-
expect(
|
|
249
|
+
expect(
|
|
250
|
+
wrapper.find(PageHeaderActionsGroup).prop('groupActionsDisabled'),
|
|
251
|
+
).toBe(true);
|
|
243
252
|
});
|
|
244
253
|
|
|
245
254
|
it('Reloads data when a bulk action with reloadData is triggered', async () => {
|
|
@@ -267,10 +276,10 @@ describe('Explorer', () => {
|
|
|
267
276
|
// Initial Data Loading
|
|
268
277
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
269
278
|
|
|
270
|
-
const
|
|
279
|
+
const bulkActions = wrapper.find(PageHeaderActionsGroup);
|
|
271
280
|
|
|
272
281
|
await act(async () => {
|
|
273
|
-
|
|
282
|
+
bulkActions.prop('actions')?.[0].onClick?.();
|
|
274
283
|
|
|
275
284
|
await wrapper.update();
|
|
276
285
|
});
|
|
@@ -301,9 +310,9 @@ describe('Explorer', () => {
|
|
|
301
310
|
return wrapper;
|
|
302
311
|
});
|
|
303
312
|
|
|
304
|
-
const
|
|
313
|
+
const bulkActions = wrapper.find(PageHeaderActionsGroup);
|
|
305
314
|
await act(async () => {
|
|
306
|
-
|
|
315
|
+
bulkActions.prop('actions')?.[0].onClick?.();
|
|
307
316
|
await wrapper.update();
|
|
308
317
|
});
|
|
309
318
|
|
|
@@ -336,9 +345,9 @@ describe('Explorer', () => {
|
|
|
336
345
|
return wrapper;
|
|
337
346
|
});
|
|
338
347
|
|
|
339
|
-
const
|
|
348
|
+
const bulkActions = wrapper.find(PageHeaderActionsGroup);
|
|
340
349
|
await act(async () => {
|
|
341
|
-
|
|
350
|
+
bulkActions.prop('actions')?.[0].onClick?.();
|
|
342
351
|
await wrapper.update();
|
|
343
352
|
});
|
|
344
353
|
|
|
@@ -401,7 +410,7 @@ describe('Explorer', () => {
|
|
|
401
410
|
expectComponentReceivesValue(header, 'title');
|
|
402
411
|
// expectComponentReceivesValue(header, 'actions');
|
|
403
412
|
// expectComponentReceivesValue(header, 'bulkActions');
|
|
404
|
-
expectComponentReceivesValue(header, 'openBulkActionsOnStart');
|
|
413
|
+
// expectComponentReceivesValue(header, 'openBulkActionsOnStart');
|
|
405
414
|
|
|
406
415
|
const filters = wrapper.find(Filters);
|
|
407
416
|
expectComponentReceivesValue(filters, 'filters');
|
|
@@ -2025,9 +2034,10 @@ describe('Explorer', () => {
|
|
|
2025
2034
|
});
|
|
2026
2035
|
|
|
2027
2036
|
await act(async () => {
|
|
2028
|
-
wrapper.find(
|
|
2029
|
-
|
|
2030
|
-
|
|
2037
|
+
wrapper.find(PageHeaderActionsGroup).prop('onActionsGroupToggled')!(true);
|
|
2038
|
+
wrapper.find(PageHeaderActionsGroup).prop('onActionsGroupToggled')!(
|
|
2039
|
+
false,
|
|
2040
|
+
);
|
|
2031
2041
|
});
|
|
2032
2042
|
|
|
2033
2043
|
expect(spy).toHaveBeenCalledTimes(2);
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
useEffect,
|
|
8
8
|
useState,
|
|
9
9
|
} from 'react';
|
|
10
|
-
import { ActionData } from '..';
|
|
10
|
+
import { ActionData, IconName } from '..';
|
|
11
11
|
import { noop } from '../../helpers/utils';
|
|
12
12
|
import { showNotification } from '../../initialize';
|
|
13
13
|
import { Data } from '../../types/data';
|
|
@@ -23,7 +23,11 @@ import {
|
|
|
23
23
|
ListSelectMode,
|
|
24
24
|
SortData,
|
|
25
25
|
} from '../List';
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
PageHeader,
|
|
28
|
+
PageHeaderActionItemProps,
|
|
29
|
+
PageHeaderActionProps,
|
|
30
|
+
} from '../PageHeader';
|
|
27
31
|
import { isPageHeaderNavigationAction } from '../PageHeader/PageHeaderAction/PageHeaderAction';
|
|
28
32
|
import { PageHeaderJsActionProps } from '../PageHeader/PageHeaderAction/PageHeaderAction.model';
|
|
29
33
|
import { getState, storeState } from '../Utils/State/GlobalState';
|
|
@@ -88,6 +92,9 @@ export interface ExplorerProps<T extends Data> {
|
|
|
88
92
|
/** Vertical alignment of text */
|
|
89
93
|
verticalTextAlign?: 'start' | 'center' | 'end';
|
|
90
94
|
|
|
95
|
+
/** If set to true, column text overflow will be wrapped to a new line. Otherwise, it will be truncated with an ellipsis (default: true) */
|
|
96
|
+
textWrap?: boolean;
|
|
97
|
+
|
|
91
98
|
/** Defines when the loading of the next page is triggered. The number represents the number of row left, before a load is triggered. (default: 10) */
|
|
92
99
|
loadingTriggerOffset?: number;
|
|
93
100
|
|
|
@@ -179,6 +186,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
|
|
|
179
186
|
headerRowActionSize,
|
|
180
187
|
horizontalTextAlign,
|
|
181
188
|
verticalTextAlign,
|
|
189
|
+
textWrap,
|
|
182
190
|
|
|
183
191
|
columns,
|
|
184
192
|
filterOptions,
|
|
@@ -348,24 +356,48 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
|
|
|
348
356
|
});
|
|
349
357
|
};
|
|
350
358
|
|
|
351
|
-
const pageHeaderActionsHandler = ():
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
const pageHeaderActionsHandler = (): PageHeaderActionItemProps[] => {
|
|
360
|
+
const headerActions: PageHeaderActionItemProps[] = [];
|
|
361
|
+
|
|
362
|
+
if (bulkActions && bulkActions.length > 0) {
|
|
363
|
+
headerActions.push({
|
|
364
|
+
label: 'Bulk Actions',
|
|
365
|
+
icon: IconName.Bulk,
|
|
366
|
+
kind: 'group',
|
|
367
|
+
actions: bulkActionsHandler(),
|
|
368
|
+
openActionsGroupOnStart: openBulkActionsOnStart,
|
|
369
|
+
onActionsGroupToggled: (isOpen) => {
|
|
370
|
+
setIsBulkOpen(isOpen);
|
|
371
|
+
onBulkActionsToggled(isOpen);
|
|
372
|
+
},
|
|
373
|
+
groupActionsDisabled:
|
|
374
|
+
itemSelection.items?.length === 0 || resultCount?.filtered === 0,
|
|
375
|
+
});
|
|
376
|
+
headerActions.push({ kind: 'spacer' });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
actions?.forEach((action) => {
|
|
380
|
+
headerActions.push({
|
|
381
|
+
...(isPageHeaderNavigationAction(action)
|
|
382
|
+
? action
|
|
383
|
+
: {
|
|
384
|
+
...action,
|
|
385
|
+
onClick: async () => {
|
|
386
|
+
try {
|
|
387
|
+
const result = await action.onClick();
|
|
388
|
+
if (result) {
|
|
389
|
+
setStationMessage(errMsg(result));
|
|
390
|
+
}
|
|
391
|
+
} catch (error) {
|
|
392
|
+
setStationMessage(errMsg(error, errAction));
|
|
362
393
|
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
};
|
|
394
|
+
},
|
|
395
|
+
}),
|
|
396
|
+
kind: 'action',
|
|
397
|
+
});
|
|
368
398
|
});
|
|
399
|
+
|
|
400
|
+
return headerActions;
|
|
369
401
|
};
|
|
370
402
|
|
|
371
403
|
const bulkActionsHandler = (): PageHeaderJsActionProps[] => {
|
|
@@ -421,16 +453,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
|
|
|
421
453
|
<PageHeader
|
|
422
454
|
title={title}
|
|
423
455
|
subtitle={resultsTitle}
|
|
424
|
-
actions={
|
|
425
|
-
bulkActions={bulkActions && bulkActionsHandler()}
|
|
426
|
-
openBulkActionsOnStart={openBulkActionsOnStart}
|
|
427
|
-
bulkActionsDisabled={
|
|
428
|
-
itemSelection.items?.length === 0 || resultCount?.filtered === 0
|
|
429
|
-
}
|
|
430
|
-
onBulkActionsToggled={(isOpen) => {
|
|
431
|
-
setIsBulkOpen(isOpen);
|
|
432
|
-
onBulkActionsToggled(isOpen);
|
|
433
|
-
}}
|
|
456
|
+
actions={pageHeaderActionsHandler()}
|
|
434
457
|
setTabTitle={setTabTitle}
|
|
435
458
|
/>
|
|
436
459
|
{StationMessage}
|
|
@@ -460,6 +483,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
|
|
|
460
483
|
headerRowActionSize={headerRowActionSize}
|
|
461
484
|
horizontalTextAlign={horizontalTextAlign}
|
|
462
485
|
verticalTextAlign={verticalTextAlign}
|
|
486
|
+
textWrap={textWrap}
|
|
463
487
|
keyProperty={keyProperty}
|
|
464
488
|
showActionButton={Boolean(generateItemLink) || Boolean(onItemClicked)} // or hard code to `true`?
|
|
465
489
|
selectionMode={mode}
|
|
@@ -117,7 +117,7 @@ describe('NavigationExplorer', () => {
|
|
|
117
117
|
return wrapper;
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
const action = wrapper.find(PageHeaderAction);
|
|
120
|
+
const action = wrapper.find(PageHeaderAction).last();
|
|
121
121
|
expect(action.props().label).toBe('test');
|
|
122
122
|
});
|
|
123
123
|
|
|
@@ -219,7 +219,7 @@ describe('NavigationExplorer', () => {
|
|
|
219
219
|
return wrapper;
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
-
const createAction = wrapper.find(PageHeaderAction);
|
|
222
|
+
const createAction = wrapper.find(PageHeaderAction).last();
|
|
223
223
|
|
|
224
224
|
createAction.simulate('click');
|
|
225
225
|
|
|
@@ -3,14 +3,16 @@ import { noop } from 'lodash';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { act } from 'react-dom/test-utils';
|
|
5
5
|
import { actWithReturn } from '../../../helpers/testing';
|
|
6
|
-
import * as app from '../../../initialize';
|
|
7
6
|
import { Column } from '../../List';
|
|
8
|
-
import {
|
|
9
|
-
import { PageHeaderBulkActions } from '../../PageHeader/PageHeaderBulkActions/PageHeaderBulkActions';
|
|
7
|
+
import { PageHeaderActionsGroup } from '../../PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup';
|
|
10
8
|
import { Explorer } from '../Explorer';
|
|
11
9
|
import { ExplorerDataProvider } from '../Explorer.model';
|
|
12
10
|
import { SelectionExplorer } from './SelectionExplorer';
|
|
13
11
|
|
|
12
|
+
jest.mock('../../../utils/GenerateId', () => ({
|
|
13
|
+
uuid: jest.fn().mockReturnValue('test-uuid'),
|
|
14
|
+
}));
|
|
15
|
+
|
|
14
16
|
interface SelectExplorerTestData {
|
|
15
17
|
id: string;
|
|
16
18
|
desc: string;
|
|
@@ -78,7 +80,7 @@ describe('SelectionExplorer', () => {
|
|
|
78
80
|
return wrapper;
|
|
79
81
|
});
|
|
80
82
|
|
|
81
|
-
const bulkActions = wrapper.find(
|
|
83
|
+
const bulkActions = wrapper.find(PageHeaderActionsGroup);
|
|
82
84
|
|
|
83
85
|
expect(bulkActions.exists()).toBe(true);
|
|
84
86
|
});
|
|
@@ -101,38 +103,12 @@ describe('SelectionExplorer', () => {
|
|
|
101
103
|
return wrapper;
|
|
102
104
|
});
|
|
103
105
|
|
|
104
|
-
const bulkActions = wrapper.find(
|
|
106
|
+
const bulkActions = wrapper.find(PageHeaderActionsGroup);
|
|
105
107
|
|
|
106
108
|
expect(bulkActions.exists()).toBe(false);
|
|
107
109
|
});
|
|
108
110
|
|
|
109
|
-
it('Does not call "showNotification" when "Apply Selection" is clicked'
|
|
110
|
-
const [provider] = getDataProvider();
|
|
111
|
-
const showNotificationSpy: jest.SpyInstance = jest.spyOn(
|
|
112
|
-
app,
|
|
113
|
-
'showNotification',
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const wrapper = await actWithReturn(async () => {
|
|
117
|
-
const wrapper = mount(
|
|
118
|
-
<SelectionExplorer
|
|
119
|
-
columns={mockListColumns}
|
|
120
|
-
dataProvider={provider}
|
|
121
|
-
stationKey="mock-key"
|
|
122
|
-
allowBulkSelect={true}
|
|
123
|
-
/>,
|
|
124
|
-
);
|
|
125
|
-
return wrapper;
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const header = wrapper.find(PageHeader);
|
|
129
|
-
await act(async () => {
|
|
130
|
-
header.prop('bulkActions')?.[0].onClick?.();
|
|
131
|
-
await wrapper.update();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
expect(showNotificationSpy).toHaveBeenCalledTimes(0);
|
|
135
|
-
});
|
|
111
|
+
it.todo('Does not call "showNotification" when "Apply Selection" is clicked');
|
|
136
112
|
|
|
137
113
|
it('sends onSelection callback when the selection of a single item is triggered', async () => {
|
|
138
114
|
const [provider] = getDataProvider();
|
|
@@ -21,6 +21,7 @@ import { SearcheableOptionsFilter } from '../SelectionTypes/SearcheableOptionsFi
|
|
|
21
21
|
import classes from './Filter.scss';
|
|
22
22
|
|
|
23
23
|
export interface FilterProps<T extends Data> {
|
|
24
|
+
selectOnFocus?: boolean;
|
|
24
25
|
options: FilterType<T>;
|
|
25
26
|
value?: FilterValue;
|
|
26
27
|
index?: number;
|
|
@@ -42,6 +43,7 @@ export const Filter = <T extends Data>({
|
|
|
42
43
|
onFilterChange,
|
|
43
44
|
onFilterClicked,
|
|
44
45
|
onValidate,
|
|
46
|
+
selectOnFocus = true,
|
|
45
47
|
}: PropsWithChildren<FilterProps<T>>): JSX.Element => {
|
|
46
48
|
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
|
47
49
|
const [hasError, setHasError] = useState<boolean>(false);
|
|
@@ -116,6 +118,7 @@ export const Filter = <T extends Data>({
|
|
|
116
118
|
}
|
|
117
119
|
onError={onError}
|
|
118
120
|
onValidate={onValidate}
|
|
121
|
+
selectOnFocus={selectOnFocus}
|
|
119
122
|
/>
|
|
120
123
|
);
|
|
121
124
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallow } from 'enzyme';
|
|
1
|
+
import { mount, shallow } from 'enzyme';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { noop } from '../../../../helpers/utils';
|
|
4
4
|
import { FreeTextFilter } from './FreeTextFilter';
|
|
@@ -42,4 +42,19 @@ describe('FreeTextFilter', () => {
|
|
|
42
42
|
|
|
43
43
|
expect(error).toBeDefined();
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
it('selects text on focus when selectOnFocus is true and there is a value', () => {
|
|
47
|
+
const mockValue = 'test value';
|
|
48
|
+
const spy = jest.fn();
|
|
49
|
+
|
|
50
|
+
const wrapper = mount(
|
|
51
|
+
<FreeTextFilter onSelect={spy} value={mockValue} selectOnFocus={true} />,
|
|
52
|
+
);
|
|
53
|
+
const input = wrapper.find('input');
|
|
54
|
+
input.simulate('focus');
|
|
55
|
+
|
|
56
|
+
const inputElement = input.getDOMNode<HTMLInputElement>();
|
|
57
|
+
expect(inputElement.selectionStart).toBe(0);
|
|
58
|
+
expect(inputElement.selectionEnd).toBe(mockValue.length);
|
|
59
|
+
});
|
|
45
60
|
});
|