Skip to content

Commit 2b00d3e

Browse files
authored
Use UI toolkit for variable table (#291)
* Use UI toolkit for variable table * Register web components * Integrate ui-toolkit library to filter variable * Convert tests to use ui-toolkit * jlpm build * Make requested PR changes * Fix select component
1 parent 1f2c840 commit 2b00d3e

File tree

6 files changed

+676
-510
lines changed

6 files changed

+676
-510
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"watch:src": "tsc -w --sourceMap"
5353
},
5454
"dependencies": {
55+
"@jupyter/web-components": "^0.13.3",
5556
"@jupyterlab/application": "^4.0.0",
5657
"@jupyterlab/apputils": "^4.0.0",
5758
"@jupyterlab/console": "^4.0.0",

src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import { VariableInspectorManager } from './manager';
2424
import { VariableInspectorPanel } from './variableinspector';
2525

2626
import { IVariableInspector, IVariableInspectorManager } from './tokens';
27-
27+
import { addJupyterLabThemeChangeListener } from '@jupyter/web-components';
28+
addJupyterLabThemeChangeListener();
2829
namespace CommandIDs {
2930
export const open = 'variableinspector:open';
3031
}

src/variableinspector.ts

+126-61
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,38 @@ import { DockLayout, Widget } from '@lumino/widgets';
88

99
import { IVariableInspector } from './tokens';
1010

11+
import {
12+
DataGrid as WebDataGrid,
13+
DataGridRow,
14+
DataGridCell,
15+
provideJupyterDesignSystem,
16+
Select,
17+
Option,
18+
TextField,
19+
Button,
20+
jpDataGrid,
21+
jpDataGridRow,
22+
jpDataGridCell,
23+
jpTextField,
24+
jpOption,
25+
jpSelect,
26+
jpButton
27+
} from '@jupyter/web-components';
28+
provideJupyterDesignSystem().register(
29+
jpDataGrid(),
30+
jpDataGridRow(),
31+
jpDataGridCell(),
32+
jpTextField(),
33+
jpOption(),
34+
jpSelect(),
35+
jpButton()
36+
);
37+
1138
import wildcardMatch from 'wildcard-match';
1239

1340
const TITLE_CLASS = 'jp-VarInspector-title';
1441
const PANEL_CLASS = 'jp-VarInspector';
1542
const TABLE_CLASS = 'jp-VarInspector-table';
16-
const TABLE_BODY_CLASS = 'jp-VarInspector-content';
1743
const TABLE_ROW_CLASS = 'jp-VarInspector-table-row';
1844
const TABLE_ROW_HIDDEN_CLASS = 'jp-VarInspector-table-row-hidden';
1945
const TABLE_TYPE_CLASS = 'jp-VarInspector-type';
@@ -34,8 +60,8 @@ export class VariableInspectorPanel
3460
implements IVariableInspector
3561
{
3662
private _source: IVariableInspector.IInspectable | null = null;
63+
private _table: WebDataGrid;
3764
private _filteredTable: HTMLDivElement;
38-
private _table: HTMLTableElement;
3965
private _title: HTMLElement;
4066
private _filtered: { type: Array<string>; name: Array<string> };
4167

@@ -58,13 +84,13 @@ export class VariableInspectorPanel
5884
protected intializeFilteredTable() {
5985
const filterType = this._filteredTable.querySelector(
6086
'.' + FILTER_TYPE_CLASS
61-
) as HTMLSelectElement;
87+
) as Select;
6288
const filterInput = this._filteredTable.querySelector(
6389
'.' + FILTER_INPUT_CLASS
64-
) as HTMLInputElement;
90+
) as TextField;
6591
const filterButton = this._filteredTable.querySelector(
6692
'.' + FILTER_BUTTON_CLASS
67-
) as HTMLButtonElement;
93+
) as Button;
6894
filterButton.addEventListener('click', () => {
6995
this.onFilterChange(
7096
filterType.value as FILTER_TYPES,
@@ -137,14 +163,14 @@ export class VariableInspectorPanel
137163
protected addFilteredOutRows() {
138164
const rows = this._table.querySelectorAll(
139165
'.' + TABLE_ROW_HIDDEN_CLASS
140-
) as NodeListOf<HTMLTableRowElement>;
166+
) as NodeListOf<DataGridRow>;
141167
for (let i = 0; i < rows.length; i++) {
142168
const rowName = rows[i].querySelector(
143169
'.' + TABLE_NAME_CLASS
144-
) as HTMLTableCellElement;
170+
) as DataGridCell;
145171
const rowType = rows[i].querySelector(
146172
'.' + TABLE_TYPE_CLASS
147-
) as HTMLTableCellElement;
173+
) as DataGridCell;
148174
if (
149175
!this.stringInFilter(rowName.innerHTML, 'name') &&
150176
!this._filtered['type'].includes(rowType.innerHTML)
@@ -161,14 +187,14 @@ export class VariableInspectorPanel
161187
protected filterOutTable() {
162188
const rows = this._table.querySelectorAll(
163189
'.' + TABLE_ROW_CLASS
164-
) as NodeListOf<HTMLTableRowElement>;
190+
) as NodeListOf<DataGridRow>;
165191
for (let i = 0; i < rows.length; i++) {
166192
const rowName = rows[i].querySelector(
167193
'.' + TABLE_NAME_CLASS
168-
) as HTMLTableCellElement;
194+
) as DataGridCell;
169195
const rowType = rows[i].querySelector(
170196
'.' + TABLE_TYPE_CLASS
171-
) as HTMLTableCellElement;
197+
) as DataGridCell;
172198
if (
173199
this.stringInFilter(rowName.innerHTML, 'name') ||
174200
this._filtered['type'].includes(rowType.innerHTML)
@@ -178,6 +204,24 @@ export class VariableInspectorPanel
178204
}
179205
}
180206

207+
/*
208+
Goes through each row and if it finds a variable with name 'name', then it deletes it
209+
*/
210+
protected removeRow(name: string) {
211+
const rows = this._table.querySelectorAll(
212+
'.' + TABLE_ROW_CLASS
213+
) as NodeListOf<DataGridRow>;
214+
for (let i = 0; i < rows.length; i++) {
215+
const cell = rows[i].querySelector(
216+
'.' + TABLE_NAME_CLASS
217+
) as DataGridCell;
218+
if (cell.innerHTML === name) {
219+
rows[i].remove();
220+
return;
221+
}
222+
}
223+
}
224+
181225
get source(): IVariableInspector.IInspectable | null {
182226
return this._source;
183227
}
@@ -230,18 +274,28 @@ export class VariableInspectorPanel
230274
" Inspecting '" + title.kernelName + "' " + title.contextName;
231275
}
232276

277+
this._table.innerHTML = '';
278+
const headerRow = document.createElement('jp-data-grid-row') as DataGridRow;
279+
headerRow.className = 'sticky-header';
280+
const columns = [' ', ' ', 'NAME', 'TYPE', 'SIZE', 'SHAPE', 'CONTENT'];
281+
for (let i = 0; i < columns.length; i++) {
282+
const headerCell = document.createElement(
283+
'jp-data-grid-cell'
284+
) as DataGridCell;
285+
headerCell.className = 'column-header';
286+
headerCell.textContent = columns[i];
287+
headerCell.gridColumn = (i + 1).toString();
288+
headerRow.appendChild(headerCell);
289+
}
290+
this._table.appendChild(headerRow);
291+
233292
//Render new variable state
234-
let row: HTMLTableRowElement;
235-
this._table.deleteTFoot();
236-
this._table.createTFoot();
237-
this._table.tFoot!.className = TABLE_BODY_CLASS;
238293
for (let index = 0; index < args.length; index++) {
239294
const item = args[index];
240-
241295
const name = item.varName;
242296
const varType = item.varType;
243297

244-
row = this._table.tFoot!.insertRow();
298+
const row = document.createElement('jp-data-grid-row') as DataGridRow;
245299
row.className = TABLE_ROW_CLASS;
246300
if (this._filtered['type'].includes(varType)) {
247301
row.className = TABLE_ROW_HIDDEN_CLASS;
@@ -250,48 +304,67 @@ export class VariableInspectorPanel
250304
}
251305

252306
// Add delete icon and onclick event
253-
let cell = row.insertCell(0);
307+
let cell = document.createElement('jp-data-grid-cell') as DataGridCell;
254308
cell.title = 'Delete Variable';
255309
cell.className = 'jp-VarInspector-deleteButton';
310+
cell.gridColumn = '1';
311+
const closeButton = document.createElement('jp-button') as Button;
312+
closeButton.appearance = 'stealth';
256313
const ico = closeIcon.element();
314+
ico.className = 'icon-button';
257315
ico.onclick = (ev: MouseEvent): any => {
258-
this.source?.performDelete(name);
316+
this.removeRow(name);
259317
};
260-
cell.append(ico);
318+
closeButton.append(ico);
319+
cell.append(closeButton);
320+
row.appendChild(cell);
261321

262322
// Add onclick event for inspection
263-
cell = row.insertCell(1);
323+
cell = document.createElement('jp-data-grid-cell') as DataGridCell;
264324
if (item.isMatrix) {
265325
cell.title = 'View Contents';
266326
cell.className = 'jp-VarInspector-inspectButton';
327+
const searchButton = document.createElement('jp-button') as Button;
328+
searchButton.appearance = 'stealth';
267329
const ico = searchIcon.element();
330+
ico.className = 'icon-button';
268331
ico.onclick = (ev: MouseEvent): any => {
269-
console.log('Click on ' + name);
270332
this._source
271-
?.performMatrixInspection(name)
333+
?.performMatrixInspection(item.varName)
272334
.then((model: DataModel) => {
273-
this._showMatrix(model, name, varType);
335+
this._showMatrix(model, item.varName, item.varType);
274336
});
275337
};
276-
cell.append(ico);
338+
searchButton.append(ico);
339+
cell.append(searchButton);
277340
} else {
278341
cell.innerHTML = '';
279342
}
343+
cell.gridColumn = '2';
344+
row.appendChild(cell);
280345

281-
cell = row.insertCell(2);
346+
cell = document.createElement('jp-data-grid-cell') as DataGridCell;
282347
cell.className = TABLE_NAME_CLASS;
283348
cell.innerHTML = name;
349+
cell.gridColumn = '3';
350+
row.appendChild(cell);
284351

285352
// Add remaining cells
286-
cell = row.insertCell(3);
353+
cell = document.createElement('jp-data-grid-cell') as DataGridCell;
287354
cell.innerHTML = varType;
288355
cell.className = TABLE_TYPE_CLASS;
289-
cell = row.insertCell(4);
356+
cell.gridColumn = '4';
357+
row.appendChild(cell);
358+
cell = document.createElement('jp-data-grid-cell') as DataGridCell;
290359
cell.innerHTML = item.varSize;
291-
cell = row.insertCell(5);
360+
cell.gridColumn = '5';
361+
row.appendChild(cell);
362+
cell = document.createElement('jp-data-grid-cell') as DataGridCell;
292363
cell.innerHTML = item.varShape;
293-
cell = row.insertCell(6);
364+
cell.gridColumn = '6';
365+
row.appendChild(cell);
294366

367+
cell = document.createElement('jp-data-grid-cell') as DataGridCell;
295368
const rendermime = this._source?.rendermime;
296369
if (item.isWidget && rendermime) {
297370
const model = new OutputAreaModel({ trusted: true });
@@ -304,6 +377,9 @@ export class VariableInspectorPanel
304377
'</br>'
305378
);
306379
}
380+
cell.gridColumn = '7';
381+
row.appendChild(cell);
382+
this._table.appendChild(row);
307383
}
308384
}
309385

@@ -356,25 +432,10 @@ namespace Private {
356432
);
357433
}
358434

359-
export function createTable(): HTMLTableElement {
360-
const table = document.createElement('table');
361-
table.createTHead();
362-
const hrow = table.tHead!.insertRow(0) as HTMLTableRowElement;
363-
364-
const cell1 = hrow.insertCell(0);
365-
cell1.innerHTML = '';
366-
const cell2 = hrow.insertCell(1);
367-
cell2.innerHTML = '';
368-
const cell3 = hrow.insertCell(2);
369-
cell3.innerHTML = 'Name';
370-
const cell4 = hrow.insertCell(3);
371-
cell4.innerHTML = 'Type';
372-
const cell5 = hrow.insertCell(4);
373-
cell5.innerHTML = 'Size';
374-
const cell6 = hrow.insertCell(5);
375-
cell6.innerHTML = 'Shape';
376-
const cell7 = hrow.insertCell(6);
377-
cell7.innerHTML = 'Content';
435+
export function createTable(): WebDataGrid {
436+
const table = document.createElement('jp-data-grid') as WebDataGrid;
437+
table.generateHeader = 'sticky';
438+
table.gridTemplateColumns = '1fr 1fr 6fr 4fr 4fr 5fr 16fr';
378439
return table;
379440
}
380441

@@ -387,27 +448,27 @@ namespace Private {
387448
export function createFilterTable(): HTMLDivElement {
388449
const container = document.createElement('div');
389450
container.className = 'filter-container';
390-
const filterType = document.createElement('select');
451+
const filterType = document.createElement('jp-select') as Select;
391452
filterType.className = FILTER_TYPE_CLASS;
392453
filterType.selectedIndex = 0;
393-
const varTypeOption = document.createElement('option');
454+
const varTypeOption = document.createElement('jp-option') as Option;
394455
varTypeOption.value = 'type';
395456
varTypeOption.innerHTML = 'Type';
396-
const nameOption = document.createElement('option');
457+
const nameOption = document.createElement('jp-option') as Option;
397458
nameOption.value = 'name';
398459
nameOption.innerHTML = 'Name';
399460
filterType.appendChild(varTypeOption);
400461
filterType.appendChild(nameOption);
401462
const searchContainer = document.createElement('div');
402463
searchContainer.className = 'jp-InputGroup filter-search-container';
403-
const input = document.createElement('input');
464+
const input = document.createElement('jp-text-field') as TextField;
404465
input.setAttribute('type', 'text');
405466
input.setAttribute('placeholder', 'Filter out variable');
406467
input.className = FILTER_INPUT_CLASS;
407-
const filterButton = document.createElement('button');
408-
const buttonText = document.createTextNode('Filter');
409-
filterButton.appendChild(buttonText);
468+
const filterButton = document.createElement('jp-button') as Button;
469+
filterButton.textContent = 'Filter';
410470
filterButton.className = FILTER_BUTTON_CLASS;
471+
filterButton.appearance = 'accent';
411472
const list = document.createElement('ul');
412473
list.className = FILTER_LIST_CLASS;
413474

@@ -423,18 +484,22 @@ namespace Private {
423484
export function createFilteredButton(
424485
filterName: string,
425486
filterType: FILTER_TYPES
426-
): HTMLButtonElement {
427-
const filteredButton = document.createElement('button');
487+
): Button {
488+
const filteredButton = document.createElement('jp-button') as Button;
428489
filteredButton.value = filterType;
429490
filteredButton.title = filterType;
491+
filteredButton.className = FILTERED_BUTTON_CLASS;
492+
const filterButtonContent = document.createElement('div');
493+
filterButtonContent.className = 'filter-button-content';
430494
const buttonText = document.createElement('div');
431495
buttonText.className = 'filtered-variable-button-text';
432496
buttonText.innerHTML = filterName;
433497
const icon = closeIcon.element({
434-
container: filteredButton
498+
container: filterButtonContent
435499
});
436-
filteredButton.appendChild(buttonText);
437-
filteredButton.appendChild(icon);
500+
filterButtonContent.appendChild(buttonText);
501+
filterButtonContent.appendChild(icon);
502+
filteredButton.appendChild(filterButtonContent);
438503
filteredButton.className = FILTERED_BUTTON_CLASS;
439504
return filteredButton;
440505
}

0 commit comments

Comments
 (0)