@dile/ui 3.11.0 → 3.12.2
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/components/data-grid/__screenshots__/data-grid.spec.js/dile-data-grid-dispatches-item-checkbox-changed-and-dile-data-grid-item-selected-when-a-checkbox-is-toggled-1.png +0 -0
- package/components/data-grid/data-grid.spec.js +129 -1
- package/components/data-grid/src/DileDataGrid.js +209 -16
- package/components/order-switch/order-switch.spec.js +26 -0
- package/components/order-switch/src/DileOrderSwitch.js +11 -2
- package/mixins/close-on-esc-pressed/src/DileCloseOnEscPressed.js +1 -1
- package/package.json +2 -2
|
@@ -45,6 +45,7 @@ describe('dile-data-grid', () => {
|
|
|
45
45
|
emptyMessage: 'Nothing found',
|
|
46
46
|
});
|
|
47
47
|
const emptyCell = el.shadowRoot.querySelector('.empty-cell');
|
|
48
|
+
expect(emptyCell).toBeTruthy();
|
|
48
49
|
expect(emptyCell.textContent.trim()).toBe('Nothing found');
|
|
49
50
|
});
|
|
50
51
|
|
|
@@ -276,7 +277,7 @@ describe('dile-data-grid', () => {
|
|
|
276
277
|
expect(tds[2].classList.contains('sticky-right')).toBe(true);
|
|
277
278
|
});
|
|
278
279
|
|
|
279
|
-
it('applies sticky-left to the first column when stickyFirstColumn property is set', async () => {
|
|
280
|
+
it('applies sticky-left to the first column when stickyFirstColumn property is set without selection', async () => {
|
|
280
281
|
const el = await createDataGrid({
|
|
281
282
|
columns: sampleColumns,
|
|
282
283
|
items: sampleItems,
|
|
@@ -290,4 +291,131 @@ describe('dile-data-grid', () => {
|
|
|
290
291
|
expect(tds[0].classList.contains('sticky-left')).toBe(true);
|
|
291
292
|
expect(ths[1].classList.contains('sticky-left')).toBe(false);
|
|
292
293
|
});
|
|
294
|
+
|
|
295
|
+
it('handles sticky columns properly when selectable is true and stickyFirstColumn is true', async () => {
|
|
296
|
+
const el = await createDataGrid({
|
|
297
|
+
columns: sampleColumns,
|
|
298
|
+
items: sampleItems,
|
|
299
|
+
selectable: true,
|
|
300
|
+
stickyFirstColumn: true,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const ths = el.shadowRoot.querySelectorAll('th');
|
|
304
|
+
const tds = el.shadowRoot.querySelectorAll('tbody tr td');
|
|
305
|
+
|
|
306
|
+
// th[0] is selection cell -> sticky-left
|
|
307
|
+
expect(ths[0].classList.contains('selection-cell')).toBe(true);
|
|
308
|
+
expect(ths[0].classList.contains('sticky-left')).toBe(true);
|
|
309
|
+
expect(tds[0].classList.contains('selection-cell')).toBe(true);
|
|
310
|
+
expect(tds[0].classList.contains('sticky-left')).toBe(true);
|
|
311
|
+
|
|
312
|
+
// th[1] is first data column -> sticky-left-after-selection
|
|
313
|
+
expect(ths[1].classList.contains('sticky-left-after-selection')).toBe(true);
|
|
314
|
+
expect(tds[1].classList.contains('sticky-left-after-selection')).toBe(true);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('renders selection column when selectable is true and checks items matching selectedIds', async () => {
|
|
318
|
+
const el = await createDataGrid({
|
|
319
|
+
columns: sampleColumns,
|
|
320
|
+
items: sampleItems,
|
|
321
|
+
selectable: true,
|
|
322
|
+
selectedIds: [2],
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const ths = el.shadowRoot.querySelectorAll('th');
|
|
326
|
+
expect(ths.length).toBe(5);
|
|
327
|
+
expect(ths[0].classList.contains('selection-cell')).toBe(true);
|
|
328
|
+
|
|
329
|
+
const rows = el.shadowRoot.querySelectorAll('tbody tr');
|
|
330
|
+
expect(rows.length).toBe(3);
|
|
331
|
+
|
|
332
|
+
const checkboxes = el.shadowRoot.querySelectorAll('dile-checkbox');
|
|
333
|
+
expect(checkboxes.length).toBe(3);
|
|
334
|
+
expect(checkboxes[0].checked).toBe(false);
|
|
335
|
+
expect(checkboxes[1].checked).toBe(true);
|
|
336
|
+
expect(checkboxes[2].checked).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('dispatches item-checkbox-changed and dile-data-grid-item-selected when a checkbox is toggled', async () => {
|
|
340
|
+
const el = await createDataGrid({
|
|
341
|
+
columns: sampleColumns,
|
|
342
|
+
items: sampleItems,
|
|
343
|
+
selectable: true,
|
|
344
|
+
selectedIds: [],
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
let itemCheckboxDetail = null;
|
|
348
|
+
let gridItemSelectedDetail = null;
|
|
349
|
+
|
|
350
|
+
el.addEventListener('item-checkbox-changed', (e) => {
|
|
351
|
+
itemCheckboxDetail = e.detail;
|
|
352
|
+
});
|
|
353
|
+
el.addEventListener('dile-data-grid-item-selected', (e) => {
|
|
354
|
+
gridItemSelectedDetail = e.detail;
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const firstCheckbox = el.shadowRoot.querySelectorAll('dile-checkbox')[0];
|
|
358
|
+
await firstCheckbox.updateComplete;
|
|
359
|
+
const clickTarget = firstCheckbox.shadowRoot?.querySelector('div') || firstCheckbox;
|
|
360
|
+
clickTarget.click();
|
|
361
|
+
await el.updateComplete;
|
|
362
|
+
|
|
363
|
+
expect(itemCheckboxDetail).toBeTruthy();
|
|
364
|
+
expect(itemCheckboxDetail.checked).toBe(true);
|
|
365
|
+
expect(itemCheckboxDetail.itemId).toBe(1);
|
|
366
|
+
expect(itemCheckboxDetail.index).toBe(0);
|
|
367
|
+
|
|
368
|
+
expect(gridItemSelectedDetail).toBeTruthy();
|
|
369
|
+
expect(gridItemSelectedDetail.checked).toBe(true);
|
|
370
|
+
expect(gridItemSelectedDetail.itemId).toBe(1);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('applies rowClass custom class to table rows', async () => {
|
|
374
|
+
const el = await createDataGrid({
|
|
375
|
+
columns: sampleColumns,
|
|
376
|
+
items: [
|
|
377
|
+
{ id: 1, name: 'Carlos', deleted_at: null },
|
|
378
|
+
{ id: 2, name: 'Ana', deleted_at: '2026-01-01' },
|
|
379
|
+
],
|
|
380
|
+
rowClass: (row) => (row.deleted_at ? 'is-deleted' : ''),
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const rows = el.shadowRoot.querySelectorAll('tbody tr');
|
|
384
|
+
expect(rows[0].classList.contains('is-deleted')).toBe(false);
|
|
385
|
+
expect(rows[1].classList.contains('is-deleted')).toBe(true);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('supports custom computeRowId function', async () => {
|
|
389
|
+
const customItems = [
|
|
390
|
+
{ uuid: 'abc-1', name: 'Carlos' },
|
|
391
|
+
{ uuid: 'xyz-2', name: 'Ana' },
|
|
392
|
+
];
|
|
393
|
+
const el = await createDataGrid({
|
|
394
|
+
columns: [{ field: 'name', header: 'Name' }],
|
|
395
|
+
items: customItems,
|
|
396
|
+
selectable: true,
|
|
397
|
+
computeRowId: (row) => row.uuid,
|
|
398
|
+
selectedIds: ['xyz-2'],
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const checkboxes = el.shadowRoot.querySelectorAll('dile-checkbox');
|
|
402
|
+
expect(checkboxes[0].checked).toBe(false);
|
|
403
|
+
expect(checkboxes[1].checked).toBe(true);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('reads sortMode and emptyMessage from their kebab-case HTML attributes', async () => {
|
|
407
|
+
document.body.innerHTML = `
|
|
408
|
+
<dile-data-grid sort-mode="external" empty-message="Nothing here"></dile-data-grid>
|
|
409
|
+
`;
|
|
410
|
+
const el = document.body.querySelector('dile-data-grid');
|
|
411
|
+
await el.updateComplete;
|
|
412
|
+
|
|
413
|
+
expect(el.sortMode).toBe('external');
|
|
414
|
+
expect(el.emptyMessage).toBe('Nothing here');
|
|
415
|
+
|
|
416
|
+
el.items = [];
|
|
417
|
+
await el.updateComplete;
|
|
418
|
+
const emptyCell = el.shadowRoot.querySelector('.empty-cell');
|
|
419
|
+
expect(emptyCell.textContent.trim()).toBe('Nothing here');
|
|
420
|
+
});
|
|
293
421
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { html, css, LitElement } from 'lit';
|
|
2
2
|
import { arrowDropUpIcon, arrowDropDownIcon } from '@dile/icons';
|
|
3
|
+
import '../../checkbox/checkbox.js';
|
|
3
4
|
|
|
4
5
|
export class DileDataGrid extends LitElement {
|
|
5
6
|
static get properties() {
|
|
@@ -17,7 +18,7 @@ export class DileDataGrid extends LitElement {
|
|
|
17
18
|
sortDirection: { type: String },
|
|
18
19
|
|
|
19
20
|
/** Mode of sorting: 'client' (sorts data internally) or 'external' (only emits event) */
|
|
20
|
-
sortMode: { type: String },
|
|
21
|
+
sortMode: { type: String, attribute: 'sort-mode' },
|
|
21
22
|
|
|
22
23
|
/** Responsive mode: 'auto' (container query card switch), 'cards' (always card), or 'scroll' (table scroll) */
|
|
23
24
|
responsiveMode: { type: String, attribute: 'responsive-mode', reflect: true },
|
|
@@ -26,13 +27,25 @@ export class DileDataGrid extends LitElement {
|
|
|
26
27
|
stickyFirstColumn: { type: Boolean, attribute: 'sticky-first-column' },
|
|
27
28
|
|
|
28
29
|
/** Message to display when there are no items */
|
|
29
|
-
emptyMessage: { type: String },
|
|
30
|
+
emptyMessage: { type: String, attribute: 'empty-message' },
|
|
30
31
|
|
|
31
32
|
/** Unique property name for identifying rows */
|
|
32
33
|
rowIdField: { type: String },
|
|
33
34
|
|
|
34
35
|
/** Enables striped rows styling */
|
|
35
36
|
striped: { type: Boolean },
|
|
37
|
+
|
|
38
|
+
/** Enables selection checkboxes column for batch operations */
|
|
39
|
+
selectable: { type: Boolean },
|
|
40
|
+
|
|
41
|
+
/** Array of selected row IDs */
|
|
42
|
+
selectedIds: { type: Array },
|
|
43
|
+
|
|
44
|
+
/** Optional function to compute row ID from a row object: (row) => string|number */
|
|
45
|
+
computeRowId: { type: Function },
|
|
46
|
+
|
|
47
|
+
/** Optional function to compute custom CSS class for a row: (row, index) => string */
|
|
48
|
+
rowClass: { type: Function },
|
|
36
49
|
};
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -48,6 +61,10 @@ export class DileDataGrid extends LitElement {
|
|
|
48
61
|
this.emptyMessage = 'No data available';
|
|
49
62
|
this.rowIdField = 'id';
|
|
50
63
|
this.striped = false;
|
|
64
|
+
this.selectable = false;
|
|
65
|
+
this.selectedIds = [];
|
|
66
|
+
this.computeRowId = null;
|
|
67
|
+
this.rowClass = null;
|
|
51
68
|
}
|
|
52
69
|
|
|
53
70
|
static get styles() {
|
|
@@ -149,6 +166,12 @@ export class DileDataGrid extends LitElement {
|
|
|
149
166
|
background-color: var(--dile-data-grid-row-hover-background-color, #f8fafc);
|
|
150
167
|
}
|
|
151
168
|
|
|
169
|
+
tbody tr.is-deleted,
|
|
170
|
+
tbody tr.deleted {
|
|
171
|
+
opacity: var(--dile-data-grid-deleted-opacity, 0.6);
|
|
172
|
+
background-color: var(--dile-data-grid-deleted-background-color, rgba(239, 68, 68, 0.04));
|
|
173
|
+
}
|
|
174
|
+
|
|
152
175
|
td {
|
|
153
176
|
padding: var(--dile-data-grid-row-padding, 0.75rem 1rem);
|
|
154
177
|
color: var(--dile-data-grid-row-color, #1e293b);
|
|
@@ -175,7 +198,42 @@ export class DileDataGrid extends LitElement {
|
|
|
175
198
|
font-size: var(--dile-data-grid-empty-font-size, 0.875rem);
|
|
176
199
|
}
|
|
177
200
|
|
|
201
|
+
/* Selection Checkbox Cell */
|
|
202
|
+
th.selection-cell,
|
|
203
|
+
td.selection-cell {
|
|
204
|
+
width: var(--dile-data-grid-selection-width, 44px);
|
|
205
|
+
min-width: var(--dile-data-grid-selection-width, 44px);
|
|
206
|
+
max-width: var(--dile-data-grid-selection-width, 44px);
|
|
207
|
+
text-align: center;
|
|
208
|
+
padding-left: 0.5rem;
|
|
209
|
+
padding-right: 0.5rem;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
td.selection-cell dile-checkbox {
|
|
213
|
+
display: inline-flex;
|
|
214
|
+
vertical-align: middle;
|
|
215
|
+
--dile-checkbox-unchecked-color: var(--dile-data-grid-checkbox-unchecked-color, #888);
|
|
216
|
+
}
|
|
217
|
+
|
|
178
218
|
/* Sticky Columns */
|
|
219
|
+
th.selection-cell.sticky-left,
|
|
220
|
+
td.selection-cell.sticky-left {
|
|
221
|
+
position: sticky;
|
|
222
|
+
left: 0;
|
|
223
|
+
z-index: 2;
|
|
224
|
+
background-color: var(--dile-data-grid-sticky-background-color, var(--dile-data-grid-background-color, #ffffff));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
th.selection-cell.sticky-left {
|
|
228
|
+
z-index: 3;
|
|
229
|
+
background-color: var(--dile-data-grid-sticky-header-background-color, var(--dile-data-grid-header-background-color, #f8fafc));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
th.selection-cell.sticky-left.has-shadow,
|
|
233
|
+
td.selection-cell.sticky-left.has-shadow {
|
|
234
|
+
box-shadow: var(--dile-data-grid-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, 0.12));
|
|
235
|
+
}
|
|
236
|
+
|
|
179
237
|
th.sticky-left,
|
|
180
238
|
td.sticky-left {
|
|
181
239
|
position: sticky;
|
|
@@ -190,6 +248,20 @@ export class DileDataGrid extends LitElement {
|
|
|
190
248
|
background-color: var(--dile-data-grid-sticky-header-background-color, var(--dile-data-grid-header-background-color, #f8fafc));
|
|
191
249
|
}
|
|
192
250
|
|
|
251
|
+
th.sticky-left-after-selection,
|
|
252
|
+
td.sticky-left-after-selection {
|
|
253
|
+
position: sticky;
|
|
254
|
+
left: var(--dile-data-grid-selection-width, 44px);
|
|
255
|
+
z-index: 2;
|
|
256
|
+
background-color: var(--dile-data-grid-sticky-background-color, var(--dile-data-grid-background-color, #ffffff));
|
|
257
|
+
box-shadow: var(--dile-data-grid-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, 0.12));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
th.sticky-left-after-selection {
|
|
261
|
+
z-index: 3;
|
|
262
|
+
background-color: var(--dile-data-grid-sticky-header-background-color, var(--dile-data-grid-header-background-color, #f8fafc));
|
|
263
|
+
}
|
|
264
|
+
|
|
193
265
|
th.sticky-right,
|
|
194
266
|
td.sticky-right {
|
|
195
267
|
position: sticky;
|
|
@@ -206,12 +278,18 @@ export class DileDataGrid extends LitElement {
|
|
|
206
278
|
|
|
207
279
|
:host([striped]) tbody tr:nth-child(even) td.sticky-left,
|
|
208
280
|
.striped tbody tr:nth-child(even) td.sticky-left,
|
|
281
|
+
:host([striped]) tbody tr:nth-child(even) td.sticky-left-after-selection,
|
|
282
|
+
.striped tbody tr:nth-child(even) td.sticky-left-after-selection,
|
|
283
|
+
:host([striped]) tbody tr:nth-child(even) td.selection-cell.sticky-left,
|
|
284
|
+
.striped tbody tr:nth-child(even) td.selection-cell.sticky-left,
|
|
209
285
|
:host([striped]) tbody tr:nth-child(even) td.sticky-right,
|
|
210
286
|
.striped tbody tr:nth-child(even) td.sticky-right {
|
|
211
287
|
background-color: var(--dile-data-grid-sticky-striped-background-color, var(--dile-data-grid-row-striped-background-color, #fcfdfe));
|
|
212
288
|
}
|
|
213
289
|
|
|
214
290
|
tbody tr:hover td.sticky-left,
|
|
291
|
+
tbody tr:hover td.sticky-left-after-selection,
|
|
292
|
+
tbody tr:hover td.selection-cell.sticky-left,
|
|
215
293
|
tbody tr:hover td.sticky-right {
|
|
216
294
|
background-color: var(--dile-data-grid-row-hover-background-color, #f8fafc);
|
|
217
295
|
}
|
|
@@ -264,6 +342,20 @@ export class DileDataGrid extends LitElement {
|
|
|
264
342
|
box-shadow: none;
|
|
265
343
|
}
|
|
266
344
|
|
|
345
|
+
:host([responsive-mode="cards"]) td.selection-cell,
|
|
346
|
+
:host([responsive-mode="card"]) td.selection-cell {
|
|
347
|
+
justify-content: flex-start;
|
|
348
|
+
padding-top: 0.35rem;
|
|
349
|
+
padding-bottom: 0.35rem;
|
|
350
|
+
width: 100%;
|
|
351
|
+
max-width: 100%;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
:host([responsive-mode="cards"]) td.selection-cell::before,
|
|
355
|
+
:host([responsive-mode="card"]) td.selection-cell::before {
|
|
356
|
+
display: none !important;
|
|
357
|
+
}
|
|
358
|
+
|
|
267
359
|
:host([responsive-mode="cards"]) td:last-child,
|
|
268
360
|
:host([responsive-mode="card"]) td:last-child {
|
|
269
361
|
border-bottom: none;
|
|
@@ -352,6 +444,18 @@ export class DileDataGrid extends LitElement {
|
|
|
352
444
|
box-shadow: none;
|
|
353
445
|
}
|
|
354
446
|
|
|
447
|
+
:host(:not([responsive-mode="scroll"]):not([responsive-mode="cards"]):not([responsive-mode="card"])) td.selection-cell {
|
|
448
|
+
justify-content: flex-start;
|
|
449
|
+
padding-top: 0.35rem;
|
|
450
|
+
padding-bottom: 0.35rem;
|
|
451
|
+
width: 100%;
|
|
452
|
+
max-width: 100%;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
:host(:not([responsive-mode="scroll"]):not([responsive-mode="cards"]):not([responsive-mode="card"])) td.selection-cell::before {
|
|
456
|
+
display: none !important;
|
|
457
|
+
}
|
|
458
|
+
|
|
355
459
|
:host(:not([responsive-mode="scroll"]):not([responsive-mode="cards"]):not([responsive-mode="card"])) td:last-child {
|
|
356
460
|
border-bottom: none;
|
|
357
461
|
}
|
|
@@ -430,8 +534,32 @@ export class DileDataGrid extends LitElement {
|
|
|
430
534
|
});
|
|
431
535
|
}
|
|
432
536
|
|
|
433
|
-
|
|
434
|
-
|
|
537
|
+
_getRowId(row) {
|
|
538
|
+
if (!row) return undefined;
|
|
539
|
+
if (typeof this.computeRowId === 'function') {
|
|
540
|
+
return this.computeRowId(row);
|
|
541
|
+
}
|
|
542
|
+
return row[this.rowIdField];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
_isRowSelected(row) {
|
|
546
|
+
if (!this.selectedIds || !Array.isArray(this.selectedIds)) {
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
const rowId = this._getRowId(row);
|
|
550
|
+
const stringIds = this.selectedIds.map(String);
|
|
551
|
+
return stringIds.includes(String(rowId));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
_getStickyLeftClass(column, colIndex) {
|
|
555
|
+
const isSticky = column.sticky === true || column.sticky === 'left' || (this.stickyFirstColumn && colIndex === 0);
|
|
556
|
+
if (!isSticky) {
|
|
557
|
+
return '';
|
|
558
|
+
}
|
|
559
|
+
if (this.selectable) {
|
|
560
|
+
return 'sticky-left-after-selection';
|
|
561
|
+
}
|
|
562
|
+
return 'sticky-left';
|
|
435
563
|
}
|
|
436
564
|
|
|
437
565
|
_isStickyRight(column) {
|
|
@@ -441,12 +569,14 @@ export class DileDataGrid extends LitElement {
|
|
|
441
569
|
render() {
|
|
442
570
|
const items = this.displayedItems;
|
|
443
571
|
const cols = this.columns || [];
|
|
572
|
+
const totalCols = (cols.length || 1) + (this.selectable ? 1 : 0);
|
|
444
573
|
|
|
445
574
|
return html`
|
|
446
575
|
<div class="table-container ${this.striped ? 'striped' : ''}">
|
|
447
576
|
<table>
|
|
448
577
|
<thead>
|
|
449
578
|
<tr>
|
|
579
|
+
${this.selectable ? this._renderSelectionHeaderCell() : ''}
|
|
450
580
|
${cols.map((col, colIndex) => this._renderHeaderCell(col, colIndex))}
|
|
451
581
|
</tr>
|
|
452
582
|
</thead>
|
|
@@ -454,7 +584,7 @@ export class DileDataGrid extends LitElement {
|
|
|
454
584
|
${items.length === 0
|
|
455
585
|
? html`
|
|
456
586
|
<tr class="empty-row">
|
|
457
|
-
<td class="empty-cell" colspan="${
|
|
587
|
+
<td class="empty-cell" colspan="${totalCols}" data-label="">
|
|
458
588
|
${this.emptyMessage}
|
|
459
589
|
</td>
|
|
460
590
|
</tr>
|
|
@@ -466,15 +596,23 @@ export class DileDataGrid extends LitElement {
|
|
|
466
596
|
`;
|
|
467
597
|
}
|
|
468
598
|
|
|
599
|
+
_renderSelectionHeaderCell() {
|
|
600
|
+
const isSticky = this.stickyFirstColumn;
|
|
601
|
+
const hasShadow = !this.columns.some((col, idx) => (col.sticky === true || col.sticky === 'left' || (this.stickyFirstColumn && idx === 0)));
|
|
602
|
+
const stickyClass = isSticky ? `sticky-left ${hasShadow ? 'has-shadow' : ''}` : '';
|
|
603
|
+
|
|
604
|
+
return html`
|
|
605
|
+
<th class="selection-cell ${stickyClass}" aria-label="Select"></th>
|
|
606
|
+
`;
|
|
607
|
+
}
|
|
608
|
+
|
|
469
609
|
_renderHeaderCell(column, colIndex) {
|
|
470
610
|
const isSorted = this.sortField === column.field && !!this.sortDirection;
|
|
471
611
|
const alignClass = column.align ? `align-${column.align}` : 'align-left';
|
|
472
612
|
const sortableClass = column.sortable ? 'sortable' : '';
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
? 'sticky-right'
|
|
477
|
-
: '';
|
|
613
|
+
const stickyLeftClass = this._getStickyLeftClass(column, colIndex);
|
|
614
|
+
const stickyRightClass = this._isStickyRight(column) ? 'sticky-right' : '';
|
|
615
|
+
const stickyClass = `${stickyLeftClass} ${stickyRightClass}`.trim();
|
|
478
616
|
const styleWidth = column.width ? `width: ${column.width};` : '';
|
|
479
617
|
|
|
480
618
|
return html`
|
|
@@ -508,22 +646,77 @@ export class DileDataGrid extends LitElement {
|
|
|
508
646
|
}
|
|
509
647
|
|
|
510
648
|
_renderRow(row, rowIndex, columns) {
|
|
649
|
+
let rowClassName = '';
|
|
650
|
+
if (typeof this.rowClass === 'function') {
|
|
651
|
+
rowClassName = this.rowClass(row, rowIndex) || '';
|
|
652
|
+
}
|
|
653
|
+
|
|
511
654
|
return html`
|
|
512
|
-
<tr @click=${(e) => this._handleRowClick(row, rowIndex, e)}>
|
|
655
|
+
<tr class="${rowClassName}" @click=${(e) => this._handleRowClick(row, rowIndex, e)}>
|
|
656
|
+
${this.selectable ? this._renderSelectionCell(row, rowIndex) : ''}
|
|
513
657
|
${columns.map((col, colIndex) => this._renderCell(row, rowIndex, col, colIndex))}
|
|
514
658
|
</tr>
|
|
515
659
|
`;
|
|
516
660
|
}
|
|
517
661
|
|
|
662
|
+
_renderSelectionCell(row, rowIndex) {
|
|
663
|
+
const isSticky = this.stickyFirstColumn;
|
|
664
|
+
const hasShadow = !this.columns.some((col, idx) => (col.sticky === true || col.sticky === 'left' || (this.stickyFirstColumn && idx === 0)));
|
|
665
|
+
const stickyClass = isSticky ? `sticky-left ${hasShadow ? 'has-shadow' : ''}` : '';
|
|
666
|
+
const isSelected = this._isRowSelected(row);
|
|
667
|
+
|
|
668
|
+
return html`
|
|
669
|
+
<td
|
|
670
|
+
class="selection-cell ${stickyClass}"
|
|
671
|
+
data-label=""
|
|
672
|
+
@click=${(e) => e.stopPropagation()}
|
|
673
|
+
>
|
|
674
|
+
<dile-checkbox
|
|
675
|
+
?checked=${isSelected}
|
|
676
|
+
@dile-checkbox-changed=${(e) => this._handleCheckboxChange(row, rowIndex, e)}
|
|
677
|
+
></dile-checkbox>
|
|
678
|
+
</td>
|
|
679
|
+
`;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
_handleCheckboxChange(row, rowIndex, event) {
|
|
683
|
+
const itemId = this._getRowId(row);
|
|
684
|
+
const checked = Boolean(event.detail.checked);
|
|
685
|
+
|
|
686
|
+
this.dispatchEvent(
|
|
687
|
+
new CustomEvent('item-checkbox-changed', {
|
|
688
|
+
bubbles: true,
|
|
689
|
+
composed: true,
|
|
690
|
+
detail: {
|
|
691
|
+
checked,
|
|
692
|
+
itemId,
|
|
693
|
+
row,
|
|
694
|
+
index: rowIndex,
|
|
695
|
+
},
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
this.dispatchEvent(
|
|
700
|
+
new CustomEvent('dile-data-grid-item-selected', {
|
|
701
|
+
bubbles: true,
|
|
702
|
+
composed: true,
|
|
703
|
+
detail: {
|
|
704
|
+
checked,
|
|
705
|
+
itemId,
|
|
706
|
+
row,
|
|
707
|
+
index: rowIndex,
|
|
708
|
+
},
|
|
709
|
+
})
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
518
713
|
_renderCell(row, rowIndex, column, colIndex) {
|
|
519
714
|
const alignClass = column.align ? `align-${column.align}` : 'align-left';
|
|
520
715
|
const hideOnCardClass = column.hideOnCard ? 'hide-on-card' : '';
|
|
521
716
|
const noCardLabelClass = column.hideCardLabel ? 'no-card-label' : '';
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
? 'sticky-right'
|
|
526
|
-
: '';
|
|
717
|
+
const stickyLeftClass = this._getStickyLeftClass(column, colIndex);
|
|
718
|
+
const stickyRightClass = this._isStickyRight(column) ? 'sticky-right' : '';
|
|
719
|
+
const stickyClass = `${stickyLeftClass} ${stickyRightClass}`.trim();
|
|
527
720
|
const label = column.hideCardLabel ? '' : (column.header || column.field || '');
|
|
528
721
|
|
|
529
722
|
let content;
|
|
@@ -28,4 +28,30 @@ describe('dile-order-switch', () => {
|
|
|
28
28
|
|
|
29
29
|
expect(el.value).toBe('desc');
|
|
30
30
|
});
|
|
31
|
+
|
|
32
|
+
it('clicking the label re-announces the current value without toggling when not selected', async () => {
|
|
33
|
+
const el = await renderOrderSwitch('<dile-order-switch name="name" label="Name" value="asc"></dile-order-switch>');
|
|
34
|
+
|
|
35
|
+
let receivedDetail = null;
|
|
36
|
+
el.addEventListener('element-changed', (e) => { receivedDetail = e.detail; });
|
|
37
|
+
|
|
38
|
+
el.shadowRoot.querySelector('span').click();
|
|
39
|
+
await el.updateComplete;
|
|
40
|
+
|
|
41
|
+
expect(el.value).toBe('asc'); // unchanged
|
|
42
|
+
expect(receivedDetail).toEqual({ name: 'name', value: 'asc' });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('clicking the label toggles the order when selected', async () => {
|
|
46
|
+
const el = await renderOrderSwitch('<dile-order-switch name="name" label="Name" value="asc" selected></dile-order-switch>');
|
|
47
|
+
|
|
48
|
+
let receivedDetail = null;
|
|
49
|
+
el.addEventListener('element-changed', (e) => { receivedDetail = e.detail; });
|
|
50
|
+
|
|
51
|
+
el.shadowRoot.querySelector('span').click();
|
|
52
|
+
await el.updateComplete;
|
|
53
|
+
|
|
54
|
+
expect(el.value).toBe('desc'); // toggled
|
|
55
|
+
expect(receivedDetail).toEqual({ name: 'name', value: 'desc' });
|
|
56
|
+
});
|
|
31
57
|
});
|
|
@@ -10,12 +10,18 @@ export class DileOrderSwitch extends DileEmmitChange(LitElement) {
|
|
|
10
10
|
value: { type: String },
|
|
11
11
|
name: { type: String },
|
|
12
12
|
label: { type: String },
|
|
13
|
+
/** Marks this switch as the one currently in effect, e.g. inside a group of
|
|
14
|
+
* order options where only one applies at a time. When true, clicking the
|
|
15
|
+
* label toggles the order (same as clicking the icon) instead of just
|
|
16
|
+
* re-announcing the current value. */
|
|
17
|
+
selected: { type: Boolean },
|
|
13
18
|
};
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
constructor() {
|
|
17
22
|
super();
|
|
18
23
|
this.value = "asc";
|
|
24
|
+
this.selected = false;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
static get styles() {
|
|
@@ -69,8 +75,11 @@ export class DileOrderSwitch extends DileEmmitChange(LitElement) {
|
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
onlyChange() {
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
if (this.selected) {
|
|
79
|
+
this.toggle();
|
|
80
|
+
} else {
|
|
81
|
+
this.emmitChange();
|
|
82
|
+
}
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
toggle() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.2",
|
|
4
4
|
"description": "UI Core components from dile-components.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "27dbd2411bd5856021209c49da6aed285c3fae28"
|
|
32
32
|
}
|