Add option to open a notebook in NbClassic if it is installed; show "Open in..." dropdown menu if there are multiple options, show single button otherwise (#6866)

* add option to open in nbclassic if installed

* account that getOption() can return truthy 'false'

* immidiately convert nbclassicInstalled to boolean

* Capitalize NbClassic as in PyPI per @JasonWeill

* capitalize commandDescription

* Update packages/lab-extension/src/index.ts

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>

* Update packages/lab-extension/src/index.ts

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>

* Update packages/lab-extension/src/index.ts

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>

* Update packages/lab-extension/src/index.ts

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>

* Update packages/lab-extension/src/index.ts

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>

* Update packages/lab-extension/src/index.ts

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>

* add openInNewIcon based on ##6793 by @JasonWeill

* show single 'open' button if only one option is available

* add button styling

* add css class to toolbarButton

* rename addCommand to addSwitcherCommand

* set page_config["nbclassic_installed"]

* Update snapshots

* fix general.spec.ts lint problem as detected in CI run https://github.com/jupyter/notebook/actions/runs/5008582825/jobs/8976612053?pr=6866

* Use optional chaining instead of non-null assertion to fix lint error https://github.com/jupyter/notebook/actions/runs/5008628805/jobs/8976713072?pr=6866

* add new line to try to fix eslint error

* fix prettier CI errors

* Revert "fix general.spec.ts lint problem as detected in CI run https://github.com/jupyter/notebook/actions/runs/5008582825/jobs/8976612053?pr=6866"

This reverts commit 9f0b54409f6a6b2c0be750f6ddaac1d435f622ae.

* Revert "add openInNewIcon based on ##6793 by @JasonWeill"

This reverts commit eca0a4e8fafe38307a1081b24615be0b0ab8502d.

* use launchIcon instead of openInNewIcon

* fix nbclassic urlprefix

* update general snapshots to account for use of launchIcon

* update mobile snapshots to account for launchIcon

* check if extension is enabled, not installed

* add bundled server extension

* Revert "add bundled server extension"

This reverts commit 7d261db4a987980b82bb8ca917ebc8f82d76e85d.

* set page_config in initalize_handlers

---------

Co-authored-by: Jason Weill <93281816+JasonWeill@users.noreply.github.com>
Andrii Ieroshenko 3 years ago committed by GitHub
parent 95baeab889
commit 92f2336793
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -261,8 +261,22 @@ class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp):
def _default_workspaces_dir(self):
return get_workspaces_dir()
def server_extension_is_enabled(self, extension):
"""Check if server extension is enabled."""
try:
extension_enabled = (
self.serverapp.extension_manager.extensions[extension].enabled is True
)
except (AttributeError, KeyError, TypeError):
extension_enabled = False
return extension_enabled
def initialize_handlers(self):
"""Initialize handlers."""
page_config = self.serverapp.web_app.settings.setdefault("page_config_data", {})
nbclassic_enabled = self.server_extension_is_enabled("nbclassic")
page_config["nbclassic_enabled"] = nbclassic_enabled
self.handlers.append(
(
rf"/{self.file_url_prefix}/((?!.*\.ipynb($|\?)).*)",

@ -22,6 +22,13 @@
"args": {
"isMenu": true
}
},
{
"command": "jupyter-notebook:open-nbclassic",
"rank": 10,
"args": {
"isMenu": true
}
}
]
}

@ -15,11 +15,15 @@ import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
import { ITranslator } from '@jupyterlab/translation';
import { Menu, MenuBar } from '@lumino/widgets';
import { Menu, MenuBar, Widget } from '@lumino/widgets';
import { INotebookShell } from '@jupyter-notebook/application';
import { caretDownIcon } from '@jupyterlab/ui-components';
import {
caretDownIcon,
CommandToolbarButton,
launchIcon,
} from '@jupyterlab/ui-components';
/**
* The command IDs used by the application plugin.
@ -39,6 +43,11 @@ namespace CommandIDs {
* Open in JupyterLab
*/
export const openLab = 'jupyter-notebook:open-lab';
/**
* Open in NbClassic
*/
export const openNbClassic = 'jupyter-notebook:open-nbclassic';
}
interface ISwitcherChoice {
@ -74,14 +83,40 @@ const interfaceSwitcher: JupyterFrontEndPlugin<void> = {
const { commands, shell } = app;
const baseUrl = PageConfig.getBaseUrl();
const trans = translator.load('notebook');
const overflowOptions = {
overflowMenuOptions: { isVisible: false },
};
const menubar = new MenuBar(overflowOptions);
const nbClassicEnabled =
PageConfig.getOption('nbclassic_enabled') === 'true';
const switcher = new Menu({ commands });
switcher.title.label = trans.__('Open in...');
switcher.title.icon = caretDownIcon;
menubar.addMenu(switcher);
const switcherOptions: ISwitcherChoice[] = [];
if (!notebookShell) {
switcherOptions.push({
command: CommandIDs.openNotebook,
commandLabel: trans.__('Notebook'),
commandDescription: trans.__('Open in %1', 'Jupyter Notebook'),
buttonLabel: 'openNotebook',
urlPrefix: `${baseUrl}tree/`,
});
}
if (!labShell) {
switcherOptions.push({
command: CommandIDs.openLab,
commandLabel: trans.__('JupyterLab'),
commandDescription: trans.__('Open in %1', 'JupyterLab'),
buttonLabel: 'openLab',
urlPrefix: `${baseUrl}doc/tree/`,
});
}
if (nbClassicEnabled) {
switcherOptions.push({
command: CommandIDs.openNbClassic,
commandLabel: trans.__('NbClassic'),
commandDescription: trans.__('Open in %1', 'NbClassic'),
buttonLabel: 'openNbClassic',
urlPrefix: `${baseUrl}nbclassic/notebooks/`,
});
}
const isEnabled = () => {
return (
@ -90,7 +125,7 @@ const interfaceSwitcher: JupyterFrontEndPlugin<void> = {
);
};
const addInterface = (option: ISwitcherChoice) => {
const addSwitcherCommand = (option: ISwitcherChoice) => {
const { command, commandLabel, commandDescription, urlPrefix } = option;
const execute = () => {
@ -121,40 +156,48 @@ const interfaceSwitcher: JupyterFrontEndPlugin<void> = {
args: { isPalette: true },
});
}
switcher.addItem({ command });
};
if (!notebookShell) {
addInterface({
command: CommandIDs.openNotebook,
commandLabel: trans.__('Notebook'),
commandDescription: trans.__('Open in %1', 'Jupyter Notebook'),
buttonLabel: 'openNotebook',
urlPrefix: `${baseUrl}tree/`,
});
}
switcherOptions.forEach((option) => {
const { command } = option;
addSwitcherCommand(option);
switcher.addItem({ command });
});
if (!labShell) {
addInterface({
command: CommandIDs.openLab,
commandLabel: trans.__('JupyterLab'),
commandDescription: trans.__('Open in %1', 'JupyterLab'),
buttonLabel: 'openLab',
urlPrefix: `${baseUrl}doc/tree/`,
});
let toolbarFactory: (panel: NotebookPanel) => Widget;
if (switcherOptions.length === 1) {
toolbarFactory = (panel: NotebookPanel) => {
const toolbarButton = new CommandToolbarButton({
commands,
id: switcherOptions[0].command,
label: switcherOptions[0].commandLabel,
icon: launchIcon,
});
toolbarButton.addClass('jp-nb-interface-switcher-button');
return toolbarButton;
};
} else {
const overflowOptions = {
overflowMenuOptions: { isVisible: false },
};
const menubar = new MenuBar(overflowOptions);
switcher.title.label = trans.__('Open in...');
switcher.title.icon = caretDownIcon;
menubar.addMenu(switcher);
toolbarFactory = (panel: NotebookPanel) => {
const menubar = new MenuBar(overflowOptions);
menubar.addMenu(switcher);
menubar.addClass('jp-InterfaceSwitcher');
return menubar;
};
}
if (toolbarRegistry) {
toolbarRegistry.addFactory<NotebookPanel>(
'Notebook',
'interfaceSwitcher',
(panel) => {
const menubar = new MenuBar(overflowOptions);
menubar.addMenu(switcher);
menubar.addClass('jp-InterfaceSwitcher');
return menubar;
}
toolbarFactory
);
}
},

@ -13,3 +13,13 @@
.jp-InterfaceSwitcher .lm-MenuBar-itemIcon svg {
vertical-align: sub;
}
.jp-nb-interface-switcher-button > .jp-ToolbarButtonComponent {
flex-direction: row-reverse;
}
.jp-nb-interface-switcher-button
> .jp-ToolbarButtonComponent
> .jp-ToolbarButtonComponent-icon {
padding-left: 3px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

@ -44,7 +44,7 @@ test.describe('Notebook Menus', () => {
const imageName = `opened-menu-${menuPath.replace(/>/g, '-')}.png`;
const menu = await page.menu.getOpenMenu();
expect(menu).toBeDefined();
expect(await menu!.screenshot()).toMatchSnapshot(imageName.toLowerCase());
expect(await menu?.screenshot()).toMatchSnapshot(imageName.toLowerCase());
});
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Loading…
Cancel
Save