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: Theme Selector #1661

Merged
merged 13 commits into from
Dec 6, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ packages/*/package-lock.json
/playwright/.cache/
/tests/*-snapshots/*
!/tests/*-snapshots/*-linux*
vite.config.local.ts
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,45 @@ If your DHC address is different from the default `http://localhost:10000`, edit
VITE_PROXY_URL=http://<dhc-host>:<port>
```

## Local Plugin Development
The plugins repo supports [serving plugins locally](https://github.com/deephaven/deephaven-plugins/blob/main/README.md#serve-plugins). DHC can be configured to proxy `js-plugins`requests to the local dev server by setting `VITE_JS_PLUGINS_DEV_PORT` in `packages/code-studio/.env.development.local`.

e.g. To point to the default dev port:

```
VITE_JS_PLUGINS_DEV_PORT=4100
```

## Local Vite Config
If you'd like to override the vite config for local dev, you can define a `packages/code-studio/vite.config.local.ts` file that extends from `vite.config.ts`. This file is excluded via `.gitignore` which makes it easy to keep local overrides in tact.

The config can be used by running:

`npm run start:app -- -- -- --config=vite.config.local.ts`

For example, to proxy `js-plugins` requests to a local server, you could use this `vite.config.local.ts`:

```typescript
export default defineConfig((config: ConfigEnv) => {
const baseConfig = (createBaseConfig as UserConfigFn)(config) as UserConfig;

return {
...baseConfig,
server: {
...baseConfig.server,
proxy: {
...baseConfig.server?.proxy,
'/js-plugins': {
target: 'http://localhost:5173',
changeOrigin: true,
rewrite: path => path.replace(/^\/js-plugins/, ''),
},
},
},
};
});
```

## Debugging from VSCode

We have a pre-defined launch config that lets you set breakpoints directly in VSCode for debugging browser code. The `Launch Deephaven` config will launch a new Chrome window that stores its data in your repo workspace. With this setup, you only need to install the React and Redux devtool extensions once. They will persist to future launches using the launch config.
Expand Down
4 changes: 4 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/app-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@deephaven/auth-plugins": "file:../auth-plugins",
"@deephaven/chart": "file:../chart",
"@deephaven/components": "file:../components",
"@deephaven/console": "file:../console",
"@deephaven/dashboard": "file:../dashboard",
"@deephaven/icons": "file:../icons",
"@deephaven/iris-grid": "file:../iris-grid",
Expand Down
5 changes: 2 additions & 3 deletions packages/app-utils/src/components/AppBootstrap.test.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import React, { useContext } from 'react';
import { act, render, screen } from '@testing-library/react';
import { AUTH_HANDLER_TYPE_ANONYMOUS } from '@deephaven/auth-plugins';
import { ApiContext } from '@deephaven/jsapi-bootstrap';
import { PluginModuleMap, PluginsContext } from '@deephaven/plugin';
import { BROADCAST_LOGIN_MESSAGE } from '@deephaven/jsapi-utils';
import type {
CoreClient,
IdeConnection,
dh as DhType,
} from '@deephaven/jsapi-types';
import { TestUtils } from '@deephaven/utils';
import { act, render, screen } from '@testing-library/react';
import AppBootstrap from './AppBootstrap';
import { PluginsContext } from './PluginsBootstrap';
import { PluginModuleMap } from '../plugins';

const { asMock } = TestUtils;

Expand Down
11 changes: 8 additions & 3 deletions packages/app-utils/src/components/ThemeBootstrap.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useContext, useMemo } from 'react';
import { ChartThemeProvider } from '@deephaven/chart';
import { MonacoThemeProvider } from '@deephaven/console';
import { ThemeProvider } from '@deephaven/components';
import { PluginsContext } from '@deephaven/plugin';
import { getThemeDataFromPlugins } from '../plugins';
import { IrisGridThemeProvider } from '@deephaven/iris-grid';
import { getThemeDataFromPlugins, PluginsContext } from '@deephaven/plugin';

export interface ThemeBootstrapProps {
children: React.ReactNode;
Expand All @@ -22,7 +23,11 @@ export function ThemeBootstrap({ children }: ThemeBootstrapProps): JSX.Element {

return (
<ThemeProvider themes={themes}>
<ChartThemeProvider>{children}</ChartThemeProvider>
<ChartThemeProvider>
<MonacoThemeProvider>
<IrisGridThemeProvider>{children}</IrisGridThemeProvider>
</MonacoThemeProvider>
</ChartThemeProvider>
</ThemeProvider>
);
}
Expand Down
104 changes: 0 additions & 104 deletions packages/app-utils/src/plugins/PluginUtils.test.tsx

This file was deleted.

37 changes: 0 additions & 37 deletions packages/app-utils/src/plugins/PluginUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getThemeKey, ThemeData } from '@deephaven/components';
import Log from '@deephaven/log';
import {
type PluginModuleMap,
Expand All @@ -11,8 +10,6 @@ import {
PluginType,
isLegacyAuthPlugin,
isLegacyPlugin,
isThemePlugin,
ThemePlugin,
} from '@deephaven/plugin';
import loadRemoteModule from './loadRemoteModule';

Expand Down Expand Up @@ -172,37 +169,3 @@ export function getAuthPluginComponent(

return component;
}

/**
* Extract theme data from theme plugins in the given plugin map.
* @param pluginMap
*/
export function getThemeDataFromPlugins(
pluginMap: PluginModuleMap
): ThemeData[] {
const themePluginEntries = [...pluginMap.entries()].filter(
(entry): entry is [string, ThemePlugin] => isThemePlugin(entry[1])
);

log.debug('Getting theme data from plugins', themePluginEntries);

return themePluginEntries
.map(([pluginName, plugin]) => {
// Normalize to an array since config can be an array of configs or a
// single config
const configs = Array.isArray(plugin.themes)
? plugin.themes
: [plugin.themes];

return configs.map(
({ name, baseTheme, styleContent }) =>
({
baseThemeKey: `default-${baseTheme ?? 'dark'}`,
themeKey: getThemeKey(pluginName, name),
name,
styleContent,
}) as const
);
})
.flat();
}
1 change: 1 addition & 0 deletions packages/app-utils/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
{ "path": "../auth-plugins" },
{ "path": "../chart" },
{ "path": "../components" },
{ "path": "../console" },
{ "path": "../dashboard" },
{ "path": "../iris-grid" },
{ "path": "../jsapi-bootstrap" },
Expand Down
40 changes: 39 additions & 1 deletion packages/code-studio/src/settings/SettingsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ import {
vsRecordKeys,
vsInfo,
vsLayers,
vsPaintcan,
dhUserIncognito,
dhUser,
} from '@deephaven/icons';
import { Button, CopyButton, Tooltip } from '@deephaven/components';
import {
Button,
CopyButton,
ThemeContext,
ThemePicker,
Tooltip,
} from '@deephaven/components';
import { ServerConfigValues, User } from '@deephaven/redux';
import {
BROADCAST_CHANNEL_NAME,
BROADCAST_LOGOUT_MESSAGE,
makeMessage,
} from '@deephaven/jsapi-utils';
import { assertNotNull } from '@deephaven/utils';
import Logo from './community-wordmark-app.svg';
import FormattingSectionContent from './FormattingSectionContent';
import LegalNotice from './LegalNotice';
Expand Down Expand Up @@ -51,6 +59,8 @@ export class SettingsMenu extends Component<

static SHORTCUT_SECTION_KEY = 'SettingsMenu.shortcuts';

static THEME_SECTION_KEY = 'SettingsMenu.theme';

static focusFirstInputInContainer(container: HTMLDivElement | null): void {
const input = container?.querySelector('input, select, textarea');
if (input) {
Expand Down Expand Up @@ -232,6 +242,34 @@ export class SettingsMenu extends Component<
<ColumnSpecificSectionContent scrollTo={this.handleScrollTo} />
</SettingsMenuSection>

<ThemeContext.Consumer>
{contextValue => {
assertNotNull(contextValue, 'ThemeContext value is null');

return contextValue.themes.length > 1 ? (
<SettingsMenuSection
sectionKey={SettingsMenu.THEME_SECTION_KEY}
isExpanded={this.isSectionExpanded(
SettingsMenu.THEME_SECTION_KEY
)}
onToggle={this.handleSectionToggle}
title={
<>
<FontAwesomeIcon
icon={vsPaintcan}
transform="grow-4"
className="mr-2"
/>
Theme
</>
}
>
<ThemePicker />
</SettingsMenuSection>
) : null;
}}
</ThemeContext.Consumer>

<SettingsMenuSection
sectionKey={SettingsMenu.SHORTCUT_SECTION_KEY}
isExpanded={this.isSectionExpanded(
Expand Down
Loading
Loading