Skip to content

Commit 7fa1999

Browse files
authored
Add support for a down area (#7619)
* add a down area * log console * temp patch for the logconsole-extension * fix default * update snapshots * remove patch * add UI test
1 parent 6853096 commit 7fa1999

File tree

7 files changed

+106
-15
lines changed

7 files changed

+106
-15
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ ui-tests/playwright-report
136136
ui-tests/.yarn/*
137137
ui-tests/.pnp.*
138138

139+
# keep potential upstream patches
140+
!.yarn/patches
141+
139142
# generated html
140143
notebook/templates/*.html
141144

app/package.json

+15-12
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@jupyterlab/imageviewer-extension": "~4.4.0-rc.1",
6464
"@jupyterlab/javascript-extension": "~4.4.0-rc.1",
6565
"@jupyterlab/json-extension": "~4.4.0-rc.1",
66+
"@jupyterlab/logconsole-extension": "~4.4.0-rc.1",
6667
"@jupyterlab/lsp": "~4.4.0-rc.1",
6768
"@jupyterlab/lsp-extension": "~4.4.0-rc.1",
6869
"@jupyterlab/mainmenu": "~4.4.0-rc.1",
@@ -107,18 +108,18 @@
107108
"@jupyterlab/vega5-extension": "~4.4.0-rc.1",
108109
"@lezer/common": "~1.2.1",
109110
"@lezer/highlight": "~1.2.0",
110-
"@lumino/algorithm": "~2.0.2",
111-
"@lumino/application": "~2.4.2",
112-
"@lumino/commands": "~2.3.1",
113-
"@lumino/coreutils": "~2.2.0",
114-
"@lumino/disposable": "~2.1.3",
115-
"@lumino/domutils": "~2.0.2",
116-
"@lumino/dragdrop": "~2.1.5",
117-
"@lumino/messaging": "~2.0.2",
118-
"@lumino/properties": "~2.0.2",
119-
"@lumino/signaling": "~2.1.3",
120-
"@lumino/virtualdom": "~2.0.2",
121-
"@lumino/widgets": "~2.6.0",
111+
"@lumino/algorithm": "~2.0.3",
112+
"@lumino/application": "~2.4.3",
113+
"@lumino/commands": "~2.3.2",
114+
"@lumino/coreutils": "~2.2.1",
115+
"@lumino/disposable": "~2.1.4",
116+
"@lumino/domutils": "~2.0.3",
117+
"@lumino/dragdrop": "~2.1.6",
118+
"@lumino/messaging": "~2.0.3",
119+
"@lumino/properties": "~2.0.3",
120+
"@lumino/signaling": "~2.1.4",
121+
"@lumino/virtualdom": "~2.0.3",
122+
"@lumino/widgets": "~2.7.0",
122123
"react": "~18.2.0",
123124
"react-dom": "~18.2.0",
124125
"yjs": "~13.6.8"
@@ -158,6 +159,7 @@
158159
"@jupyterlab/imageviewer-extension": "~4.4.0-rc.1",
159160
"@jupyterlab/javascript-extension": "~4.4.0-rc.1",
160161
"@jupyterlab/json-extension": "~4.4.0-rc.1",
162+
"@jupyterlab/logconsole-extension": "~4.4.0-rc.1",
161163
"@jupyterlab/lsp": "~4.4.0-rc.1",
162164
"@jupyterlab/lsp-extension": "~4.4.0-rc.1",
163165
"@jupyterlab/mainmenu-extension": "~4.4.0-rc.1",
@@ -337,6 +339,7 @@
337339
"@jupyterlab/debugger-extension:sidebar",
338340
"@jupyterlab/debugger-extension:sources"
339341
],
342+
"@jupyterlab/logconsole-extension": true,
340343
"@jupyterlab/metadataform-extension": true,
341344
"@jupyterlab/notebook-extension": [
342345
"@jupyterlab/notebook-extension:active-cell-tool",

packages/application/src/shell.ts

+48-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import {
1414
FocusTracker,
1515
Panel,
1616
SplitPanel,
17+
TabPanel,
1718
Widget,
1819
} from '@lumino/widgets';
1920
import { PanelHandler, SidePanelHandler } from './panelhandler';
21+
import { TabPanelSvg } from '@jupyterlab/ui-components';
2022

2123
/**
2224
* The Jupyter Notebook application shell token.
@@ -37,7 +39,7 @@ export namespace INotebookShell {
3739
/**
3840
* The areas of the application shell where widgets can reside.
3941
*/
40-
export type Area = 'main' | 'top' | 'menu' | 'left' | 'right';
42+
export type Area = 'main' | 'top' | 'menu' | 'left' | 'right' | 'down';
4143

4244
/**
4345
* Widget position
@@ -134,6 +136,18 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
134136
middlePanel.addWidget(this._spacer_bottom);
135137
middlePanel.layout = middleLayout;
136138

139+
const vsplitPanel = new SplitPanel();
140+
vsplitPanel.id = 'jp-main-vsplit-panel';
141+
vsplitPanel.spacing = 1;
142+
vsplitPanel.orientation = 'vertical';
143+
SplitPanel.setStretch(vsplitPanel, 1);
144+
145+
const downPanel = new TabPanelSvg({
146+
tabsMovable: true,
147+
});
148+
this._downPanel = downPanel;
149+
this._downPanel.id = 'jp-down-stack';
150+
137151
// TODO: Consider storing this as an attribute this._hsplitPanel if saving/restoring layout needed
138152
const hsplitPanel = new SplitPanel();
139153
hsplitPanel.id = 'main-split-panel';
@@ -153,8 +167,21 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
153167
// panel.
154168
hsplitPanel.setRelativeSizes([1, 2.5, 1]);
155169

170+
vsplitPanel.addWidget(hsplitPanel);
171+
vsplitPanel.addWidget(downPanel);
172+
156173
rootLayout.spacing = 0;
157-
rootLayout.addWidget(hsplitPanel);
174+
rootLayout.addWidget(vsplitPanel);
175+
176+
// initially hiding the down panel
177+
this._downPanel.hide();
178+
179+
// Connect down panel change listeners
180+
this._downPanel.tabBar.tabMoved.connect(this._onTabPanelChanged, this);
181+
this._downPanel.stackedPanel.widgetRemoved.connect(
182+
this._onTabPanelChanged,
183+
this
184+
);
158185

159186
this.layout = rootLayout;
160187

@@ -267,7 +294,7 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
267294
*/
268295
activateById(id: string): void {
269296
// Search all areas that can have widgets for this widget, starting with main.
270-
for (const area of ['main', 'top', 'left', 'right', 'menu']) {
297+
for (const area of ['main', 'top', 'left', 'right', 'menu', 'down']) {
271298
const widget = find(
272299
this.widgets(area as INotebookShell.Area),
273300
(w) => w.id === id
@@ -277,6 +304,9 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
277304
this.expandLeft(id);
278305
} else if (area === 'right') {
279306
this.expandRight(id);
307+
} else if (area === 'down') {
308+
this._downPanel.show();
309+
widget.activate();
280310
} else {
281311
widget.activate();
282312
}
@@ -342,6 +372,8 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
342372
return this._leftHandler.addWidget(widget, rank);
343373
case 'right':
344374
return this._rightHandler.addWidget(widget, rank);
375+
case 'down':
376+
return this._downPanel.addWidget(widget);
345377
default:
346378
console.warn(`Cannot add widget to area: ${area}`);
347379
}
@@ -385,6 +417,9 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
385417
case 'right':
386418
yield* this._rightHandler.widgets;
387419
return;
420+
case 'down':
421+
yield* this._downPanel.widgets;
422+
return;
388423
default:
389424
console.error(`This shell has no area called "${area}"`);
390425
return;
@@ -432,6 +467,15 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
432467
this._userLayout = configuration;
433468
}
434469

470+
/**
471+
* Handle a change on the down panel widgets
472+
*/
473+
private _onTabPanelChanged(): void {
474+
if (this._downPanel.stackedPanel.widgets.length === 0) {
475+
this._downPanel.hide();
476+
}
477+
}
478+
435479
private _topWrapper: Panel;
436480
private _topHandler: PanelHandler;
437481
private _menuWrapper: Panel;
@@ -442,6 +486,7 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
442486
private _spacer_bottom: Widget;
443487
private _skipLinkWidgetHandler: Private.SkipLinkWidgetHandler;
444488
private _main: Panel;
489+
private _downPanel: TabPanel;
445490
private _translator: ITranslator = nullTranslator;
446491
private _currentChanged = new Signal<this, FocusTracker.IChangedArgs<Widget>>(
447492
this
Loading
Loading

ui-tests/test/notebook.spec.ts

+17
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,21 @@ test.describe('Notebook', () => {
206206
await page.menu.clickMenuItem(menuPath);
207207
await expect(notebookPanel).not.toHaveClass(/jp-mod-fullwidth/);
208208
});
209+
210+
test('Open the log console widget in the down area', async ({
211+
page,
212+
tmpPath,
213+
}) => {
214+
const notebook = 'simple.ipynb';
215+
await page.contents.uploadFile(
216+
path.resolve(__dirname, `./notebooks/${notebook}`),
217+
`${tmpPath}/${notebook}`
218+
);
219+
await page.goto(`notebooks/${tmpPath}/${notebook}`);
220+
221+
const menuPath = 'View>Show Log Console';
222+
await page.menu.clickMenuItem(menuPath);
223+
224+
await expect(page.locator('.jp-LogConsole')).toBeVisible();
225+
});
209226
});

yarn.lock

+23
Original file line numberDiff line numberDiff line change
@@ -2167,6 +2167,7 @@ __metadata:
21672167
"@jupyterlab/imageviewer-extension": ~4.4.0-rc.1
21682168
"@jupyterlab/javascript-extension": ~4.4.0-rc.1
21692169
"@jupyterlab/json-extension": ~4.4.0-rc.1
2170+
"@jupyterlab/logconsole-extension": ~4.4.0-rc.1
21702171
"@jupyterlab/lsp": ~4.4.0-rc.1
21712172
"@jupyterlab/lsp-extension": ~4.4.0-rc.1
21722173
"@jupyterlab/mainmenu-extension": ~4.4.0-rc.1
@@ -3832,6 +3833,28 @@ __metadata:
38323833
languageName: node
38333834
linkType: hard
38343835

3836+
"@jupyterlab/logconsole-extension@npm:~4.4.0-rc.1":
3837+
version: 4.4.0-rc.1
3838+
resolution: "@jupyterlab/logconsole-extension@npm:4.4.0-rc.1"
3839+
dependencies:
3840+
"@jupyterlab/application": ^4.4.0-rc.1
3841+
"@jupyterlab/apputils": ^4.5.0-rc.1
3842+
"@jupyterlab/coreutils": ^6.4.0-rc.1
3843+
"@jupyterlab/docregistry": ^4.4.0-rc.1
3844+
"@jupyterlab/logconsole": ^4.4.0-rc.1
3845+
"@jupyterlab/rendermime": ^4.4.0-rc.1
3846+
"@jupyterlab/settingregistry": ^4.4.0-rc.1
3847+
"@jupyterlab/statusbar": ^4.4.0-rc.1
3848+
"@jupyterlab/translation": ^4.4.0-rc.1
3849+
"@jupyterlab/ui-components": ^4.4.0-rc.1
3850+
"@lumino/coreutils": ^2.2.1
3851+
"@lumino/signaling": ^2.1.4
3852+
"@lumino/widgets": ^2.7.0
3853+
react: ^18.2.0
3854+
checksum: b49a7a16162dd612f8e7acb24d505cfaa4540957c82630c53392e5d93be8333795777c5a31cd3a5c3864f89cff0ba725980832a86778baf952a65eacf2659a14
3855+
languageName: node
3856+
linkType: hard
3857+
38353858
"@jupyterlab/logconsole@npm:^4.4.0-rc.1":
38363859
version: 4.4.0-rc.1
38373860
resolution: "@jupyterlab/logconsole@npm:4.4.0-rc.1"

0 commit comments

Comments
 (0)