Skip to content

Commit

Permalink
Backport PR #15331: Fix update button in extension manager (#15373)
Browse files Browse the repository at this point in the history
Co-authored-by: Nate Bowditch <111072326+nbowditch-einblick@users.noreply.github.com>
  • Loading branch information
meeseeksmachine and nbowditch-einblick committed Nov 8, 2023
1 parent 1052b5d commit 40d2a31
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 16 deletions.
2 changes: 1 addition & 1 deletion galata/test/documentation/data/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"homepage_url": "https://github.com/jupyterlab/jupyter-renderers",
"enabled": true,
"core": false,
"latest_version": "3.2.0",
"latest_version": "3.2.1",
"installed_version": "3.2.0",
"status": "ok",
"pkg_type": "prebuilt",
Expand Down
22 changes: 22 additions & 0 deletions galata/test/documentation/extension_manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,28 @@ test.describe('Extension Manager', () => {
await page.screenshot({ clip: { y: 31, x: 0, width: 283, height: 600 } })
).toMatchSnapshot('extensions_search.png');
});

test('Update button', async ({ page }) => {
await page.goto();
await openExtensionSidebar(page);

const waitRequest = page.waitForRequest(request => {
if (
request.method() !== 'POST' ||
!galata.Routes.extensions.test(request.url())
) {
return false;
}
const data = request.postDataJSON();
return (
data.cmd === 'install' &&
data.extension_name === '@jupyterlab/geojson-extension' &&
data.extension_version === '3.2.1'
);
});
await page.getByRole('button', { name: 'Update to 3.2.1' }).click();
await waitRequest;
});
});

test.describe('Filtered Extension Manager', () => {
Expand Down
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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 32 additions & 7 deletions packages/extensionmanager/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,19 @@ const EXTENSION_API_PATH = 'lab/api/extensions';
*/
export type Action = 'install' | 'uninstall' | 'enable' | 'disable';

/**
* Additional options for an action.
*/
export interface IActionOptions {
/**
* Install the version of the entry.
*
* #### Note
* This is ignored by all actions except install.
*/
useVersion?: string;
}

/**
* Model for an extension list.
*/
Expand Down Expand Up @@ -343,9 +356,13 @@ export class ListModel extends VDomModel {
* Install an extension.
*
* @param entry An entry indicating which extension to install.
* @param options Additional options for the action.
*/
async install(entry: IEntry): Promise<void> {
await this.performAction('install', entry).then(data => {
async install(
entry: IEntry,
options: { useVersion?: string } = {}
): Promise<void> {
await this.performAction('install', entry, options).then(data => {
if (data.status !== 'ok') {
reportInstallError(entry.name, data.message, this.translator);
}
Expand Down Expand Up @@ -480,19 +497,27 @@ export class ListModel extends VDomModel {
*
* @param action A valid action to perform.
* @param entry The extension to perform the action on.
* @param actionOptions Additional options for the action.
*/
protected performAction(
action: string,
entry: IEntry
entry: IEntry,
actionOptions: IActionOptions = {}
): Promise<IActionReply> {
const bodyJson: Record<string, string> = {
cmd: action,
extension_name: entry.name
};

if (actionOptions.useVersion) {
bodyJson['extension_version'] = actionOptions.useVersion;
}

const actionRequest = Private.requestAPI<IActionReply>(
{},
{
method: 'POST',
body: JSON.stringify({
cmd: action,
extension_name: entry.name
})
body: JSON.stringify(bodyJson)
}
);

Expand Down
38 changes: 30 additions & 8 deletions packages/extensionmanager/src/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Message } from '@lumino/messaging';
import { AccordionLayout, AccordionPanel } from '@lumino/widgets';
import * as React from 'react';
import ReactPaginate from 'react-paginate';
import { Action, IEntry, ListModel } from './model';
import { Action, IActionOptions, IEntry, ListModel } from './model';

const BADGE_SIZE = 32;
const BADGE_QUERY_SIZE = Math.floor(devicePixelRatio * BADGE_SIZE);
Expand Down Expand Up @@ -129,7 +129,11 @@ function ListEntry(props: ListEntry.IProperties): React.ReactElement<any> {
<>
{ListModel.entryHasUpdate(entry) && (
<Button
onClick={() => props.performAction!('install', entry)}
onClick={() =>
props.performAction!('install', entry, {
useVersion: entry.latest_version
})
}
title={trans.__(
'Update "%1" to "%2"',
entry.name,
Expand Down Expand Up @@ -216,7 +220,11 @@ namespace ListEntry {
*
* Not provided if actions are not allowed.
*/
performAction?: (action: Action, entry: IEntry) => void;
performAction?: (
action: Action,
entry: IEntry,
actionOptions?: IActionOptions
) => void;

/**
* The language translator.
Expand Down Expand Up @@ -318,7 +326,11 @@ namespace ListView {
*
* Not provided if actions are not allowed.
*/
performAction?: (action: Action, entry: IEntry) => void;
performAction?: (
action: Action,
entry: IEntry,
actionOptions?: IActionOptions
) => void;
}
}

Expand Down Expand Up @@ -504,11 +516,16 @@ class InstalledList extends ReactWidget {
*
* @param action The action to perform.
* @param entry The entry to perform the action on.
* @param actionOptions Additional options for the action.
*/
onAction(action: Action, entry: IEntry): Promise<void> {
onAction(
action: Action,
entry: IEntry,
actionOptions: IActionOptions = {}
): Promise<void> {
switch (action) {
case 'install':
return this.model.install(entry);
return this.model.install(entry, actionOptions);
case 'uninstall':
return this.model.uninstall(entry);
case 'enable':
Expand Down Expand Up @@ -544,11 +561,16 @@ class SearchResult extends ReactWidget {
*
* @param action The action to perform.
* @param entry The entry to perform the action on.
* @param actionOptions Additional options for the action.
*/
onAction(action: Action, entry: IEntry): Promise<void> {
onAction(
action: Action,
entry: IEntry,
actionOptions: IActionOptions = {}
): Promise<void> {
switch (action) {
case 'install':
return this.model.install(entry);
return this.model.install(entry, actionOptions);
case 'uninstall':
return this.model.uninstall(entry);
case 'enable':
Expand Down

0 comments on commit 40d2a31

Please sign in to comment.