Skip to content

Commit

Permalink
Add buttons to move the chat between sidepanel and main area
Browse files Browse the repository at this point in the history
  • Loading branch information
brichet committed Apr 22, 2024
1 parent 859a1ee commit 021606a
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 19 deletions.
15 changes: 15 additions & 0 deletions packages/jupyterlab-collaborative-chat/schema/chat-panel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "Jupyter collaborative chat",
"description": "Configuration for the chat commands",
"type": "object",
"jupyter.lab.toolbars": {
"Chat": [
{
"name": "moveToSide",
"command": "collaborative-chat:moveToSide"
}
]
},
"properties": {},
"additionalProperties": false
}
34 changes: 33 additions & 1 deletion packages/jupyterlab-collaborative-chat/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { Contents } from '@jupyterlab/services';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
import { launchIcon } from '@jupyterlab/ui-components';
import { Awareness } from 'y-protocols/awareness';

import {
Expand Down Expand Up @@ -109,7 +110,6 @@ export const docFactories: JupyterFrontEndPlugin<IWidgetConfig> = {
pluginIds.docFactories,
translator
);
console.log('Create toolbarFactory', toolbarFactory);
}

// Wait for the application to be restored and
Expand Down Expand Up @@ -326,6 +326,7 @@ const chatCommands: JupyterFrontEndPlugin<void> = {
}

if (inSidePanel && chatPanel) {
app.shell.activateById(chatPanel.id);
// The chat is opened in the chat panel.
const model = await drive.get(filepath);

Expand Down Expand Up @@ -442,6 +443,37 @@ const chatPanel: JupyterFrontEndPlugin<ChatPanel> = {
}
});

/*
* Command to move a chat from the main area to the side panel.
*
*/
commands.addCommand(CommandIDs.moveToSide, {
label: 'Move the chat to the side panel',
caption: 'Move the chat to the side panel',
icon: launchIcon,
execute: async () => {
const widget = app.shell.currentWidget;
// Ensure widget is a CollaborativeChatWidget and is in main area
if (
!widget ||
!(widget instanceof CollaborativeChatWidget) ||
!Array.from(app.shell.widgets('main')).includes(widget)
) {
console.error(
`The command '${CommandIDs.moveToSide}' should be executed from the toolbar button only`
);
return;
}
// Remove potential drive prefix
const filepath = widget.context.path.split(':').pop();
commands.execute(CommandIDs.openChat, {
filepath,
inSidePanel: true
});
widget.dispose();
}
});

return chatPanel;
}
};
Expand Down
6 changes: 5 additions & 1 deletion packages/jupyterlab-collaborative-chat/src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export const CommandIDs = {
/**
* Open a chat file.
*/
openChat: 'collaborative-chat:open'
openChat: 'collaborative-chat:open',
/**
* Move a main widget to the side panel
*/
moveToSide: 'collaborative-chat:moveToSide'
};

/**
Expand Down
35 changes: 27 additions & 8 deletions packages/jupyterlab-collaborative-chat/src/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ import {
closeIcon,
CommandToolbarButton,
HTMLSelect,
launchIcon,
PanelWithToolbar,
ReactWidget,
SidePanel,
ToolbarButton
} from '@jupyterlab/ui-components';
import { CommandRegistry } from '@lumino/commands';
import { Message } from '@lumino/messaging';
import { ISignal, Signal } from '@lumino/signaling';
import { AccordionPanel, Panel } from '@lumino/widgets';
import React, { useState } from 'react';

import { CollaborativeChatModel } from './model';
import { CommandIDs } from './token';
import { ISignal, Signal } from '@lumino/signaling';
import { Message } from '@lumino/messaging';
import { CommandIDs, chatFileType } from './token';

const MAIN_PANEL_CLASS = 'jp-collab-chat_main-panel';
const SIDEPANEL_CLASS = 'jp-collab-chat-sidepanel';
Expand All @@ -53,6 +54,7 @@ export class CollaborativeChatWidget extends DocumentWidget<
* Dispose of the resources held by the widget.
*/
dispose(): void {
this.context.dispose();
this.content.dispose();
super.dispose();
}
Expand Down Expand Up @@ -135,16 +137,17 @@ export class ChatPanel extends SidePanel {
rmRegistry: this._rmRegistry,
themeManager: this._themeManager
});
this.addWidget(new ChatSection({ widget, name }));
this.addWidget(new ChatSection({ name, widget, commands: this._commands }));
}

updateChatNames = async (): Promise<void> => {
const extension = chatFileType.extensions[0];
this._drive
.get('.')
.then(model => {
const chatsName = (model.content as any[])
.filter(f => f.type === 'file' && f.name.endsWith('.chat'))
.map(f => PathExt.basename(f.name, '.chat'));
.filter(f => f.type === 'file' && f.name.endsWith(extension))
.map(f => PathExt.basename(f.name, extension));
this._chatNamesChanged.emit(chatsName);
})
.catch(e => console.error('Error getting the chat files from drive', e));
Expand Down Expand Up @@ -174,7 +177,7 @@ export class ChatPanel extends SidePanel {
);
if (index === -1) {
this._commands.execute(CommandIDs.openChat, {
filepath: `${value}.chat`,
filepath: `${value}${chatFileType.extensions[0]}`,
inSidePanel: true
});
} else if (!this.widgets[index].isVisible) {
Expand Down Expand Up @@ -237,14 +240,29 @@ class ChatSection extends PanelWithToolbar {
this.title.caption = this._name;
this.toolbar.addClass(TOOLBAR_CLASS);

const moveToMain = new ToolbarButton({
icon: launchIcon,
iconLabel: 'Move the chat to the main area',
className: 'jp-mod-styled',
onClick: () => {
this.model.dispose();
options.commands.execute(CommandIDs.openChat, {
filepath: `${this._name}${chatFileType.extensions[0]}`
});
this.dispose();
}
});

const closeButton = new ToolbarButton({
icon: closeIcon,
iconLabel: 'Close the chat',
className: 'jp-mod-styled',
onClick: () => {
this.model.dispose();
this.dispose();
}
});
this.toolbar.addItem('collaborativeChat-main', moveToMain);
this.toolbar.addItem('collaborativeChat-close', closeButton);

this.addWidget(options.widget);
Expand Down Expand Up @@ -276,8 +294,9 @@ export namespace ChatSection {
* Options to build a chat section.
*/
export interface IOptions extends Panel.IOptions {
widget: ChatWidget;
commands: CommandRegistry;
name: string;
widget: ChatWidget;
}
}

Expand Down
10 changes: 8 additions & 2 deletions packages/jupyterlab-collaborative-chat/style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
*/

/*
See the JupyterLab Developer Guide for useful CSS Patterns:
See the JupyterLab Developer Guide for useful CSS Patterns:
https://jupyterlab.readthedocs.io/en/stable/developer/css.html
https://jupyterlab.readthedocs.io/en/stable/developer/css.html
*/

.jp-collab-chat_main-panel
.jp-ToolbarButtonComponent[data-command='collaborative-chat:moveToSide']
svg {
transform: rotate(180deg);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ const openChat = async (
return (await page.activity.getPanelLocator(filename)) as Locator;
};

const openChatToSide = async (
page: IJupyterLabPageFixture,
filename: string
): Promise<Locator> => {
const panel = page.locator('.jp-SidePanel.jp-collab-chat-sidepanel');
await page.evaluate(async filepath => {
const inSidePanel = true;
await window.jupyterapp.commands.execute('collaborative-chat:open', {
filepath,
inSidePanel
});
}, filename);
await expect(panel).toBeVisible();
return panel;
};

const openSettings = async (
page: IJupyterLabPageFixture,
globalSettings?: boolean
Expand All @@ -48,7 +64,9 @@ const openSettings = async (
return (await page.activity.getPanelLocator('Settings')) as Locator;
};

const openPanel = async (page: IJupyterLabPageFixture): Promise<Locator> => {
const openSidePanel = async (
page: IJupyterLabPageFixture
): Promise<Locator> => {
const panel = page.locator('.jp-SidePanel.jp-collab-chat-sidepanel');

if (!(await panel?.isVisible())) {
Expand Down Expand Up @@ -359,7 +377,7 @@ test.describe('#chatPanel', () => {
});

test('chat panel should contain a toolbar', async ({ page }) => {
const panel = await openPanel(page);
const panel = await openSidePanel(page);
const toolbar = panel.locator('.jp-SidePanel-toolbar');
await expect(toolbar).toHaveCount(1);

Expand All @@ -370,7 +388,7 @@ test.describe('#chatPanel', () => {
});

test('chat panel should not contain a chat at init', async ({ page }) => {
const panel = await openPanel(page);
const panel = await openSidePanel(page);
const content = panel.locator('.jp-SidePanel-content');
await expect(content).toBeEmpty();
});
Expand All @@ -382,7 +400,7 @@ test.describe('#chatPanel', () => {
let dialog: Locator;

test.beforeEach(async ({ page }) => {
panel = await openPanel(page);
panel = await openSidePanel(page);
const addButton = panel.locator(
'.jp-SidePanel-toolbar .jp-Toolbar-item.jp-collab-chat-add'
);
Expand Down Expand Up @@ -464,7 +482,7 @@ test.describe('#chatPanel', () => {
// reload to update the chat list
// FIX: add listener on file creation
await page.reload();
panel = await openPanel(page);
panel = await openSidePanel(page);
select = panel.locator(
'.jp-SidePanel-toolbar .jp-Toolbar-item.jp-collab-chat-open select'
);
Expand All @@ -480,7 +498,7 @@ test.describe('#chatPanel', () => {
// reload to update the chat list
// FIX: add listener on file creation
await page.reload();
panel = await openPanel(page);
panel = await openSidePanel(page);
select = panel.locator(
'.jp-SidePanel-toolbar .jp-Toolbar-item.jp-collab-chat-open select'
);
Expand All @@ -495,8 +513,73 @@ test.describe('#chatPanel', () => {
chatTitle.locator('.lm-AccordionPanel-titleLabel')
).toHaveText(name);

await chatTitle.getByRole('button').click();
await chatTitle.getByTitle('Close the chat').click();
await expect(chatTitle).toHaveCount(0);
});
});

test.describe('#movingChat', () => {
const filename = 'my-chat.chat';

test.use({ mockSettings: { ...galata.DEFAULT_SETTINGS } });

test.beforeEach(async ({ page }) => {
// Create a chat file
await page.filebrowser.contents.uploadContent('{}', 'text', filename);
});

test.afterEach(async ({ page }) => {
if (await page.filebrowser.contents.fileExists(filename)) {
await page.filebrowser.contents.deleteFile(filename);
}
});

test('main widget toolbar should have a button', async ({ page }) => {
const chatPanel = await openChat(page, filename);
const button = chatPanel.getByTitle('Move the chat to the side panel');
expect(button).toBeVisible();
expect(await button.screenshot()).toMatchSnapshot('moveToSide.png');
});

test('chat should move to the side panel', async ({ page }) => {
const chatPanel = await openChat(page, filename);
const button = chatPanel.getByTitle('Move the chat to the side panel');
await button.click();
await expect(chatPanel).not.toBeAttached();

const sidePanel = page.locator('.jp-SidePanel.jp-collab-chat-sidepanel');
await expect(sidePanel).toBeVisible();
const chatTitle = sidePanel.locator(
'.jp-SidePanel-content .jp-AccordionPanel-title'
);
await expect(chatTitle).toHaveCount(1);
await expect(
chatTitle.locator('.lm-AccordionPanel-titleLabel')
).toHaveText(filename.split('.')[0]);
});

test('side panel should contain a button to move the chat', async ({
page
}) => {
const sidePanel = await openChatToSide(page, filename);
const chatTitle = sidePanel
.locator('.jp-SidePanel-content .jp-AccordionPanel-title')
.first();
const button = chatTitle.getByTitle('Move the chat to the main area');
expect(button).toBeVisible();
expect(await button.screenshot()).toMatchSnapshot('moveToMain.png');
});

test('chat should move to the main area', async ({ page }) => {
const sidePanel = await openChatToSide(page, filename);
const chatTitle = sidePanel
.locator('.jp-SidePanel-content .jp-AccordionPanel-title')
.first();
const button = chatTitle.getByTitle('Move the chat to the main area');
await button.click();
expect(chatTitle).not.toBeAttached();

await expect(page.activity.getTabLocator(filename)).toBeVisible();
});
});
});
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.

0 comments on commit 021606a

Please sign in to comment.