Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 2 additions & 4 deletions packages/base/src/commands/BaseCommandIDs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@ export const newGeoTiffEntry = 'jupytergis:newGeoTiffEntry';
export const newGeoParquetEntry = 'jupytergis:newGeoParquetEntry';

// Layer and group actions
export const renameLayer = 'jupytergis:renameLayer';
export const removeLayer = 'jupytergis:removeLayer';
export const renameGroup = 'jupytergis:renameGroup';
export const removeGroup = 'jupytergis:removeGroup';
export const renameSelected = 'jupytergis:renameSelected';
export const removeSelected = 'jupytergis:removeSelected';
export const moveLayersToGroup = 'jupytergis:moveLayersToGroup';
export const moveLayerToNewGroup = 'jupytergis:moveLayerToNewGroup';

Expand Down
125 changes: 67 additions & 58 deletions packages/base/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
IJupyterGISModel,
JgisCoordinates,
LayerType,
SelectionType,
SourceType,
} from '@jupytergis/schema';
import { JupyterFrontEnd } from '@jupyterlab/application';
Expand Down Expand Up @@ -534,41 +533,41 @@ export function addCommands(
/**
* LAYERS and LAYER GROUP actions.
*/
commands.addCommand(CommandIDs.renameLayer, {
label: trans.__('Rename Layer'),
execute: async () => {
commands.addCommand(CommandIDs.renameSelected, {
label: trans.__('Rename'),
isEnabled: () => {
const model = tracker.currentWidget?.model;
await Private.renameSelectedItem(model, 'layer');
const selected = model?.localState?.selected?.value;
return !!selected && Object.keys(selected).length === 1;
},
});

commands.addCommand(CommandIDs.removeLayer, {
label: trans.__('Remove Layer'),
execute: () => {
execute: async () => {
const model = tracker.currentWidget?.model;
Private.removeSelectedItems(model, 'layer', selection => {
model?.removeLayer(selection);
});
const selected = model?.localState?.selected?.value;

commands.notifyCommandChanged(CommandIDs.toggleStoryPresentationMode);
if (!model || !selected) {
return;
}

await Private.renameSelectedItem(model);
},
});

commands.addCommand(CommandIDs.renameGroup, {
label: trans.__('Rename Group'),
execute: async () => {
commands.addCommand(CommandIDs.removeSelected, {
label: trans.__('Remove'),
isEnabled: () => {
const model = tracker.currentWidget?.model;
await Private.renameSelectedItem(model, 'group');
const selected = model?.localState?.selected?.value;
return !!selected && Object.keys(selected).length > 0;
},
});

commands.addCommand(CommandIDs.removeGroup, {
label: trans.__('Remove Group'),
execute: async () => {
const model = tracker.currentWidget?.model;
Private.removeSelectedItems(model, 'group', selection => {
model?.removeLayerGroup(selection);
});
const selected = model?.localState?.selected?.value;

if (!model || !selected) {
return;
}

await Private.removeSelectedItems(model);
},
});

Expand Down Expand Up @@ -659,24 +658,15 @@ export function addCommands(
label: trans.__('Rename Source'),
execute: async () => {
const model = tracker.currentWidget?.model;
await Private.renameSelectedItem(model, 'source');
await Private.renameSelectedItem(model);
},
});

commands.addCommand(CommandIDs.removeSource, {
label: trans.__('Remove Source'),
execute: () => {
const model = tracker.currentWidget?.model;
Private.removeSelectedItems(model, 'source', selection => {
if (!(model?.getLayersBySource(selection).length ?? true)) {
model?.sharedModel.removeSource(selection);
} else {
showErrorMessage(
'Remove source error',
'The source is used by a layer.',
);
}
});
Private.removeSelectedSources(model);
},
});

Expand Down Expand Up @@ -1230,52 +1220,71 @@ namespace Private {
};
}

export function removeSelectedItems(
model: IJupyterGISModel | undefined,
itemTypeToRemove: SelectionType,
removeFunction: (id: string) => void,
) {
export function removeSelectedItems(model: IJupyterGISModel | undefined) {
const selected = model?.localState?.selected?.value;

if (!selected) {
if (!selected || !model) {
console.error('Failed to remove selected item -- nothing selected');
return;
}

for (const selection in selected) {
if (selected[selection].type === itemTypeToRemove) {
removeFunction(selection);
for (const id of Object.keys(selected)) {
const item = selected[id];

switch (item.type) {
case 'layer':
model.removeLayer(id);
break;
case 'group':
model.removeLayerGroup(id);
break;
}
}
}

export async function renameSelectedItem(
model: IJupyterGISModel | undefined,
itemType: SelectionType,
) {
const selectedItems = model?.localState?.selected.value;
const selectedItems = model?.localState?.selected?.value;

if (!selectedItems || !model) {
console.error(`No ${itemType} selected`);
console.error('No item selected');
return;
}

let itemId = '';
const ids = Object.keys(selectedItems);
if (ids.length === 0) {
return;
}

// If more then one item is selected, only rename the first
for (const id in selectedItems) {
if (selectedItems[id].type === itemType) {
itemId = id;
break;
}
const itemId = ids[0];
const item = selectedItems[itemId];

if (!item.type) {
return;
}

if (!itemId) {
model.setEditingItem(item.type, itemId);
}

export function removeSelectedSources(model: IJupyterGISModel | undefined) {
const selected = model?.localState?.selected?.value;

if (!selected || !model) {
return;
}

// Set editing state - component will show inline input
model.setEditingItem(itemType, itemId);
for (const id of Object.keys(selected)) {
if (model.getLayersBySource(id).length > 0) {
showErrorMessage(
'Remove source error',
'The source is used by a layer.',
);
continue;
}

model.sharedModel.removeSource(id);
}
}

export function executeConsole(tracker: JupyterGISTracker): void {
Expand Down
18 changes: 4 additions & 14 deletions packages/base/src/keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,14 @@
"selector": ".data-jgis-keybinding .jp-gis-source"
},
{
"command": "jupytergis:removeLayer",
"command": "jupytergis:removeSelected",
"keys": ["Delete"],
"selector": ".data-jgis-keybinding .jp-gis-layerItem"
},
{
"command": "jupytergis:renameLayer",
"keys": ["F2"],
"selector": ".jp-gis-layerItem"
},
{
"command": "jupytergis:removeGroup",
"keys": ["Delete"],
"selector": ".data-jgis-keybinding .jp-gis-layerGroupHeader"
"selector": ".data-jgis-keybinding"
},
{
"command": "jupytergis:renameGroup",
"command": "jupytergis:renameSelected",
"keys": ["F2"],
"selector": ".jp-gis-layerGroupHeader"
"selector": ".data-jgis-keybinding"
},
{
"command": "jupytergis:executeConsole",
Expand Down
26 changes: 14 additions & 12 deletions python/jupytergis_lab/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
);
};

const LAYER = '.jp-gis-layerItem:not(.jp-gis-layerGroup)';

createDefaultLayerRegistry(layerBrowserRegistry);
const stateDbManager = GlobalStateDbManager.getInstance();
stateDbManager.initialize(state);
Expand Down Expand Up @@ -87,32 +89,32 @@ const plugin: JupyterFrontEndPlugin<void> = {
// LAYERS and LAYER GROUPS context menu
app.contextMenu.addItem({
command: CommandIDs.symbology,
selector: '.jp-gis-layerItem',
selector: LAYER,
rank: 1,
});

// Separator
app.contextMenu.addItem({
type: 'separator',
selector: '.jp-gis-layerPanel',
selector: LAYER,
rank: 1,
});

app.contextMenu.addItem({
command: CommandIDs.removeLayer,
selector: '.jp-gis-layerItem',
command: CommandIDs.removeSelected,
selector: LAYER,
rank: 2,
});

app.contextMenu.addItem({
command: CommandIDs.renameLayer,
selector: '.jp-gis-layerItem',
command: CommandIDs.renameSelected,
selector: LAYER,
rank: 2,
});

app.contextMenu.addItem({
command: CommandIDs.zoomToLayer,
selector: '.jp-gis-layerItem',
selector: LAYER,
rank: 2,
});

Expand All @@ -128,7 +130,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
// Add the Download submenu to the context menu
app.contextMenu.addItem({
type: 'submenu',
selector: '.jp-gis-layerItem',
selector: LAYER,
rank: 2,
submenu: downloadSubmenu,
});
Expand All @@ -148,7 +150,7 @@ const plugin: JupyterFrontEndPlugin<void> = {

app.contextMenu.addItem({
type: 'submenu',
selector: '.jp-gis-layerItem',
selector: LAYER,
rank: 2,
submenu: processingSubmenu,
});
Expand All @@ -161,7 +163,7 @@ const plugin: JupyterFrontEndPlugin<void> = {

app.contextMenu.addItem({
type: 'submenu',
selector: '.jp-gis-layerItem',
selector: LAYER,
rank: 2,
submenu: moveLayerSubmenu,
});
Expand All @@ -171,13 +173,13 @@ const plugin: JupyterFrontEndPlugin<void> = {
);

app.contextMenu.addItem({
command: CommandIDs.removeGroup,
command: CommandIDs.removeSelected,
selector: '.jp-gis-layerGroupHeader',
rank: 2,
});

app.contextMenu.addItem({
command: CommandIDs.renameGroup,
command: CommandIDs.renameSelected,
selector: '.jp-gis-layerGroupHeader',
rank: 2,
});
Expand Down
12 changes: 6 additions & 6 deletions ui-tests/tests/contextmenu.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { expect, galata, test } from '@jupyterlab/galata';
import path from 'path';

Expand All @@ -24,14 +24,14 @@
.getByText('Open Topo Map')
.click({ button: 'right' });

const text = page.getByRole('menu').getByText('Remove Layer');
const text = page.getByRole('menu').getByText('Remove');
await expect(text).toBeVisible();
});

test('right click on group should open group menu', async ({ page }) => {
await page.getByText('level 1 group').click({ button: 'right' });

const text = page.getByRole('menu').getByText('Remove Group');
const text = page.getByRole('menu').getByText('Remove');
await expect(text).toBeVisible();
});

Expand Down Expand Up @@ -74,7 +74,7 @@
await expect(page.getByText('new group', { exact: true })).toHaveCount(1);
});

test('clicking remove layer should remove the layer from the tree', async ({
test('clicking remove should remove the layer from the tree', async ({
page,
}) => {
// Create new layer first
Expand Down Expand Up @@ -102,12 +102,12 @@
button: 'right',
});

await page.getByRole('menu').getByText('Remove Layer').click();
await page.getByRole('menu').getByText('Remove').click();

expect(layerInTree).not.toBeVisible();
});

test('clicking remove group should remove the group from the tree', async ({
test('clicking remove should remove the group from the tree', async ({
page,
}) => {
const firstItem = page
Expand All @@ -119,7 +119,7 @@
.getByText('level 1 group')
.click({ button: 'right' });

await page.getByRole('menu').getByText('Remove Group').click();
await page.getByRole('menu').getByText('Remove').click();
await expect(firstItem).not.toBeVisible();
});

Expand All @@ -135,7 +135,7 @@
);

// Select the layer
await layerText.click();

Check failure on line 138 in ui-tests/tests/contextmenu.spec.ts

View workflow job for this annotation

GitHub Actions / Integration tests

[chromium] › tests/contextmenu.spec.ts:126:7 › context menu › pressing F2 should start rename for layer

1) [chromium] › tests/contextmenu.spec.ts:126:7 › context menu › pressing F2 should start rename for layer Error: locator.click: Test timeout of 60000ms exceeded. Call log: - waiting for getByLabel('Layers', { exact: true }).getByText('Open Topo Map') 136 | 137 | // Select the layer > 138 | await layerText.click(); | ^ 139 | await page.waitForTimeout(1000); 140 | await expect(layerItem).toHaveClass(/jp-mod-selected/); 141 | at /home/runner/work/jupytergis/jupytergis/ui-tests/tests/contextmenu.spec.ts:138:21
await page.waitForTimeout(1000);
await expect(layerItem).toHaveClass(/jp-mod-selected/);

Expand Down
Loading