Add a setting to enable the notebook to take up the full width (#7487)
* Toggle full width * settings only * enable more ways to toggle * lint * fixes * add ui test * fix snapshots * reusable waitForNotebook * more updates * fix * update
27
packages/notebook-extension/schema/full-width-notebook.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"title": "Jupyter Notebook Full Width Notebook",
|
||||
"description": "Jupyter Notebook Notebook With settings",
|
||||
"jupyter.lab.menus": {
|
||||
"main": [
|
||||
{
|
||||
"id": "jp-mainmenu-view",
|
||||
"items": [
|
||||
{
|
||||
"command": "notebook:toggle-full-width",
|
||||
"rank": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"fullWidthNotebook": {
|
||||
"type": "boolean",
|
||||
"title": "Full Width Notebook",
|
||||
"description": "Whether to the notebook should take up the full width of the application",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object"
|
||||
}
|
@ -64,6 +64,11 @@ const KERNEL_STATUS_FADE_OUT_CLASS = 'jp-NotebookKernelStatus-fade';
|
||||
*/
|
||||
const SCROLLED_OUTPUTS_CLASS = 'jp-mod-outputsScrolled';
|
||||
|
||||
/**
|
||||
* The class for the full width notebook
|
||||
*/
|
||||
const FULL_WIDTH_NOTEBOOK_CLASS = 'jp-mod-fullwidth';
|
||||
|
||||
/**
|
||||
* The command IDs used by the notebook plugins.
|
||||
*/
|
||||
@ -72,6 +77,11 @@ namespace CommandIDs {
|
||||
* A command to open right sidebar for Editing Notebook Metadata
|
||||
*/
|
||||
export const openEditNotebookMetadata = 'notebook:edit-metadata';
|
||||
|
||||
/**
|
||||
* A command to toggle full width of the notebook
|
||||
*/
|
||||
export const toggleFullWidth = 'notebook:toggle-full-width';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,6 +212,83 @@ const openTreeTab: JupyterFrontEndPlugin<void> = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A plugin to set the notebook to full width.
|
||||
*/
|
||||
const fullWidthNotebook: JupyterFrontEndPlugin<void> = {
|
||||
id: '@jupyter-notebook/notebook-extension:full-width-notebook',
|
||||
description: 'A plugin to set the notebook to full width.',
|
||||
autoStart: true,
|
||||
requires: [INotebookTracker],
|
||||
optional: [ICommandPalette, ISettingRegistry, ITranslator],
|
||||
activate: (
|
||||
app: JupyterFrontEnd,
|
||||
tracker: INotebookTracker,
|
||||
palette: ICommandPalette | null,
|
||||
settingRegistry: ISettingRegistry | null,
|
||||
translator: ITranslator | null
|
||||
) => {
|
||||
const trans = (translator ?? nullTranslator).load('notebook');
|
||||
|
||||
let fullWidth = false;
|
||||
|
||||
const toggleFullWidth = () => {
|
||||
const current = tracker.currentWidget;
|
||||
fullWidth = !fullWidth;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
const content = current;
|
||||
content.toggleClass(FULL_WIDTH_NOTEBOOK_CLASS, fullWidth);
|
||||
};
|
||||
|
||||
let notebookSettings: ISettingRegistry.ISettings;
|
||||
|
||||
if (settingRegistry) {
|
||||
const loadSettings = settingRegistry.load(fullWidthNotebook.id);
|
||||
|
||||
const updateSettings = (settings: ISettingRegistry.ISettings): void => {
|
||||
const newFullWidth = settings.get('fullWidthNotebook')
|
||||
.composite as boolean;
|
||||
if (newFullWidth !== fullWidth) {
|
||||
toggleFullWidth();
|
||||
}
|
||||
};
|
||||
|
||||
Promise.all([loadSettings, app.restored])
|
||||
.then(([settings]) => {
|
||||
notebookSettings = settings;
|
||||
updateSettings(settings);
|
||||
settings.changed.connect((settings) => {
|
||||
updateSettings(settings);
|
||||
});
|
||||
})
|
||||
.catch((reason: Error) => {
|
||||
console.error(reason.message);
|
||||
});
|
||||
}
|
||||
|
||||
app.commands.addCommand(CommandIDs.toggleFullWidth, {
|
||||
label: trans.__('Enable Full Width Notebook'),
|
||||
execute: () => {
|
||||
toggleFullWidth();
|
||||
if (notebookSettings) {
|
||||
notebookSettings.set('fullWidthNotebook', fullWidth);
|
||||
}
|
||||
},
|
||||
isEnabled: () => tracker.currentWidget !== null,
|
||||
isToggled: () => fullWidth,
|
||||
});
|
||||
|
||||
if (palette) {
|
||||
palette.addItem({
|
||||
command: CommandIDs.toggleFullWidth,
|
||||
category: 'Notebook Operations',
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The kernel logo plugin.
|
||||
*/
|
||||
@ -597,6 +684,7 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
|
||||
closeTab,
|
||||
openTreeTab,
|
||||
editNotebookMetadata,
|
||||
fullWidthNotebook,
|
||||
kernelLogo,
|
||||
kernelStatus,
|
||||
notebookToolsWidget,
|
||||
|
@ -16,6 +16,16 @@
|
||||
- compact view on mobile
|
||||
*/
|
||||
|
||||
/* Make the notebook take up the full width of the page when jp-mod-fullwidth is set */
|
||||
|
||||
body[data-notebook='notebooks']
|
||||
.jp-NotebookPanel.jp-mod-fullwidth
|
||||
.jp-WindowedPanel-outer {
|
||||
padding-left: unset;
|
||||
padding-right: unset !important;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
/* Keep the notebook centered on the page */
|
||||
|
||||
body[data-notebook='notebooks'] .jp-NotebookPanel-toolbar {
|
||||
|
@ -7,7 +7,7 @@ import { expect } from '@jupyterlab/galata';
|
||||
|
||||
import { test } from './fixtures';
|
||||
|
||||
import { hideAddCellButton, waitForKernelReady } from './utils';
|
||||
import { waitForNotebook } from './utils';
|
||||
|
||||
test.describe('General', () => {
|
||||
test('The notebook should render', async ({ page, tmpPath, browserName }) => {
|
||||
@ -18,23 +18,6 @@ test.describe('General', () => {
|
||||
);
|
||||
await page.goto(`notebooks/${tmpPath}/${notebook}`);
|
||||
|
||||
// wait for the kernel status animations to be finished
|
||||
await waitForKernelReady(page);
|
||||
await page.waitForSelector(
|
||||
".jp-Notebook-ExecutionIndicator[data-status='idle']"
|
||||
);
|
||||
|
||||
const checkpointLocator = '.jp-NotebookCheckpoint';
|
||||
// wait for the checkpoint indicator to be displayed
|
||||
await page.waitForSelector(checkpointLocator);
|
||||
|
||||
// remove the amount of seconds manually since it might display strings such as "3 seconds ago"
|
||||
await page
|
||||
.locator(checkpointLocator)
|
||||
.evaluate(
|
||||
(element) => (element.innerHTML = 'Last Checkpoint: 3 seconds ago')
|
||||
);
|
||||
|
||||
// check the notebook footer shows up on hover
|
||||
const notebookFooter = '.jp-Notebook-footer';
|
||||
await page.hover(notebookFooter);
|
||||
@ -46,11 +29,8 @@ test.describe('General', () => {
|
||||
// click to make the blue border around the cell disappear
|
||||
await page.click('.jp-WindowedPanel-outer');
|
||||
|
||||
// special case for firefox headless issue
|
||||
// see https://github.com/jupyter/notebook/pull/6872#issuecomment-1549594166 for more details
|
||||
if (browserName === 'firefox') {
|
||||
await hideAddCellButton(page);
|
||||
}
|
||||
// wait for the notebook to be ready
|
||||
await waitForNotebook(page, browserName);
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot('notebook.png');
|
||||
});
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 50 KiB |
@ -7,7 +7,7 @@ import { expect } from '@jupyterlab/galata';
|
||||
|
||||
import { test } from './fixtures';
|
||||
|
||||
import { runAndAdvance, waitForKernelReady } from './utils';
|
||||
import { waitForNotebook, runAndAdvance, waitForKernelReady } from './utils';
|
||||
|
||||
const NOTEBOOK = 'example.ipynb';
|
||||
|
||||
@ -175,4 +175,35 @@ test.describe('Notebook', () => {
|
||||
|
||||
expect(page.isClosed());
|
||||
});
|
||||
|
||||
test('Toggle the full width of the notebook', async ({
|
||||
page,
|
||||
browserName,
|
||||
tmpPath,
|
||||
}) => {
|
||||
const notebook = 'simple.ipynb';
|
||||
await page.contents.uploadFile(
|
||||
path.resolve(__dirname, `./notebooks/${notebook}`),
|
||||
`${tmpPath}/${notebook}`
|
||||
);
|
||||
await page.goto(`notebooks/${tmpPath}/${notebook}`);
|
||||
|
||||
const menuPath = 'View>Enable Full Width Notebook';
|
||||
await page.menu.clickMenuItem(menuPath);
|
||||
|
||||
const notebookPanel = page.locator('.jp-NotebookPanel').first();
|
||||
await expect(notebookPanel).toHaveClass(/jp-mod-fullwidth/);
|
||||
|
||||
// click to make the blue border around the cell disappear
|
||||
await page.click('.jp-WindowedPanel-outer');
|
||||
|
||||
// wait for the notebook to be ready
|
||||
await waitForNotebook(page, browserName);
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot('notebook-full-width.png');
|
||||
|
||||
// undo the full width
|
||||
await page.menu.clickMenuItem(menuPath);
|
||||
await expect(notebookPanel).not.toHaveClass(/jp-mod-fullwidth/);
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 19 KiB |
@ -30,7 +30,9 @@ export async function waitForKernelReady(page: Page): Promise<void> {
|
||||
}, true);
|
||||
return finished;
|
||||
});
|
||||
if (page.viewportSize()?.width > 600) {
|
||||
const viewport = page.viewportSize();
|
||||
const width = viewport?.width;
|
||||
if (width && width > 600) {
|
||||
await page.waitForSelector('.jp-DebuggerBugButton[aria-disabled="false"]');
|
||||
}
|
||||
}
|
||||
@ -44,3 +46,34 @@ export async function hideAddCellButton(page: Page): Promise<void> {
|
||||
.locator('.jp-Notebook-footer')
|
||||
.evaluate((element) => (element.style.display = 'none'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the notebook to be ready
|
||||
*/
|
||||
export async function waitForNotebook(
|
||||
page: Page,
|
||||
browserName = ''
|
||||
): Promise<void> {
|
||||
// wait for the kernel status animations to be finished
|
||||
await waitForKernelReady(page);
|
||||
await page.waitForSelector(
|
||||
".jp-Notebook-ExecutionIndicator[data-status='idle']"
|
||||
);
|
||||
|
||||
const checkpointLocator = '.jp-NotebookCheckpoint';
|
||||
// wait for the checkpoint indicator to be displayed
|
||||
await page.waitForSelector(checkpointLocator);
|
||||
|
||||
// remove the amount of seconds manually since it might display strings such as "3 seconds ago"
|
||||
await page
|
||||
.locator(checkpointLocator)
|
||||
.evaluate(
|
||||
(element) => (element.innerHTML = 'Last Checkpoint: 3 seconds ago')
|
||||
);
|
||||
|
||||
// special case for firefox headless issue
|
||||
// see https://github.com/jupyter/notebook/pull/6872#issuecomment-1549594166 for more details
|
||||
if (browserName === 'firefox') {
|
||||
await hideAddCellButton(page);
|
||||
}
|
||||
}
|
||||
|