Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 32 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
37aed34
add option to open in nbclassic if installed
andrii-i May 11, 2023
8896143
account that getOption() can return truthy 'false'
andrii-i May 11, 2023
e90306b
immidiately convert nbclassicInstalled to boolean
andrii-i May 11, 2023
1cd4f43
Capitalize NbClassic as in PyPI per @JasonWeill
andrii-i May 11, 2023
2122571
capitalize commandDescription
andrii-i May 11, 2023
ec1bd9b
Update packages/lab-extension/src/index.ts
andrii-i May 11, 2023
4078369
Update packages/lab-extension/src/index.ts
andrii-i May 11, 2023
b6b9549
Update packages/lab-extension/src/index.ts
andrii-i May 11, 2023
5b56f11
Update packages/lab-extension/src/index.ts
andrii-i May 11, 2023
0de8064
Update packages/lab-extension/src/index.ts
andrii-i May 11, 2023
f530d9c
Update packages/lab-extension/src/index.ts
andrii-i May 11, 2023
d418b8a
add openInNewIcon based on ##6793 by @JasonWeill
andrii-i May 12, 2023
abae288
show single 'open' button if only one option is available
andrii-i May 12, 2023
b442f2f
add button styling
andrii-i May 12, 2023
502f6d7
add css class to toolbarButton
andrii-i May 12, 2023
2b1e329
rename addCommand to addSwitcherCommand
andrii-i May 12, 2023
ed20a30
set page_config["nbclassic_installed"]
andrii-i May 12, 2023
6135319
Update snapshots
andrii-i May 17, 2023
9300e8d
fix general.spec.ts lint problem as detected in CI run https://github…
andrii-i May 17, 2023
98693c3
Use optional chaining instead of non-null assertion to fix lint error…
andrii-i May 17, 2023
81c1a1b
add new line to try to fix eslint error
andrii-i May 18, 2023
c2584cb
fix prettier CI errors
andrii-i May 18, 2023
5d0b3ad
Revert "fix general.spec.ts lint problem as detected in CI run https:…
andrii-i May 18, 2023
d802947
Revert "add openInNewIcon based on ##6793 by @JasonWeill"
andrii-i May 18, 2023
6f095b6
use launchIcon instead of openInNewIcon
andrii-i May 18, 2023
24bf9ec
fix nbclassic urlprefix
andrii-i May 18, 2023
f2f71e6
update general snapshots to account for use of launchIcon
andrii-i May 19, 2023
87a7708
update mobile snapshots to account for launchIcon
andrii-i May 19, 2023
3930a9b
check if extension is enabled, not installed
andrii-i Jun 2, 2023
7f0363d
add bundled server extension
andrii-i Jun 2, 2023
1d02d06
Revert "add bundled server extension"
andrii-i Jun 8, 2023
936f647
set page_config in initalize_handlers
andrii-i Jun 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions notebook/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,22 @@ def _default_user_settings_dir(self):
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($|\?)).*)",
Expand Down
7 changes: 7 additions & 0 deletions packages/lab-extension/schema/interface-switcher.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
"args": {
"isMenu": true
}
},
{
"command": "jupyter-notebook:open-nbclassic",
"rank": 10,
"args": {
"isMenu": true
}
}
]
}
Expand Down
113 changes: 78 additions & 35 deletions packages/lab-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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 (
Expand All @@ -90,7 +125,7 @@ const interfaceSwitcher: JupyterFrontEndPlugin<void> = {
);
};

const addInterface = (option: ISwitcherChoice) => {
const addSwitcherCommand = (option: ISwitcherChoice) => {
const { command, commandLabel, commandDescription, urlPrefix } = option;

const execute = () => {
Expand Down Expand Up @@ -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) {
andrii-i marked this conversation as resolved.
Show resolved Hide resolved
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
);
}
},
Expand Down
10 changes: 10 additions & 0 deletions packages/lab-extension/style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 modified ui-tests/test/general.spec.ts-snapshots/notebook-chromium-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ui-tests/test/general.spec.ts-snapshots/notebook-firefox-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ui-tests/test/menus.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 modified ui-tests/test/mobile.spec.ts-snapshots/notebook-chromium-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ui-tests/test/mobile.spec.ts-snapshots/notebook-firefox-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.