Skip to content

Commit 83ab8ca

Browse files
committed
Merge branch 'main' into feature-soql-dml-rows-columns-on-calltree
2 parents 48a4cbe + f5dbd47 commit 83ab8ca

6 files changed

Lines changed: 188 additions & 29 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Column totals for Grouped rows will now be shown inside the groups instead of on a seperate row ([#583][#583]).
1313
- Table actions: Copy to clipboard and Export to CSV directly from the button above the Analysis and Database table ([#589]).
14+
- Sort grouped rows by clicking column headers ([#592]).
15+
- Tri state on Group name column will sort by number of items in the group
1416
- Replace the Rows column on the Call Tree with two seperate columns, DML rows and SOQL rows ([#93]).
1517
- Replace Rows count on the Timeline tooltip with two new counts, DML rows and SOQL rows ([#93]).
1618

@@ -390,6 +392,7 @@ Skipped due to adopting odd numbering for pre releases and even number for relea
390392
[#583]: https://github.com/certinia/debug-log-analyzer/issues/583
391393
[#589]: https://github.com/certinia/debug-log-analyzer/issues/589
392394
[#590]: https://github.com/certinia/debug-log-analyzer/issues/590
395+
[#592]: https://github.com/certinia/debug-log-analyzer/issues/592
393396
[#93]: https://github.com/certinia/debug-log-analyzer/issues/93
394397

395398
<!-- 1.16.1 -->

log-viewer/modules/components/analysis-view/AnalysisView.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import { Tabulator, type RowComponent } from 'tabulator-tables';
1919
import { isVisible } from '../../Util.js';
2020
import NumberAccessor from '../../datagrid/dataaccessor/Number.js';
2121
import { progressFormatter } from '../../datagrid/format/Progress.js';
22-
import { GroupCalcs } from '../../datagrid/group-calcs/GroupCalcs.js';
22+
import { GroupCalcs } from '../../datagrid/groups/GroupCalcs.js';
23+
import { GroupSort } from '../../datagrid/groups/GroupSort.js';
2324
import * as CommonModules from '../../datagrid/module/CommonModules.js';
2425
import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js';
2526
import { RowNavigation } from '../../datagrid/module/RowNavigation.js';
@@ -188,8 +189,10 @@ export class AnalysisView extends LitElement {
188189
_groupBy(event: Event) {
189190
const target = event.target as HTMLInputElement;
190191
const fieldName = target.value.toLowerCase();
191-
192-
this.analysisTable?.setGroupBy(fieldName !== 'none' ? fieldName : '');
192+
if (this.analysisTable) {
193+
//@ts-expect-error This is a custom function added in the GroupSort custom module
194+
this.analysisTable?.setSortedGroupBy(fieldName !== 'none' ? fieldName : '');
195+
}
193196
}
194197

195198
_appendTableWhenVisible() {
@@ -254,7 +257,7 @@ export class AnalysisView extends LitElement {
254257
const metricList = groupMetrics(rootMethod);
255258

256259
Tabulator.registerModule(Object.values(CommonModules));
257-
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs]);
260+
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs, GroupSort]);
258261
this.analysisTable = new Tabulator(this._tableWrapper, {
259262
rowKeyboardNavigation: true,
260263
selectableRows: 'highlight',
@@ -292,6 +295,7 @@ export class AnalysisView extends LitElement {
292295
height: '100%',
293296
maxHeight: '100%',
294297
groupCalcs: true,
298+
groupSort: true,
295299
groupClosedShowCalcs: true,
296300
groupStartOpen: false,
297301
groupToggleElement: 'header',
@@ -325,6 +329,7 @@ export class AnalysisView extends LitElement {
325329
formatter: 'textarea',
326330
headerSortStartingDir: 'asc',
327331
sorter: 'string',
332+
headerSortTristate: true,
328333
cssClass: 'datagrid-code-text',
329334
bottomCalc: () => {
330335
return 'Total';

log-viewer/modules/components/database-view/DMLView.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import { customElement, property, state } from 'lit/decorators.js';
1111
import { Tabulator, type GroupComponent, type RowComponent } from 'tabulator-tables';
1212

1313
// tabulator custom modules
14+
import { GroupCalcs } from '../../datagrid/groups/GroupCalcs.js';
15+
import { GroupSort } from '../../datagrid/groups/GroupSort.js';
1416
import * as CommonModules from '../../datagrid/module/CommonModules.js';
1517
import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js';
1618
import { RowNavigation } from '../../datagrid/module/RowNavigation.js';
17-
import { GroupCalcs } from '.././../datagrid/group-calcs/GroupCalcs.js';
1819
import { Find, formatter } from '../calltree-view/module/Find.js';
1920

2021
// tabulator others
@@ -161,7 +162,8 @@ export class DMLView extends LitElement {
161162

162163
_dmlGroupBy(event: Event) {
163164
const target = event.target as HTMLInputElement;
164-
this.dmlTable?.setGroupBy(target.checked ? 'dml' : '');
165+
//@ts-expect-error This is a custom function added in the GroupSort custom module
166+
this.dmlTable?.setSortedGroupBy(target.checked ? 'dml' : '');
165167
}
166168

167169
get _dmlTableWrapper(): HTMLDivElement | null {
@@ -181,7 +183,13 @@ export class DMLView extends LitElement {
181183
this.dmlLines = dbAccess.getDMLLines() || [];
182184

183185
Tabulator.registerModule(Object.values(CommonModules));
184-
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs]);
186+
Tabulator.registerModule([
187+
RowKeyboardNavigation,
188+
RowNavigation,
189+
Find,
190+
GroupCalcs,
191+
GroupSort,
192+
]);
185193
this._renderDMLTable(tableWrapper, this.dmlLines);
186194
}
187195
});
@@ -278,9 +286,11 @@ export class DMLView extends LitElement {
278286
placeholder: 'No DML statements found',
279287
columnCalcs: 'table',
280288
groupCalcs: true,
289+
groupSort: true,
281290
groupClosedShowCalcs: true,
282291
groupStartOpen: false,
283292
groupValues: [dmlText],
293+
groupBy: ['dml'],
284294
groupToggleElement: false,
285295
selectableRowsCheck: function (row: RowComponent) {
286296
return !row.getData().isDetail;
@@ -295,7 +305,6 @@ export class DMLView extends LitElement {
295305
headerTooltip: true,
296306
headerWordWrap: true,
297307
},
298-
initialSort: [{ column: 'rowCount', dir: 'desc' }],
299308
headerSortElement: function (column, dir) {
300309
switch (dir) {
301310
case 'asc':
@@ -316,6 +325,7 @@ export class DMLView extends LitElement {
316325
bottomCalc: () => {
317326
return 'Total';
318327
},
328+
headerSortTristate: true,
319329
cssClass: 'datagrid-textarea datagrid-code-text',
320330
variableHeight: true,
321331
formatter: (cell, _formatterParams, _onRendered) => {
@@ -371,10 +381,6 @@ export class DMLView extends LitElement {
371381
this._clearSearchHighlights();
372382
});
373383

374-
this.dmlTable.on('tableBuilt', () => {
375-
this.dmlTable?.setGroupBy('dml');
376-
});
377-
378384
this.dmlTable.on('groupClick', (e: UIEvent, group: GroupComponent) => {
379385
const { type } = window.getSelection() ?? {};
380386
if (type === 'Range') {

log-viewer/modules/components/database-view/SOQLView.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
} from 'tabulator-tables';
1818

1919
//tabulator custom modules
20-
import { GroupCalcs } from '../../datagrid/group-calcs/GroupCalcs.js';
20+
import { GroupCalcs } from '../../datagrid/groups/GroupCalcs.js';
21+
import { GroupSort } from '../../datagrid/groups/GroupSort.js';
2122
import * as CommonModules from '../../datagrid/module/CommonModules.js';
2223
import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js';
2324
import { RowNavigation } from '../../datagrid/module/RowNavigation.js';
@@ -192,19 +193,14 @@ export class SOQLView extends LitElement {
192193
_findEvt = ((event: FindEvt) => this._find(event)) as EventListener;
193194

194195
_soqlGroupBy(event: Event) {
195-
const target = event.target as HTMLInputElement;
196-
const fieldName = target.value.toLowerCase();
197-
const groupValue = fieldName !== 'none' ? fieldName : '';
198196
if (!this.soqlTable) {
199197
return;
200198
}
201-
202-
this.soqlTable.setGroupValues([
203-
groupValue
204-
? this.sortByFrequency(this.soqlTable.getData(), groupValue as keyof GridSOQLData)
205-
: [''],
206-
]);
207-
this.soqlTable.setGroupBy(groupValue);
199+
const target = event.target as HTMLInputElement;
200+
const fieldName = target.value.toLowerCase();
201+
const groupValue = fieldName !== 'none' ? fieldName : '';
202+
//@ts-expect-error This is a custom function added in the GroupSort custom module
203+
this.soqlTable.setSortedGroupBy(groupValue);
208204
}
209205

210206
_appendTableWhenVisible() {
@@ -219,7 +215,13 @@ export class SOQLView extends LitElement {
219215
this.soqlLines = (await DatabaseAccess.create(treeRoot)).getSOQLLines() || [];
220216

221217
Tabulator.registerModule(Object.values(CommonModules));
222-
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs]);
218+
Tabulator.registerModule([
219+
RowKeyboardNavigation,
220+
RowNavigation,
221+
Find,
222+
GroupCalcs,
223+
GroupSort,
224+
]);
223225
this._renderSOQLTable(tableWrapper, this.soqlLines);
224226
}
225227
});
@@ -326,8 +328,10 @@ export class SOQLView extends LitElement {
326328
keybindings: { copyToClipboard: ['ctrl + 67', 'meta + 67'] },
327329
clipboardCopyRowRange: 'all',
328330
groupCalcs: true,
331+
groupSort: true,
329332
groupClosedShowCalcs: true,
330333
groupStartOpen: false,
334+
groupBy: 'soql',
331335
groupValues: [soqlText],
332336
groupToggleElement: false,
333337
selectableRows: 'highlight',
@@ -343,7 +347,6 @@ export class SOQLView extends LitElement {
343347
headerTooltip: true,
344348
headerWordWrap: true,
345349
},
346-
initialSort: [{ column: 'rowCount', dir: 'desc' }],
347350
headerSortElement: function (column, dir) {
348351
switch (dir) {
349352
case 'asc':
@@ -366,6 +369,7 @@ export class SOQLView extends LitElement {
366369
bottomCalc: () => {
367370
return 'Total';
368371
},
372+
headerSortTristate: true,
369373
cssClass: 'datagrid-textarea datagrid-code-text',
370374
variableHeight: true,
371375
formatter: (cell, _formatterParams, _onRendered) => {
@@ -501,10 +505,6 @@ export class SOQLView extends LitElement {
501505
},
502506
});
503507

504-
this.soqlTable.on('tableBuilt', () => {
505-
this.soqlTable?.setGroupBy('soql');
506-
});
507-
508508
this.soqlTable.on('groupClick', (e: UIEvent, group: GroupComponent) => {
509509
const { type } = window.getSelection() ?? {};
510510
if (type === 'Range') {
File renamed without changes.
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { Module, type ColumnComponent, type GroupArg, type Tabulator } from 'tabulator-tables';
2+
3+
export class GroupSort extends Module {
4+
static moduleName = 'groupSort';
5+
6+
constructor(table: Tabulator) {
7+
super(table);
8+
this.registerTableOption('groupSort', false);
9+
this.registerTableFunction('setSortedGroupBy', this._setSortedGroupBy.bind(this));
10+
}
11+
12+
initialize() {
13+
// @ts-expect-error groupSort is a custom propoerty see registerTableOption above
14+
if (this.table.options.groupSort) {
15+
this.table.on('dataSorting', () => {
16+
this.table.blockRedraw();
17+
this._sortGroups();
18+
this.table.restoreRedraw();
19+
});
20+
}
21+
}
22+
23+
_setSortedGroupBy(...args: unknown[]) {
24+
const grpArg = args[0] as GroupArg;
25+
const grpArray = Array.isArray(grpArg) ? grpArg : [grpArg];
26+
const oldGrpArg = this.table.options.groupBy as GroupArg;
27+
const oldGrpArray = Array.isArray(oldGrpArg) ? oldGrpArg : [oldGrpArg];
28+
if (!this._areGroupsEqual(oldGrpArray, grpArray)) {
29+
this.table.options.groupBy = grpArg;
30+
this.table.blockRedraw();
31+
this._sortGroups();
32+
this.table.setGroupBy(grpArg);
33+
this.table.restoreRedraw();
34+
}
35+
}
36+
37+
_areGroupsEqual(oldGroups: unknown[], newGroups: unknown[]) {
38+
return (
39+
oldGroups &&
40+
newGroups.length === oldGroups.length &&
41+
newGroups.every((value, index) => value === oldGroups[index])
42+
);
43+
}
44+
45+
_sortGroups() {
46+
const grpArray = Array.isArray(this.table.options.groupBy)
47+
? this.table.options.groupBy
48+
: [this.table.options.groupBy];
49+
const { options } = this.table;
50+
options.groupValues = [];
51+
52+
const validGrps = grpArray.filter(Boolean).length > 0;
53+
if (this.table && this.table.options.sortMode !== 'remote' && validGrps) {
54+
const { modules } = this.table;
55+
56+
const groupRows = modules.groupRows;
57+
const rows = this.table.rowManager.rows;
58+
groupRows.configureGroupSetup();
59+
groupRows.generateGroups(rows);
60+
61+
const groupTotalsRows: InternalColumnTotal[] = [];
62+
const columnCalcs = modules.columnCalcs;
63+
const field = columnCalcs.botCalcs[0].field;
64+
groupRows.groupList.forEach((group: { key: string; rows: { data: unknown }[] }) => {
65+
const row = columnCalcs.generateBottomRow(group.rows);
66+
row.data[field] = group.key;
67+
row.key = group.key;
68+
row.rows = group.rows;
69+
row.generateCells();
70+
groupTotalsRows.push(row);
71+
});
72+
73+
const sortListActual: unknown[] = [];
74+
//build list of valid sorters and trigger column specific callbacks before sort begins
75+
const sorter = modules.sort;
76+
const sortList: InternalSortItem[] = options.sortOrderReverse
77+
? sorter.sortList.slice().reverse()
78+
: sorter.sortList;
79+
sortList.forEach((item) => {
80+
let sortObj;
81+
82+
if (item.column) {
83+
sortObj = item.column.modules.sort;
84+
85+
if (sortObj) {
86+
//if no sorter has been defined, take a guess
87+
if (!sortObj.sorter) {
88+
sortObj.sorter = sorter.findSorter(item.column);
89+
}
90+
91+
item.params =
92+
typeof sortObj.params === 'function'
93+
? sortObj.params(item.column.getComponent(), item.dir)
94+
: sortObj.params;
95+
96+
sortListActual.push(item);
97+
}
98+
}
99+
});
100+
101+
//sort data
102+
if (sortListActual.length) {
103+
sorter._sortItems(groupTotalsRows, sortListActual);
104+
} else {
105+
groupTotalsRows.sort((a, b) => {
106+
const index = b.rows.length - a.rows.length;
107+
if (index === 0) {
108+
return a.key.localeCompare(b.key);
109+
}
110+
return index;
111+
});
112+
}
113+
const groupValues: string[] = [];
114+
groupTotalsRows.forEach((colTotals) => {
115+
groupValues.push(colTotals.data[field] as string);
116+
});
117+
118+
this.table?.setGroupValues([groupValues]);
119+
}
120+
}
121+
}
122+
123+
// Representations of the internal Tabulator structures, that are entirely private to Tabulator. Subject to change and likely to b a bit flaky. May not cover all cases yet.
124+
type InternalColumnTotal = {
125+
data: { [key: string]: unknown };
126+
key: string;
127+
rows: { [key: string]: unknown }[];
128+
};
129+
130+
type InternalColumn = {
131+
getField(): string;
132+
getComponent(): ColumnComponent;
133+
modules: {
134+
sort: {
135+
params: (column: ColumnComponent, dir: string) => object;
136+
sorter: (...args: unknown[]) => number | boolean;
137+
};
138+
};
139+
};
140+
141+
type InternalSortItem = {
142+
column: InternalColumn;
143+
dir: string;
144+
params: object;
145+
};

0 commit comments

Comments
 (0)