Skip to content

Commit 390c452

Browse files
authored
Add a setting to enable the notebook to take up the full width (jupyter#7487)
* Toggle full width * settings only * enable more ways to toggle * lint * fixes * add ui test * fix snapshots * reusable waitForNotebook * more updates * fix * update
1 parent 676a0fe commit 390c452

File tree

11 files changed

+194
-25
lines changed

11 files changed

+194
-25
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"title": "Jupyter Notebook Full Width Notebook",
3+
"description": "Jupyter Notebook Notebook With settings",
4+
"jupyter.lab.menus": {
5+
"main": [
6+
{
7+
"id": "jp-mainmenu-view",
8+
"items": [
9+
{
10+
"command": "notebook:toggle-full-width",
11+
"rank": 4
12+
}
13+
]
14+
}
15+
]
16+
},
17+
"properties": {
18+
"fullWidthNotebook": {
19+
"type": "boolean",
20+
"title": "Full Width Notebook",
21+
"description": "Whether to the notebook should take up the full width of the application",
22+
"default": false
23+
}
24+
},
25+
"additionalProperties": false,
26+
"type": "object"
27+
}

packages/notebook-extension/src/index.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ const KERNEL_STATUS_FADE_OUT_CLASS = 'jp-NotebookKernelStatus-fade';
6464
*/
6565
const SCROLLED_OUTPUTS_CLASS = 'jp-mod-outputsScrolled';
6666

67+
/**
68+
* The class for the full width notebook
69+
*/
70+
const FULL_WIDTH_NOTEBOOK_CLASS = 'jp-mod-fullwidth';
71+
6772
/**
6873
* The command IDs used by the notebook plugins.
6974
*/
@@ -72,6 +77,11 @@ namespace CommandIDs {
7277
* A command to open right sidebar for Editing Notebook Metadata
7378
*/
7479
export const openEditNotebookMetadata = 'notebook:edit-metadata';
80+
81+
/**
82+
* A command to toggle full width of the notebook
83+
*/
84+
export const toggleFullWidth = 'notebook:toggle-full-width';
7585
}
7686

7787
/**
@@ -202,6 +212,83 @@ const openTreeTab: JupyterFrontEndPlugin<void> = {
202212
},
203213
};
204214

215+
/**
216+
* A plugin to set the notebook to full width.
217+
*/
218+
const fullWidthNotebook: JupyterFrontEndPlugin<void> = {
219+
id: '@jupyter-notebook/notebook-extension:full-width-notebook',
220+
description: 'A plugin to set the notebook to full width.',
221+
autoStart: true,
222+
requires: [INotebookTracker],
223+
optional: [ICommandPalette, ISettingRegistry, ITranslator],
224+
activate: (
225+
app: JupyterFrontEnd,
226+
tracker: INotebookTracker,
227+
palette: ICommandPalette | null,
228+
settingRegistry: ISettingRegistry | null,
229+
translator: ITranslator | null
230+
) => {
231+
const trans = (translator ?? nullTranslator).load('notebook');
232+
233+
let fullWidth = false;
234+
235+
const toggleFullWidth = () => {
236+
const current = tracker.currentWidget;
237+
fullWidth = !fullWidth;
238+
if (!current) {
239+
return;
240+
}
241+
const content = current;
242+
content.toggleClass(FULL_WIDTH_NOTEBOOK_CLASS, fullWidth);
243+
};
244+
245+
let notebookSettings: ISettingRegistry.ISettings;
246+
247+
if (settingRegistry) {
248+
const loadSettings = settingRegistry.load(fullWidthNotebook.id);
249+
250+
const updateSettings = (settings: ISettingRegistry.ISettings): void => {
251+
const newFullWidth = settings.get('fullWidthNotebook')
252+
.composite as boolean;
253+
if (newFullWidth !== fullWidth) {
254+
toggleFullWidth();
255+
}
256+
};
257+
258+
Promise.all([loadSettings, app.restored])
259+
.then(([settings]) => {
260+
notebookSettings = settings;
261+
updateSettings(settings);
262+
settings.changed.connect((settings) => {
263+
updateSettings(settings);
264+
});
265+
})
266+
.catch((reason: Error) => {
267+
console.error(reason.message);
268+
});
269+
}
270+
271+
app.commands.addCommand(CommandIDs.toggleFullWidth, {
272+
label: trans.__('Enable Full Width Notebook'),
273+
execute: () => {
274+
toggleFullWidth();
275+
if (notebookSettings) {
276+
notebookSettings.set('fullWidthNotebook', fullWidth);
277+
}
278+
},
279+
isEnabled: () => tracker.currentWidget !== null,
280+
isToggled: () => fullWidth,
281+
});
282+
283+
if (palette) {
284+
palette.addItem({
285+
command: CommandIDs.toggleFullWidth,
286+
category: 'Notebook Operations',
287+
});
288+
}
289+
},
290+
};
291+
205292
/**
206293
* The kernel logo plugin.
207294
*/
@@ -597,6 +684,7 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
597684
closeTab,
598685
openTreeTab,
599686
editNotebookMetadata,
687+
fullWidthNotebook,
600688
kernelLogo,
601689
kernelStatus,
602690
notebookToolsWidget,

packages/notebook-extension/style/base.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@
1616
- compact view on mobile
1717
*/
1818

19+
/* Make the notebook take up the full width of the page when jp-mod-fullwidth is set */
20+
21+
body[data-notebook='notebooks']
22+
.jp-NotebookPanel.jp-mod-fullwidth
23+
.jp-WindowedPanel-outer {
24+
padding-left: unset;
25+
padding-right: unset !important;
26+
width: unset;
27+
}
28+
1929
/* Keep the notebook centered on the page */
2030

2131
body[data-notebook='notebooks'] .jp-NotebookPanel-toolbar {

ui-tests/test/general.spec.ts

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { expect } from '@jupyterlab/galata';
77

88
import { test } from './fixtures';
99

10-
import { hideAddCellButton, waitForKernelReady } from './utils';
10+
import { waitForNotebook } from './utils';
1111

1212
test.describe('General', () => {
1313
test('The notebook should render', async ({ page, tmpPath, browserName }) => {
@@ -18,23 +18,6 @@ test.describe('General', () => {
1818
);
1919
await page.goto(`notebooks/${tmpPath}/${notebook}`);
2020

21-
// wait for the kernel status animations to be finished
22-
await waitForKernelReady(page);
23-
await page.waitForSelector(
24-
".jp-Notebook-ExecutionIndicator[data-status='idle']"
25-
);
26-
27-
const checkpointLocator = '.jp-NotebookCheckpoint';
28-
// wait for the checkpoint indicator to be displayed
29-
await page.waitForSelector(checkpointLocator);
30-
31-
// remove the amount of seconds manually since it might display strings such as "3 seconds ago"
32-
await page
33-
.locator(checkpointLocator)
34-
.evaluate(
35-
(element) => (element.innerHTML = 'Last Checkpoint: 3 seconds ago')
36-
);
37-
3821
// check the notebook footer shows up on hover
3922
const notebookFooter = '.jp-Notebook-footer';
4023
await page.hover(notebookFooter);
@@ -46,11 +29,8 @@ test.describe('General', () => {
4629
// click to make the blue border around the cell disappear
4730
await page.click('.jp-WindowedPanel-outer');
4831

49-
// special case for firefox headless issue
50-
// see https://github.com/jupyter/notebook/pull/6872#issuecomment-1549594166 for more details
51-
if (browserName === 'firefox') {
52-
await hideAddCellButton(page);
53-
}
32+
// wait for the notebook to be ready
33+
await waitForNotebook(page, browserName);
5434

5535
expect(await page.screenshot()).toMatchSnapshot('notebook.png');
5636
});
Loading
Loading

ui-tests/test/notebook.spec.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { expect } from '@jupyterlab/galata';
77

88
import { test } from './fixtures';
99

10-
import { runAndAdvance, waitForKernelReady } from './utils';
10+
import { waitForNotebook, runAndAdvance, waitForKernelReady } from './utils';
1111

1212
const NOTEBOOK = 'example.ipynb';
1313

@@ -175,4 +175,35 @@ test.describe('Notebook', () => {
175175

176176
expect(page.isClosed());
177177
});
178+
179+
test('Toggle the full width of the notebook', async ({
180+
page,
181+
browserName,
182+
tmpPath,
183+
}) => {
184+
const notebook = 'simple.ipynb';
185+
await page.contents.uploadFile(
186+
path.resolve(__dirname, `./notebooks/${notebook}`),
187+
`${tmpPath}/${notebook}`
188+
);
189+
await page.goto(`notebooks/${tmpPath}/${notebook}`);
190+
191+
const menuPath = 'View>Enable Full Width Notebook';
192+
await page.menu.clickMenuItem(menuPath);
193+
194+
const notebookPanel = page.locator('.jp-NotebookPanel').first();
195+
await expect(notebookPanel).toHaveClass(/jp-mod-fullwidth/);
196+
197+
// click to make the blue border around the cell disappear
198+
await page.click('.jp-WindowedPanel-outer');
199+
200+
// wait for the notebook to be ready
201+
await waitForNotebook(page, browserName);
202+
203+
expect(await page.screenshot()).toMatchSnapshot('notebook-full-width.png');
204+
205+
// undo the full width
206+
await page.menu.clickMenuItem(menuPath);
207+
await expect(notebookPanel).not.toHaveClass(/jp-mod-fullwidth/);
208+
});
178209
});
Loading
Loading
Loading

ui-tests/test/utils.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ export async function waitForKernelReady(page: Page): Promise<void> {
3030
}, true);
3131
return finished;
3232
});
33-
if (page.viewportSize()?.width > 600) {
33+
const viewport = page.viewportSize();
34+
const width = viewport?.width;
35+
if (width && width > 600) {
3436
await page.waitForSelector('.jp-DebuggerBugButton[aria-disabled="false"]');
3537
}
3638
}
@@ -44,3 +46,34 @@ export async function hideAddCellButton(page: Page): Promise<void> {
4446
.locator('.jp-Notebook-footer')
4547
.evaluate((element) => (element.style.display = 'none'));
4648
}
49+
50+
/**
51+
* Wait for the notebook to be ready
52+
*/
53+
export async function waitForNotebook(
54+
page: Page,
55+
browserName = ''
56+
): Promise<void> {
57+
// wait for the kernel status animations to be finished
58+
await waitForKernelReady(page);
59+
await page.waitForSelector(
60+
".jp-Notebook-ExecutionIndicator[data-status='idle']"
61+
);
62+
63+
const checkpointLocator = '.jp-NotebookCheckpoint';
64+
// wait for the checkpoint indicator to be displayed
65+
await page.waitForSelector(checkpointLocator);
66+
67+
// remove the amount of seconds manually since it might display strings such as "3 seconds ago"
68+
await page
69+
.locator(checkpointLocator)
70+
.evaluate(
71+
(element) => (element.innerHTML = 'Last Checkpoint: 3 seconds ago')
72+
);
73+
74+
// special case for firefox headless issue
75+
// see https://github.com/jupyter/notebook/pull/6872#issuecomment-1549594166 for more details
76+
if (browserName === 'firefox') {
77+
await hideAddCellButton(page);
78+
}
79+
}

0 commit comments

Comments
 (0)