Skip to content

Commit

Permalink
feat: Theme Selector (#1661)
Browse files Browse the repository at this point in the history
- Added a theme selector to settings menu when there are multiple themes
available
- IrisGrid and Monaco now respond dynamically to selected theme change
- Misc moving of theme utils
- Added vite config for local plugin dev

resolves #1660
  • Loading branch information
bmingles authored Dec 6, 2023
1 parent 4c0200e commit 5e2be64
Show file tree
Hide file tree
Showing 37 changed files with 459 additions and 233 deletions.
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

0 comments on commit 5e2be64

Please sign in to comment.