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

feat: Add global shortcut to export logs #2336

Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"lint:packages": "eslint \"packages/*/src/**/*.{ts,tsx,js,jsx}\"",
"preview": "lerna run --scope=@deephaven/{code-studio,embed-widget} preview --stream",
"preview:app": "lerna run --scope=@deephaven/code-studio preview --stream",
"preview:embed-widget": "lerna run --scope=@deephaven/embed-widget preview --stream",
"prestart": "npm run build:necessary",
"start": "run-p watch:types start:*",
"start:app": "lerna run start --scope=@deephaven/code-studio --stream",
Expand Down
31 changes: 31 additions & 0 deletions packages/app-utils/src/components/AppBootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import '@deephaven/components/scss/BaseStyleSheet.scss';
import { ClientBootstrap } from '@deephaven/jsapi-bootstrap';
import { useBroadcastLoginListener } from '@deephaven/jsapi-components';
import { type Plugin } from '@deephaven/plugin';
import { exportLogs, logHistory } from '@deephaven/log';
import {
ContextActions,
ContextMenuRoot,
GLOBAL_SHORTCUTS,
} from '@deephaven/components';
import FontBootstrap from './FontBootstrap';
import PluginsBootstrap from './PluginsBootstrap';
import AuthBootstrap from './AuthBootstrap';
Expand All @@ -19,6 +25,9 @@ export type AppBootstrapProps = {
/** URL of the server. */
serverUrl: string;

/** Version of front-end. */
uiVersion: string;
ericlln marked this conversation as resolved.
Show resolved Hide resolved

/** URL of the plugins to load. */
pluginsUrl: string;

Expand All @@ -43,6 +52,7 @@ export function AppBootstrap({
pluginsUrl,
getCorePlugins,
serverUrl,
uiVersion,
children,
}: AppBootstrapProps): JSX.Element {
const clientOptions = useMemo(() => getConnectOptions(), []);
Expand All @@ -56,6 +66,25 @@ export function AppBootstrap({
});
}, []);
useBroadcastLoginListener(onLogin, onLogout);

const contextActions = [
{
// Exporting logs in a general context
action: () => {
exportLogs(
logHistory,
{
uiVersion,
userAgent: navigator.userAgent,
},
store.getState()
);
},
shortcut: GLOBAL_SHORTCUTS.EXPORT_LOGS,
isGlobal: true,
},
];
ericlln marked this conversation as resolved.
Show resolved Hide resolved

return (
<Provider store={store}>
<FontBootstrap fontClassNames={fontClassNames}>
Expand All @@ -78,10 +107,12 @@ export function AppBootstrap({
</UserBootstrap>
</ServerConfigBootstrap>
</AuthBootstrap>
<ContextActions actions={contextActions} />
</ClientBootstrap>
</ThemeBootstrap>
</PluginsBootstrap>
</FontBootstrap>
<ContextMenuRoot />
</Provider>
);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/code-studio/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const pluginsURL = new URL(
document.baseURI
);

const uiVersion = import.meta.env.npm_package_version;

// Lazy load the configs because it breaks initial page loads otherwise
async function getCorePlugins() {
const dashboardCorePlugins = await import(
Expand Down Expand Up @@ -69,6 +71,7 @@ ReactDOM.render(
getCorePlugins={getCorePlugins}
serverUrl={apiURL.origin}
pluginsUrl={pluginsURL.href}
uiVersion={uiVersion}
>
<AppRoot />
</AppBootstrap>
Expand Down
3 changes: 1 addition & 2 deletions packages/code-studio/src/main/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { type ReactElement } from 'react';
import { ContextMenuRoot, ToastContainer } from '@deephaven/components';
import { ToastContainer } from '@deephaven/components';
ericlln marked this conversation as resolved.
Show resolved Hide resolved
import AppMainContainer from './AppMainContainer';

function App(): ReactElement {
return (
<div className="app">
<AppMainContainer />
<ContextMenuRoot />
<ToastContainer />
</div>
);
Expand Down
27 changes: 25 additions & 2 deletions packages/code-studio/src/main/AppMainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import { getVariableDescriptor } from '@deephaven/jsapi-bootstrap';
import dh from '@deephaven/jsapi-shim';
import type { dh as DhType } from '@deephaven/jsapi-types';
import { type SessionConfig } from '@deephaven/jsapi-utils';
import Log from '@deephaven/log';
import Log, { exportLogs, logHistory } from '@deephaven/log';
import {
getActiveTool,
getWorkspace,
Expand All @@ -70,6 +70,7 @@ import {
type ServerConfigValues,
type CustomizableWorkspace,
type DashboardData,
store,
} from '@deephaven/redux';
import {
bindAllMethods,
Expand All @@ -92,7 +93,10 @@ import AppControlsMenu from './AppControlsMenu';
import { getLayoutStorage, getServerConfigValues } from '../redux';
import './AppMainContainer.scss';
import WidgetList, { type WindowMouseEvent } from './WidgetList';
import { getFormattedVersionInfo } from '../settings/SettingsUtils';
import {
getFormattedPluginInfo,
getFormattedVersionInfo,
} from '../settings/SettingsUtils';
import EmptyDashboard from './EmptyDashboard';

const log = Log.module('AppMainContainer');
Expand Down Expand Up @@ -194,6 +198,25 @@ export class AppMainContainer extends Component<

this.state = {
contextActions: [
{
action: () => {
// Exports logs with same details as using the button in settings
const { serverConfigValues, plugins } = this.props;
const pluginInfo = getFormattedPluginInfo(plugins);
exportLogs(
logHistory,
{
uiVersion: import.meta.env.npm_package_version,
userAgent: navigator.userAgent,
...Object.fromEntries(serverConfigValues),
pluginInfo,
},
store.getState()
);
},
shortcut: GLOBAL_SHORTCUTS.EXPORT_LOGS,
// Not global to prevent conflict with action with same shortcut in AppBootstrap.tsx
},
{
action: () => {
// Copies the version info to the clipboard for easy pasting into a ticket
Expand Down
7 changes: 7 additions & 0 deletions packages/components/src/shortcuts/GlobalShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ const GLOBAL_SHORTCUTS = {
macShortcut: [MODIFIER.CMD, MODIFIER.SHIFT, KEY.I],
isEditable: true,
}),
EXPORT_LOGS: ShortcutRegistry.createAndAdd({
id: 'GLOBAL.EXPORT_LOGS',
name: 'Export Logs',
shortcut: [MODIFIER.CTRL, MODIFIER.ALT, MODIFIER.SHIFT, KEY.L],
macShortcut: [MODIFIER.CMD, MODIFIER.OPTION, MODIFIER.SHIFT, KEY.L],
isEditable: true,
}),
NEXT: ShortcutRegistry.createAndAdd({
id: 'GLOBAL.NEXT',
name: 'Next',
Expand Down
3 changes: 3 additions & 0 deletions packages/embed-widget/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const pluginsURL = new URL(
document.baseURI
);

const uiVersion = import.meta.env.npm_package_version;

// Lazy load the configs because it breaks initial page loads otherwise
async function getCorePlugins() {
const dashboardCorePlugins = await import(
Expand All @@ -59,6 +61,7 @@ ReactDOM.render(
getCorePlugins={getCorePlugins}
serverUrl={apiURL.origin}
pluginsUrl={pluginsURL.href}
uiVersion={uiVersion}
>
<App />
</AppBootstrap>
Expand Down
2 changes: 1 addition & 1 deletion packages/embed-widget/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineConfig(({ mode }) => {

let port = Number.parseInt(env.PORT, 10);
if (Number.isNaN(port) || port <= 0) {
port = 4030;
port = 4010;
ericlln marked this conversation as resolved.
Show resolved Hide resolved
}

const baseURL = new URL(env.BASE_URL, `http://localhost:${port}/`);
Expand Down
24 changes: 16 additions & 8 deletions playwright-ci.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@ import DefaultConfig from './playwright.config';

const config: PlaywrightTestConfig = {
...DefaultConfig,
webServer: {
// Only start the main code-studio server right now
// To test embed-widget, should have an array set for `webServer` and run them all separately as there's a port check
command: 'BASE_URL=/ide/ npm run preview:app -- -- -- --no-open', // Passing flags through npm is fun
port: 4000,
timeout: 60 * 1000,
reuseExistingServer: false,
},
webServer: [
{
command: 'BASE_URL=/ide/ npm run preview:app -- -- -- --no-open', // Passing flags through npm is fun
port: 4000,
timeout: 60 * 1000,
reuseExistingServer: false,
},
{
command:
'BASE_URL=/iframe/widget/ npm run preview:embed-widget -- -- -- --no-open',
port: 4010,
timeout: 60 * 1000,
reuseExistingServer: false,
},
],

// Applies to the npm command and CI, but CI will get overwritten in the CI config
reporter: [['github'], ['html']],
};
Expand Down
33 changes: 33 additions & 0 deletions tests/shortcuts.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { test, expect } from '@playwright/test';
import { gotoPage } from './utils';

test('shortcut downloads logs', async ({ page }) => {
await gotoPage(page, '');

const downloadPromise = page.waitForEvent('download');
await page.keyboard.press('ControlOrMeta+Alt+Shift+KeyL');
const download = await downloadPromise;

expect(download).not.toBeNull();
});

test('shortcut downloads logs in full screen error', async ({ page }) => {
// Go to embed-widget page without url parameter to trigger a full screen error
await gotoPage(page, 'http://localhost:4010/');

const downloadPromise = page.waitForEvent('download');
await page.keyboard.press('ControlOrMeta+Alt+Shift+KeyL');
const download = await downloadPromise;

expect(download).not.toBeNull();
});

test('shortcut downloads logs in embeded-widget', async ({ page }) => {
await gotoPage(page, 'http://localhost:4010?name=all_types');

const downloadPromise = page.waitForEvent('download');
await page.keyboard.press('ControlOrMeta+Alt+Shift+KeyL');
const download = await downloadPromise;

expect(download).not.toBeNull();
});
Loading