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 new deploy app command #1

Merged
merged 20 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
2 changes: 1 addition & 1 deletion .copier-answers.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
_commit: v4.3.5
_src_path: https://github.com/jupyterlab/extension-template
author_email: nbyrne@quansight.com
author_email: internal-it@quansight.com
author_name: Nick Byrne
has_binder: true
has_settings: true
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,6 @@ dmypy.json

# virtualenv
.venv/

# JUnit test results
junit.xml
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
[![Github Actions Status](https://github.com/nebari-dev/jupyterlab-jhub-apps/workflows/Build/badge.svg)](https://github.com/nebari-dev/jupyterlab-jhub-apps/actions/workflows/build.yml)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/nebari-dev/jupyterlab-jhub-apps/main?urlpath=lab)

Customizations for [jhub-apps](https://github.com/nebari-dev/jhub-apps).

Customizations for jhub-apps.
## Plugins

- `jhub-apps:deploy-app`: Adds a command to deploy an app from the current notebook. This command is available from the main menu and context menu, as well as a toolbar icon.

## Requirements

Expand Down
42 changes: 42 additions & 0 deletions schema/commands.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"jupyter.lab.menus": {
"main": [
{
"id": "jp-mainmenu-services",
"label": "Services",
"rank": 1000,
"items": [
{
"command": "jhub-apps:deploy-app",
"args": {
"origin": "main-menu"
}
}
]
}
],
"context": [
{
"command": "jhub-apps:deploy-app",
"args": {
"origin": "context-menu"
},
"selector": ".jp-DirListing-item[data-isdir=\"false\"]",
"rank": 3
}
]
},
"jupyter.lab.toolbars": {
"Notebook": [
{
"name": "deploy-app",
"command": "jhub-apps:deploy-app"
}
]
},
"title": "jupyterlab-jhub-apps",
"description": "jupyterlab-jhub-apps custom command settings.",
"type": "object",
"properties": {},
"additionalProperties": false
}
8 changes: 0 additions & 8 deletions schema/plugin.json

This file was deleted.

7 changes: 7 additions & 0 deletions src/icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LabIcon } from '@jupyterlab/ui-components';
import deployApp from '../style/icons/deploy-app.svg';

export const deployAppIcon = new LabIcon({
name: 'jhub-apps:deploy-app',
svgstr: deployApp
});
80 changes: 58 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,67 @@ import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { deployAppIcon } from './icons';
import { DocumentWidget } from '@jupyterlab/docregistry';

import { ISettingRegistry } from '@jupyterlab/settingregistry';
namespace CommandIDs {
/**
* Opens the URL to deploy the application with pre-populated fields
*/
export const deployApp = 'jhub-apps:deploy-app';
}

/**
* Initialization data for the jupyterlab-jhub-apps extension.
*/
const plugin: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-jhub-apps:plugin',
description: 'Customizations for jhub-apps.',
interface IDeployAppArgs {
/**
* The origin of the command e.g. main-menu, context-menu, etc.
*/
origin?: string;
}

const jhubAppsPlugin: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-jhub-apps:commands',
description: 'Adds additional commands used by jhub-apps.',
autoStart: true,
optional: [ISettingRegistry],
activate: (app: JupyterFrontEnd, settingRegistry: ISettingRegistry | null) => {
console.log('JupyterLab extension jupyterlab-jhub-apps is activated!');

if (settingRegistry) {
settingRegistry
.load(plugin.id)
.then(settings => {
console.log('jupyterlab-jhub-apps settings loaded:', settings.composite);
})
.catch(reason => {
console.error('Failed to load settings for jupyterlab-jhub-apps.', reason);
});
}
requires: [],
nenb marked this conversation as resolved.
Show resolved Hide resolved
activate: async (app: JupyterFrontEnd) => {
const openURL = async (url: string) => {
nenb marked this conversation as resolved.
Show resolved Hide resolved
try {
window.open(url, '_blank', 'noopener,noreferrer');
} catch (error) {
console.warn(`Error opening ${url}: ${error}`);
}
};

const calculateIcon = (args: IDeployAppArgs) => {
switch (args.origin) {
case 'main-menu':
return undefined;
case 'context-menu':
return deployAppIcon;
case undefined:
return deployAppIcon;
default:
return deployAppIcon;
nenb marked this conversation as resolved.
Show resolved Hide resolved
}
};

app.commands.addCommand(CommandIDs.deployApp, {
execute: async () => {
const currentWidget = app.shell.currentWidget;
const currentNotebookPath =
currentWidget && currentWidget instanceof DocumentWidget
? currentWidget.context.path
: '';
const deployUrl = `/services/japps/create-app?filepath=${encodeURIComponent(currentNotebookPath)}`;

await openURL(deployUrl);
},
label: 'Deploy App',
icon: calculateIcon
});
}
};

export default plugin;
const plugins = [jhubAppsPlugin];

export default plugins;
4 changes: 4 additions & 0 deletions src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.svg' {
const script: string;
export default script;
}
3 changes: 3 additions & 0 deletions style/icons/deploy-app.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 82 additions & 13 deletions ui-tests/tests/jupyterlab_jhub_apps.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,90 @@
import { expect, test } from '@jupyterlab/galata';

/**
* Don't load JupyterLab webpage before running the tests.
* This is required to ensure we capture all log messages.
*/
test.use({ autoGoto: false });
test('should have Deploy App entry in Services menu', async ({ page }) => {
await page.click('text=Services');

test('should emit an activation console message', async ({ page }) => {
const logs: string[] = [];
const deployAppEntry = page.locator('.lm-Menu-item:has-text("Deploy App")');
await expect(deployAppEntry).toBeVisible();

page.on('console', message => {
logs.push(message.text());
const servicesMenu = page.locator('.lm-Menu-content');
expect(await servicesMenu.screenshot()).toMatchSnapshot(
'services-menu-with-deploy-app.png'
);
});

test('should have Deploy App icon in notebook toolbar', async ({ page }) => {
await page.click('text=Python 3');

await page.waitForSelector('.jp-NotebookPanel-toolbar');

const deployAppIcon = page.locator(
'.jp-Toolbar-item[data-jp-item-name="deploy-app"]'
);
await expect(deployAppIcon).toBeVisible();

const notebookToolbar = page.locator('.jp-NotebookPanel-toolbar');
expect(await notebookToolbar.screenshot()).toMatchSnapshot(
'notebook-toolbar-before-click.png'
);
});

test('should show Deploy App option in context menu', async ({ page }) => {
await page.click('text=Python 3');
await page.waitForSelector('.jp-NotebookPanel');

await page.waitForSelector('.jp-DirListing-item[data-isdir="false"]');

const notebookItem = page
.locator('.jp-DirListing-item[data-isdir="false"]')
.first();
await notebookItem.click({ button: 'right' });

const deployAppOption = page.locator('.lm-Menu-item:has-text("Deploy App")');
await expect(deployAppOption).toBeVisible();

const contextMenu = page.locator('.lm-Menu-content');
expect(await contextMenu.screenshot()).toMatchSnapshot(
'notebook-context-menu-with-deploy-app.png'
);
});

test('should open new tab with correct URL when deploy-app command is executed', async ({
page,
context
}) => {
const newPagePromise = context.waitForEvent('page');

await page.evaluate(() => {
window.jupyterapp.commands.execute('jhub-apps:deploy-app');
});

await page.goto();
const newPage = await newPagePromise;

expect(
logs.filter(s => s === 'JupyterLab extension jupyterlab-jhub-apps is activated!')
).toHaveLength(1);
await newPage.waitForLoadState('load');

expect(newPage.url()).toBe(
'http://localhost:8888/services/japps/create-app?filepath='
nenb marked this conversation as resolved.
Show resolved Hide resolved
);

await newPage.close();
});

test.describe('should register custom commands', () => {
test('jhub-apps:deploy-app command works', async ({ page }) => {
const deployAppMainMenu = await page.evaluate(async () => {
const registry = window.jupyterapp.commands;
const id = 'jhub-apps:deploy-app';
const args = { origin: 'main-menu' };

return {
id,
label: registry.label(id, args),
isEnabled: registry.isEnabled(id, args)
};
});

expect(deployAppMainMenu.label).toBe('Deploy App');

expect(deployAppMainMenu.isEnabled).toBe(true);
});
});
nenb marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this did not work. Maybe needs a timeout?

Looking at the darwin snapshot:

  • the kernel busy indicator (the black circle) will flip on and off depending on the kernel state; we may want to ensure that kernel is in idle state (have a look at the tests in JupyterLab to see how to do that)
  • the deploy icon looks great here; I think the mockups also included a "Deploy App" label when displayed in the toolbar, do you want to include it straight away or in a follow-up PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add the "Deploy App" label in the toolbar in a follow-up PR, just to try and get this PR merged.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading