@axinom/mosaic-ui 0.34.0-rc.2 → 0.34.0-rc.21
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/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/Filters.d.ts.map +1 -1
- package/dist/components/Filters/Filters.model.d.ts +2 -0
- package/dist/components/Filters/Filters.model.d.ts.map +1 -1
- package/dist/components/FormElements/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/FormElements/Checkbox/CheckboxField.d.ts +1 -1
- package/dist/components/FormElements/Checkbox/CheckboxField.d.ts.map +1 -1
- package/dist/components/FormElements/CustomTags/CustomTagsField.d.ts.map +1 -1
- package/dist/components/FormElements/DateTimeField/DateTimeTextField.d.ts +1 -1
- package/dist/components/FormElements/DateTimeField/DateTimeTextField.d.ts.map +1 -1
- package/dist/components/FormElements/DynamicDataListControl/DynamicDataListField.d.ts.map +1 -1
- package/dist/components/FormElements/FileUploadControl/FileUploadControl.d.ts.map +1 -1
- package/dist/components/FormElements/FileUploadControl/FileUploadField.d.ts.map +1 -1
- package/dist/components/FormElements/MaskedSingleLineText/MaskedSingleLineTextField.d.ts +1 -1
- package/dist/components/FormElements/MaskedSingleLineText/MaskedSingleLineTextField.d.ts.map +1 -1
- package/dist/components/FormElements/SingleLineText/SingleLineText.d.ts.map +1 -1
- package/dist/components/List/List.d.ts.map +1 -1
- package/dist/components/List/List.model.d.ts +2 -0
- package/dist/components/List/List.model.d.ts.map +1 -1
- package/dist/components/List/ListHeader/ListHeader.d.ts +7 -1
- package/dist/components/List/ListHeader/ListHeader.d.ts.map +1 -1
- package/dist/components/List/ListHeader/useResize.d.ts +18 -0
- package/dist/components/List/ListHeader/useResize.d.ts.map +1 -0
- package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRowLoader.d.ts +2 -2
- package/dist/components/List/ListRow/ListRowLoader.d.ts.map +1 -1
- package/dist/components/List/ListRow/Renderers/BooleanDotRenderer/BooleanDotRenderer.d.ts.map +1 -1
- package/dist/components/List/useColumnsSize.d.ts +21 -0
- package/dist/components/List/useColumnsSize.d.ts.map +1 -0
- package/dist/index.es.js +3 -3
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/initialize.d.ts +3 -3
- package/dist/initialize.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createInputRenderer/createInputRenderer.spec.tsx +2 -2
- package/src/components/Explorer/Explorer.stories.tsx +16 -0
- package/src/components/Explorer/Explorer.tsx +33 -31
- package/src/components/Filters/Filter/Filter.spec.tsx +24 -1
- package/src/components/Filters/Filter/Filter.tsx +6 -0
- package/src/components/Filters/Filters.model.ts +3 -0
- package/src/components/Filters/Filters.stories.tsx +9 -0
- package/src/components/Filters/Filters.tsx +1 -0
- package/src/components/FormElements/Checkbox/Checkbox.tsx +1 -1
- package/src/components/FormElements/Checkbox/CheckboxField.tsx +4 -5
- package/src/components/FormElements/CustomTags/CustomTags.spec.tsx +3 -3
- package/src/components/FormElements/CustomTags/CustomTags.tsx +3 -3
- package/src/components/FormElements/CustomTags/CustomTagsField.tsx +1 -2
- package/src/components/FormElements/DateTimeField/DateTimeTextField.tsx +3 -3
- package/src/components/FormElements/DynamicDataListControl/DynamicDataListField.tsx +1 -2
- package/src/components/FormElements/FileUploadControl/FileUploadControl.spec.tsx +35 -0
- package/src/components/FormElements/FileUploadControl/FileUploadControl.tsx +2 -0
- package/src/components/FormElements/FileUploadControl/FileUploadField.tsx +1 -2
- package/src/components/FormElements/FormElementContainer/FormElementContainer.scss +2 -0
- package/src/components/FormElements/MaskedSingleLineText/MaskedSingleLineTextField.tsx +1 -1
- package/src/components/FormElements/Radio/RadioField.tsx +2 -2
- package/src/components/FormElements/SingleLineText/SingleLineText.spec.tsx +5 -4
- package/src/components/FormElements/SingleLineText/SingleLineText.tsx +6 -1
- package/src/components/FormStation/FormStation.spec.tsx +12 -6
- package/src/components/FormStation/FormStation.tsx +6 -6
- package/src/components/LandingPageTiles/TileLarge/TileLarge.scss +7 -3
- package/src/components/List/List.model.ts +3 -0
- package/src/components/List/List.scss +0 -2
- package/src/components/List/List.stories.tsx +1 -1
- package/src/components/List/List.tsx +17 -55
- package/src/components/List/ListHeader/ListHeader.scss +23 -10
- package/src/components/List/ListHeader/ListHeader.spec.tsx +56 -0
- package/src/components/List/ListHeader/ListHeader.tsx +43 -9
- package/src/components/List/ListHeader/useResize.ts +108 -0
- package/src/components/List/ListRow/ListRow.scss +8 -12
- package/src/components/List/ListRow/ListRow.spec.tsx +5 -21
- package/src/components/List/ListRow/ListRow.tsx +16 -32
- package/src/components/List/ListRow/ListRowLoader.tsx +14 -4
- package/src/components/List/ListRow/Renderers/BooleanDotRenderer/BooleanDotRenderer.scss +10 -8
- package/src/components/List/ListRow/Renderers/BooleanDotRenderer/BooleanDotRenderer.tsx +3 -1
- package/src/components/List/useColumnsSize.ts +120 -0
- package/src/initialize.ts +4 -4
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
height: 100%;
|
|
5
5
|
display: grid;
|
|
6
6
|
grid-template-columns: 1fr;
|
|
7
|
-
grid-template-rows:
|
|
7
|
+
grid-template-rows: 130px 1fr;
|
|
8
8
|
grid-column: span 4;
|
|
9
9
|
grid-row: span 2;
|
|
10
10
|
place-items: center;
|
|
@@ -41,12 +41,17 @@
|
|
|
41
41
|
$landingpage-largetile-stroke-color
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
> * {
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 100%;
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
.titlesSection {
|
|
47
52
|
display: grid;
|
|
48
53
|
grid-template-columns: 1fr;
|
|
49
|
-
grid-template-rows:
|
|
54
|
+
grid-template-rows: 3fr 1fr;
|
|
50
55
|
place-items: center;
|
|
51
56
|
align-self: stretch;
|
|
52
57
|
|
|
@@ -59,7 +64,6 @@
|
|
|
59
64
|
|
|
60
65
|
.subtitle {
|
|
61
66
|
font-size: 16px;
|
|
62
|
-
margin-bottom: 30px;
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
}
|
|
@@ -46,6 +46,9 @@ export interface Column<T extends Data> {
|
|
|
46
46
|
|
|
47
47
|
/** Specify the horizontal text alignment of the column */
|
|
48
48
|
horizontalColumnAlign?: 'left' | 'center' | 'right';
|
|
49
|
+
|
|
50
|
+
/** If set to true, the column will not be resizable */
|
|
51
|
+
disableResizing?: boolean;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
export interface ColumnMap {
|
|
@@ -248,7 +248,7 @@ export const NotSortableColumn: StoryObj<StoryListType> = {
|
|
|
248
248
|
args: {
|
|
249
249
|
columns: [
|
|
250
250
|
defaultColumns[0],
|
|
251
|
-
{ ...defaultColumns[1], sortable: false },
|
|
251
|
+
{ ...defaultColumns[1], sortable: false, disableResizing: true },
|
|
252
252
|
...defaultColumns.slice(2),
|
|
253
253
|
],
|
|
254
254
|
},
|
|
@@ -24,6 +24,7 @@ import classes from './List.scss';
|
|
|
24
24
|
import { ListHeader } from './ListHeader/ListHeader';
|
|
25
25
|
import { ListRow } from './ListRow/ListRow';
|
|
26
26
|
import { ListRowLoader } from './ListRow/ListRowLoader';
|
|
27
|
+
import { useColumnsSize } from './useColumnsSize';
|
|
27
28
|
|
|
28
29
|
export interface ListProps<T extends Data> {
|
|
29
30
|
/**
|
|
@@ -104,53 +105,6 @@ export interface ListProps<T extends Data> {
|
|
|
104
105
|
inlineMenuActions?: (data: T) => ActionData[];
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
/**
|
|
108
|
-
* Generates a combined string of all columns.columnSize values, to be used as CSS value
|
|
109
|
-
* @param columns The list of columns that should be used
|
|
110
|
-
* @returns a string of all column sizes of the array, combined
|
|
111
|
-
*/
|
|
112
|
-
const getColumnsSizeDefinition = function <T extends Data>(
|
|
113
|
-
columns: Column<T>[],
|
|
114
|
-
showActionButton: boolean,
|
|
115
|
-
selectMode: ListSelectMode,
|
|
116
|
-
showInlineMenu: boolean,
|
|
117
|
-
): string {
|
|
118
|
-
const columnSizeDefinition = columns.map((column) => column.size ?? '1fr');
|
|
119
|
-
|
|
120
|
-
const hasActionsColumn =
|
|
121
|
-
selectMode !== ListSelectMode.None || showActionButton || showInlineMenu;
|
|
122
|
-
|
|
123
|
-
if (hasActionsColumn) {
|
|
124
|
-
columnSizeDefinition.push(
|
|
125
|
-
getActionsColumnSizePx(showInlineMenu, showActionButton),
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return columnSizeDefinition.join(' ');
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const getActionsColumnSizePx = (...enableActions: boolean[]): string => {
|
|
133
|
-
const enabledActionsCount = enableActions.filter(
|
|
134
|
-
(actionEnabled) => actionEnabled,
|
|
135
|
-
).length;
|
|
136
|
-
|
|
137
|
-
const defaultActionSizePx = 50;
|
|
138
|
-
const defaultActionsRawGapPx = 8;
|
|
139
|
-
const calculateMultiActionsColumnSizePx = (actionsCount: number): number => {
|
|
140
|
-
return (
|
|
141
|
-
defaultActionSizePx * actionsCount +
|
|
142
|
-
defaultActionsRawGapPx * (actionsCount - 1)
|
|
143
|
-
);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const sizePx =
|
|
147
|
-
enabledActionsCount > 1
|
|
148
|
-
? calculateMultiActionsColumnSizePx(enabledActionsCount)
|
|
149
|
-
: defaultActionSizePx;
|
|
150
|
-
|
|
151
|
-
return `${sizePx}px`;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
108
|
const noItemsMessage = (
|
|
155
109
|
itemsCount: number,
|
|
156
110
|
isLoading: boolean,
|
|
@@ -206,7 +160,7 @@ export const List = <T extends Data>({
|
|
|
206
160
|
isError = false,
|
|
207
161
|
errorMsg = 'There was an error.',
|
|
208
162
|
handleRetry = true,
|
|
209
|
-
minimumWidth = '
|
|
163
|
+
minimumWidth = 'fit-content',
|
|
210
164
|
columnGap = '5px',
|
|
211
165
|
rowGap = '0px',
|
|
212
166
|
headerRowHeight = '44px',
|
|
@@ -243,14 +197,19 @@ export const List = <T extends Data>({
|
|
|
243
197
|
});
|
|
244
198
|
}, [data]);
|
|
245
199
|
|
|
246
|
-
const columnSizes =
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
200
|
+
const { columnSizes, resetColumnSizes, setColumnSizes, hasActionColumn } =
|
|
201
|
+
useColumnsSize(
|
|
202
|
+
columns,
|
|
203
|
+
Boolean(showActionButton),
|
|
204
|
+
selectionMode,
|
|
205
|
+
Boolean(inlineMenuActions),
|
|
206
|
+
);
|
|
252
207
|
|
|
253
|
-
const customStyles = {
|
|
208
|
+
const customStyles = {
|
|
209
|
+
gridRowGap: rowGap,
|
|
210
|
+
minWidth: minimumWidth,
|
|
211
|
+
width: '100%',
|
|
212
|
+
};
|
|
254
213
|
|
|
255
214
|
const itemSelectionHandler = useCallback(
|
|
256
215
|
(items: ListItem<T>[]) => {
|
|
@@ -327,6 +286,9 @@ export const List = <T extends Data>({
|
|
|
327
286
|
isCheckboxDisabled={listItems.length === 0}
|
|
328
287
|
onCheckboxToggled={headerCheckboxHandler}
|
|
329
288
|
onSortChanged={sortChangedHandler}
|
|
289
|
+
onResetColumnSizes={resetColumnSizes}
|
|
290
|
+
onColumnSizesChanged={setColumnSizes}
|
|
291
|
+
hasActionColumn={hasActionColumn}
|
|
330
292
|
/>
|
|
331
293
|
{/* Rows */}
|
|
332
294
|
{listItems.map((item: ListItem<T>, index) => (
|
|
@@ -3,14 +3,9 @@
|
|
|
3
3
|
.container {
|
|
4
4
|
padding-left: 5px;
|
|
5
5
|
display: grid;
|
|
6
|
-
grid-template-columns: repeat(auto-fit, minmax(20px, 1fr));
|
|
7
|
-
grid-auto-rows: 44px;
|
|
8
|
-
column-gap: 5px;
|
|
9
|
-
justify-items: left;
|
|
10
|
-
align-items: center;
|
|
11
6
|
position: sticky;
|
|
12
7
|
top: 0;
|
|
13
|
-
z-index: 1;
|
|
8
|
+
//z-index: 1;
|
|
14
9
|
|
|
15
10
|
background-color: var(
|
|
16
11
|
--explorer-header-background-color,
|
|
@@ -22,11 +17,29 @@
|
|
|
22
17
|
|
|
23
18
|
.columnLabel {
|
|
24
19
|
box-sizing: border-box;
|
|
25
|
-
width:
|
|
20
|
+
width: 100%;
|
|
26
21
|
height: 100%;
|
|
27
22
|
display: grid;
|
|
28
|
-
grid-template-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
grid-template-columns: 1fr auto;
|
|
24
|
+
position: relative;
|
|
25
|
+
|
|
26
|
+
.resizeHandle {
|
|
27
|
+
cursor: col-resize;
|
|
28
|
+
width: 7px;
|
|
29
|
+
height: 100%;
|
|
30
|
+
|
|
31
|
+
z-index: 1;
|
|
32
|
+
border-right: var(--explorer-list-row-border, 1px solid #dddddd);
|
|
33
|
+
position: absolute;
|
|
34
|
+
right: 0;
|
|
35
|
+
|
|
36
|
+
&:hover:not(.resizeHandleDisabled) {
|
|
37
|
+
border-width: 3px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&.resizeHandleDisabled {
|
|
41
|
+
cursor: default;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
31
44
|
}
|
|
32
45
|
}
|
|
@@ -40,6 +40,9 @@ const mockProps: ListHeaderProps<TestListHeaderData> = {
|
|
|
40
40
|
actionSize: '50px',
|
|
41
41
|
horizontalTextAlign: 'left',
|
|
42
42
|
verticalTextAlign: 'center',
|
|
43
|
+
hasActionColumn: true,
|
|
44
|
+
onResetColumnSizes: jest.fn(),
|
|
45
|
+
onColumnSizesChanged: jest.fn(),
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
describe('ListHeader', () => {
|
|
@@ -155,4 +158,57 @@ describe('ListHeader', () => {
|
|
|
155
158
|
});
|
|
156
159
|
|
|
157
160
|
it.todo('reacts meaningfully when the columns are empty');
|
|
161
|
+
|
|
162
|
+
describe('Column Resizing', () => {
|
|
163
|
+
it('calls onResetColumnSizes when the reset button is clicked', () => {
|
|
164
|
+
const spy = jest.fn();
|
|
165
|
+
const wrapper = shallow(
|
|
166
|
+
<ListHeader {...mockProps} onResetColumnSizes={spy} />,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const resetButton = wrapper.find('.resizeHandle').first();
|
|
170
|
+
resetButton.simulate('doubleClick');
|
|
171
|
+
|
|
172
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('calls onResetColumnSizes when the reset button is clicked also on non-resizable column', () => {
|
|
176
|
+
const spy = jest.fn();
|
|
177
|
+
const wrapper = shallow(
|
|
178
|
+
<ListHeader
|
|
179
|
+
{...mockProps}
|
|
180
|
+
columns={[
|
|
181
|
+
{ ...mockListColumns[0], disableResizing: true },
|
|
182
|
+
...mockListColumns.slice(1),
|
|
183
|
+
]}
|
|
184
|
+
onResetColumnSizes={spy}
|
|
185
|
+
/>,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const resetButton = wrapper.find('.resizeHandle').first();
|
|
189
|
+
resetButton.simulate('doubleClick');
|
|
190
|
+
|
|
191
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('calls onColumnSizesChanged when the column is resized', () => {
|
|
195
|
+
const spy = jest.fn();
|
|
196
|
+
const wrapper = mount(
|
|
197
|
+
<ListHeader {...mockProps} onColumnSizesChanged={spy} />,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const resizeHandle = wrapper.find('.resizeHandle').first();
|
|
201
|
+
resizeHandle.invoke('onMouseDown')!({
|
|
202
|
+
preventDefault: jest.fn(),
|
|
203
|
+
} as any);
|
|
204
|
+
|
|
205
|
+
window.dispatchEvent(
|
|
206
|
+
new Event('mousemove', {
|
|
207
|
+
clientX: 100,
|
|
208
|
+
} as any),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
158
214
|
});
|
|
@@ -5,6 +5,7 @@ import { Column, SortData } from '../List.model';
|
|
|
5
5
|
import { ListCheckBox } from '../ListCheckBox/ListCheckBox';
|
|
6
6
|
import { ColumnLabel } from './ColumnLabel/ColumnLabel';
|
|
7
7
|
import classes from './ListHeader.scss';
|
|
8
|
+
import { useResize } from './useResize';
|
|
8
9
|
|
|
9
10
|
export interface ListHeaderProps<T extends Data> {
|
|
10
11
|
/** Column definitions */
|
|
@@ -37,6 +38,12 @@ export interface ListHeaderProps<T extends Data> {
|
|
|
37
38
|
onSortChanged?: (sort: SortData<T>) => void;
|
|
38
39
|
/** CSS Class name for additional styles */
|
|
39
40
|
className?: string;
|
|
41
|
+
/** called when the column sizes should be reset */
|
|
42
|
+
onResetColumnSizes: () => void;
|
|
43
|
+
/** called when the column sizes should be updated */
|
|
44
|
+
onColumnSizesChanged: (columnSizes: string) => void;
|
|
45
|
+
/** Whether or not the list has an action column */
|
|
46
|
+
hasActionColumn: boolean;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
@@ -66,6 +73,9 @@ export const ListHeader = <T extends Data>({
|
|
|
66
73
|
onCheckboxToggled,
|
|
67
74
|
onSortChanged,
|
|
68
75
|
className = '',
|
|
76
|
+
onResetColumnSizes,
|
|
77
|
+
onColumnSizesChanged,
|
|
78
|
+
hasActionColumn,
|
|
69
79
|
}: PropsWithChildren<ListHeaderProps<T>>): JSX.Element => {
|
|
70
80
|
const customStyles = {
|
|
71
81
|
gridAutoRows: rowHeight,
|
|
@@ -75,15 +85,18 @@ export const ListHeader = <T extends Data>({
|
|
|
75
85
|
alignItems: verticalTextAlign,
|
|
76
86
|
} as React.CSSProperties;
|
|
77
87
|
|
|
88
|
+
const { cols, mouseDown } = useResize(columns, onColumnSizesChanged);
|
|
89
|
+
|
|
78
90
|
return (
|
|
79
91
|
<div
|
|
80
92
|
className={clsx(classes.container, 'list-header-container', className)}
|
|
81
93
|
style={customStyles}
|
|
82
94
|
data-test-id="list-header"
|
|
83
95
|
>
|
|
84
|
-
{columns.map((column) => (
|
|
96
|
+
{columns.map((column, i) => (
|
|
85
97
|
<div
|
|
86
98
|
key={column.propertyName as string}
|
|
99
|
+
ref={cols[i].ref}
|
|
87
100
|
className={clsx(classes.columnLabel)}
|
|
88
101
|
>
|
|
89
102
|
<ColumnLabel<T>
|
|
@@ -94,16 +107,37 @@ export const ListHeader = <T extends Data>({
|
|
|
94
107
|
sortData={sortData}
|
|
95
108
|
onSortChanged={onSortChanged}
|
|
96
109
|
/>
|
|
110
|
+
<div
|
|
111
|
+
onMouseDown={
|
|
112
|
+
!column.disableResizing
|
|
113
|
+
? (e) => {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
mouseDown(i);
|
|
116
|
+
}
|
|
117
|
+
: undefined
|
|
118
|
+
}
|
|
119
|
+
onDoubleClick={() => onResetColumnSizes()}
|
|
120
|
+
className={clsx(classes.resizeHandle, {
|
|
121
|
+
[classes.resizeHandleDisabled]: column.disableResizing,
|
|
122
|
+
})}
|
|
123
|
+
/>
|
|
97
124
|
</div>
|
|
98
125
|
))}
|
|
99
|
-
{
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
{hasActionColumn && (
|
|
127
|
+
<div
|
|
128
|
+
ref={cols[cols.length - 1].ref}
|
|
129
|
+
className={clsx(classes.columnLabel)}
|
|
130
|
+
>
|
|
131
|
+
{showItemCheckbox && (
|
|
132
|
+
<ListCheckBox
|
|
133
|
+
height={actionSize}
|
|
134
|
+
width={actionSize}
|
|
135
|
+
isChecked={itemSelected}
|
|
136
|
+
isDisabled={isCheckboxDisabled}
|
|
137
|
+
onCheckBoxToggled={onCheckboxToggled}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
107
141
|
)}
|
|
108
142
|
</div>
|
|
109
143
|
);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Data } from '../../../types';
|
|
3
|
+
import { Column } from '../List.model';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles the resizing logic of the columns
|
|
7
|
+
* @param columns The list of column definitions
|
|
8
|
+
* @param onColumnSizesChanged Callback that will be called when the column sizes change
|
|
9
|
+
* @returns an object containing the refs to the columns (add these to all column header elements)
|
|
10
|
+
* and the mouseDown handler (add this to the element that should be used to resize the column)
|
|
11
|
+
*/
|
|
12
|
+
export const useResize = <T extends Data>(
|
|
13
|
+
columns: Column<T>[],
|
|
14
|
+
onColumnSizesChanged: (columnSizes: string) => void,
|
|
15
|
+
): {
|
|
16
|
+
cols: {
|
|
17
|
+
ref: React.RefObject<HTMLTableCellElement>;
|
|
18
|
+
orgSize: string | undefined;
|
|
19
|
+
}[];
|
|
20
|
+
mouseDown: (index: number) => void;
|
|
21
|
+
} => {
|
|
22
|
+
const minCellWidth = 50;
|
|
23
|
+
|
|
24
|
+
const cols = useMemo(
|
|
25
|
+
() =>
|
|
26
|
+
[
|
|
27
|
+
...columns,
|
|
28
|
+
{ size: 'auto' }, // Last column for action buttons and checkbox, etc
|
|
29
|
+
].map((col) => ({
|
|
30
|
+
ref: React.createRef<HTMLTableCellElement>(),
|
|
31
|
+
orgSize: col.size,
|
|
32
|
+
})),
|
|
33
|
+
[columns],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const [activeIndex, setActiveIndex] = useState<number | undefined>(undefined);
|
|
37
|
+
const resizeStart = React.useRef<
|
|
38
|
+
{ mouse: number; width: number } | undefined
|
|
39
|
+
>(undefined);
|
|
40
|
+
|
|
41
|
+
const mouseMove = useCallback(
|
|
42
|
+
(e) => {
|
|
43
|
+
if (activeIndex !== undefined) {
|
|
44
|
+
const elem = cols[activeIndex].ref.current;
|
|
45
|
+
if (!elem) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!resizeStart.current) {
|
|
50
|
+
resizeStart.current = {
|
|
51
|
+
mouse: e.clientX,
|
|
52
|
+
width: elem.offsetWidth,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const start = resizeStart.current;
|
|
57
|
+
if (!start) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const gridColumns = cols.map((col, i) => {
|
|
62
|
+
if (!col.ref.current) {
|
|
63
|
+
return col.orgSize;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (i === activeIndex) {
|
|
67
|
+
const width = start.width + e.clientX - start.mouse;
|
|
68
|
+
if (width >= minCellWidth) {
|
|
69
|
+
return `${width}px`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return `${col.ref.current.offsetWidth}px`;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
onColumnSizesChanged(gridColumns.join(' '));
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[activeIndex, cols, onColumnSizesChanged],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const mouseDown = (index: number): void => {
|
|
82
|
+
setActiveIndex(index);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const removeListeners = useCallback(() => {
|
|
86
|
+
window.removeEventListener('mousemove', mouseMove);
|
|
87
|
+
window.removeEventListener('mouseup', removeListeners);
|
|
88
|
+
}, [mouseMove]);
|
|
89
|
+
|
|
90
|
+
const mouseUp = useCallback(() => {
|
|
91
|
+
setActiveIndex(undefined);
|
|
92
|
+
resizeStart.current = undefined;
|
|
93
|
+
removeListeners();
|
|
94
|
+
}, [setActiveIndex, removeListeners]);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (activeIndex !== undefined) {
|
|
98
|
+
window.addEventListener('mousemove', mouseMove);
|
|
99
|
+
window.addEventListener('mouseup', mouseUp);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return () => {
|
|
103
|
+
removeListeners();
|
|
104
|
+
};
|
|
105
|
+
}, [activeIndex, mouseMove, mouseUp, removeListeners]);
|
|
106
|
+
|
|
107
|
+
return { cols, mouseDown };
|
|
108
|
+
};
|
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
.columnsRoot {
|
|
4
4
|
padding: 1px 0px 1px 5px;
|
|
5
5
|
display: grid;
|
|
6
|
-
grid-template-columns: repeat(auto-fit, minmax(20px, 1fr));
|
|
7
|
-
grid-auto-rows: 50px;
|
|
8
|
-
column-gap: 5px;
|
|
9
6
|
justify-items: left;
|
|
10
7
|
align-items: center;
|
|
11
8
|
border-bottom: var(--explorer-list-row-border, $explorer-list-row-border);
|
|
@@ -30,6 +27,13 @@
|
|
|
30
27
|
}
|
|
31
28
|
}
|
|
32
29
|
|
|
30
|
+
.cellWrapper {
|
|
31
|
+
display: grid;
|
|
32
|
+
align-content: center;
|
|
33
|
+
width: 100%;
|
|
34
|
+
height: 100%;
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
.cell {
|
|
34
38
|
white-space: nowrap;
|
|
35
39
|
text-overflow: ellipsis;
|
|
@@ -47,15 +51,7 @@
|
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
.link {
|
|
50
|
-
display:
|
|
51
|
-
grid-template-columns: repeat(auto-fit, minmax(20px, 1fr));
|
|
52
|
-
column-gap: 5px;
|
|
53
|
-
|
|
54
|
-
width: 100%;
|
|
55
|
-
|
|
56
|
-
justify-items: left;
|
|
57
|
-
align-items: center;
|
|
58
|
-
|
|
54
|
+
display: contents;
|
|
59
55
|
color: inherit;
|
|
60
56
|
text-decoration: none;
|
|
61
57
|
}
|
|
@@ -51,7 +51,7 @@ describe('ListRow', () => {
|
|
|
51
51
|
<ListRow {...mockProps} columns={expectedColumns} />,
|
|
52
52
|
);
|
|
53
53
|
|
|
54
|
-
const actualColumns = wrapper.find('.
|
|
54
|
+
const actualColumns = wrapper.find('.cellWrapper');
|
|
55
55
|
|
|
56
56
|
// Check amount of rendered columns
|
|
57
57
|
expect(actualColumns).toHaveLength(expectedColumns.length);
|
|
@@ -87,7 +87,7 @@ describe('ListRow', () => {
|
|
|
87
87
|
/>,
|
|
88
88
|
);
|
|
89
89
|
|
|
90
|
-
const actualColumns = wrapper.find('.
|
|
90
|
+
const actualColumns = wrapper.find('.cellWrapper');
|
|
91
91
|
// Check amount of rendered columns
|
|
92
92
|
expect(actualColumns).toHaveLength(2);
|
|
93
93
|
|
|
@@ -119,21 +119,21 @@ describe('ListRow', () => {
|
|
|
119
119
|
// @ts-expect-error Typings to not match up
|
|
120
120
|
<ListRow {...mockProps} data={rowData} columns={expectedColumns} />,
|
|
121
121
|
);
|
|
122
|
-
const actualColumns = wrapper.find('.
|
|
122
|
+
const actualColumns = wrapper.find('.cellWrapper');
|
|
123
123
|
expect(actualColumns.at(0).text()).toBe('Changed: something');
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
it('shows empty field if a value required in a column is not present on the data object', () => {
|
|
127
127
|
const expectedColumns: Column<TestExplorerData>[] = [
|
|
128
128
|
// @ts-expect-error intentional bad property
|
|
129
|
-
{ propertyName: '
|
|
129
|
+
{ propertyName: "doesn't exist", size: '1fr', label: 'desc' },
|
|
130
130
|
];
|
|
131
131
|
|
|
132
132
|
const wrapper = shallow(
|
|
133
133
|
<ListRow {...mockProps} columns={expectedColumns} />,
|
|
134
134
|
);
|
|
135
135
|
|
|
136
|
-
const actualColumns = wrapper.find('.
|
|
136
|
+
const actualColumns = wrapper.find('.cellWrapper');
|
|
137
137
|
|
|
138
138
|
// Check amount of rendered columns
|
|
139
139
|
expect(actualColumns).toHaveLength(1);
|
|
@@ -514,22 +514,6 @@ describe('ListRow', () => {
|
|
|
514
514
|
|
|
515
515
|
expect(link.exists()).toBe(false);
|
|
516
516
|
});
|
|
517
|
-
|
|
518
|
-
it('receives list row height', () => {
|
|
519
|
-
const height = '55px';
|
|
520
|
-
const wrapper = shallow(
|
|
521
|
-
<ListRow
|
|
522
|
-
{...mockProps}
|
|
523
|
-
onItemClicked="http://this.is.a.test.url"
|
|
524
|
-
selectionMode={ListSelectMode.None}
|
|
525
|
-
rowHeight={height}
|
|
526
|
-
/>,
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
const link = wrapper.find(Link);
|
|
530
|
-
|
|
531
|
-
expect(link.prop('style')?.gridTemplateRows).toBe(height);
|
|
532
|
-
});
|
|
533
517
|
});
|
|
534
518
|
|
|
535
519
|
describe('tooltip', () => {
|