Skip to content

Commit 65faf8d

Browse files
merge google repo
2 parents 4bb13fd + 5861b01 commit 65faf8d

28 files changed

+404
-284
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
"react-arborist": "^3.1.0",
7979
"react-spinners": "^0.13.8",
8080
"react-table": "^7.8.0",
81-
"react-tagsinput": "^3.20.3",
8281
"react-toastify": "^9.1.3",
8382
"yup": "^1.2.0"
8483
},
@@ -117,6 +116,7 @@
117116
"prettier": "^2.8.7",
118117
"react": "^18.2.0",
119118
"react-dom": "^18.2.0",
119+
"react-tagsinput": "^3.20.3",
120120
"rimraf": "^4.4.1",
121121
"semantic-ui-react": "^2.1.4",
122122
"source-map-loader": "^1.0.2",

src/batches/batches.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { ReactWidget } from '@jupyterlab/apputils';
1918
import React, { useState, useEffect, useRef } from 'react';
2019
import {
2120
authApi,
@@ -44,6 +43,7 @@ import 'react-toastify/dist/ReactToastify.css';
4443
import { deleteBatchAPI } from '../utils/batchService';
4544
import CreateBatch from './createBatch';
4645
import PollingTimer from '../utils/pollingTimer';
46+
import { DataprocWidget } from '../controls/DataprocWidget';
4747

4848
const iconDelete = new LabIcon({
4949
name: 'launcher:delete-icon',
@@ -335,12 +335,8 @@ const BatchesComponent = (): React.JSX.Element => {
335335
);
336336
};
337337

338-
export class Batches extends ReactWidget {
339-
constructor() {
340-
super();
341-
}
342-
343-
render(): React.JSX.Element {
338+
export class Batches extends DataprocWidget {
339+
renderInternal(): React.JSX.Element {
344340
return <BatchesComponent />;
345341
}
346342
}

src/batches/createBatch.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ import {
4545
SERVICE_ACCOUNT,
4646
STATUS_RUNNING
4747
} from '../utils/const';
48-
import TagsInput from 'react-tagsinput';
49-
import 'react-tagsinput/react-tagsinput.css';
5048
import LabelProperties from '../jobs/labelProperties';
5149
import { authApi, toastifyCustomStyle } from '../utils/utils';
5250
import { ClipLoader } from 'react-spinners';
@@ -57,6 +55,7 @@ import { Select } from '../controls/MuiWrappedSelect';
5755
import { Input } from '../controls/MuiWrappedInput';
5856
import { Radio } from '@mui/material';
5957
import { Dropdown } from '../controls/MuiWrappedDropdown';
58+
import { TagsInput } from '../controls/MuiWrappedTagsInput';
6059

6160
type Project = {
6261
projectId: string;

src/cluster/cluster.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { ReactWidget } from '@jupyterlab/apputils';
1918
import { LabIcon } from '@jupyterlab/ui-components';
2019
import React, { useEffect, useRef, useState } from 'react';
2120
import { ClipLoader } from 'react-spinners';
@@ -45,6 +44,7 @@ import {
4544
} from '../utils/utils';
4645
import ClusterDetails from './clusterDetails';
4746
import ListCluster from './listCluster';
47+
import { DataprocWidget } from '../controls/DataprocWidget';
4848

4949
const iconStart = new LabIcon({
5050
name: 'launcher:start-icon',
@@ -445,12 +445,8 @@ const ClusterComponent = (): React.JSX.Element => {
445445
);
446446
};
447447

448-
export class Cluster extends ReactWidget {
449-
constructor() {
450-
super();
451-
}
452-
453-
render(): React.JSX.Element {
448+
export class Cluster extends DataprocWidget {
449+
renderInternal(): React.JSX.Element {
454450
return <ClusterComponent />;
455451
}
456452
}

src/controls/DataprocWidget.tsx

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import React from 'react';
2+
import { ReactWidget, IThemeManager } from '@jupyterlab/apputils';
3+
import { ThemeProvider, createTheme } from '@mui/material';
4+
5+
/**
6+
* Theme to use when the current Jupyter Theme is light.
7+
*/
8+
export const lightTheme = createTheme({
9+
palette: {
10+
mode: 'light'
11+
}
12+
});
13+
14+
/**
15+
* Theme to use when the current Jupyter Theme is dark.
16+
*/
17+
export const darkTheme = createTheme({
18+
palette: {
19+
mode: 'dark',
20+
primary: {
21+
main: '#FF0064'
22+
},
23+
secondary: {
24+
main: '#7000F2'
25+
}
26+
}
27+
});
28+
29+
/**
30+
* Base Widget class that injects MUI themeprovider context so
31+
* mui components will update when the jupyter theme changes.
32+
* TODO: Add auth context provider here?
33+
*/
34+
export abstract class DataprocWidget extends ReactWidget {
35+
/**
36+
* Whether or not currently jupyter is displaying a light theme.
37+
*/
38+
isLight: boolean = true;
39+
40+
/**
41+
* @param themeManager Need thememanager to listen to theme changes
42+
* and to retrieve current theme properties.
43+
*/
44+
constructor(protected themeManager: IThemeManager) {
45+
super();
46+
this.themeManager.themeChanged.connect(this.onThemeChanged, this);
47+
this.updateIsLight();
48+
}
49+
50+
/**
51+
* Updates the isLight property based on current theme properties.
52+
* @returns Whether or not isLight is updated based on the current theme.
53+
*/
54+
private updateIsLight() {
55+
const prevIsLight = this.isLight;
56+
const currentTheme = this.themeManager.theme;
57+
if (currentTheme) {
58+
this.isLight = this.themeManager.isLight(currentTheme);
59+
} else {
60+
this.isLight = true;
61+
}
62+
return this.isLight !== prevIsLight;
63+
}
64+
65+
private onThemeChanged() {
66+
if (this.updateIsLight()) {
67+
// Force a rerender if the theme has changed.
68+
this.update();
69+
}
70+
}
71+
72+
dispose() {
73+
this.themeManager.themeChanged.disconnect(this.onThemeChanged, this);
74+
return super.dispose();
75+
}
76+
77+
/**
78+
* Renders the theme context providers as well as the child components
79+
* as specified by renderInternal.
80+
*/
81+
protected render(): React.ReactElement {
82+
return (
83+
<ThemeProvider theme={this.isLight ? lightTheme : darkTheme}>
84+
{this.renderInternal()}
85+
</ThemeProvider>
86+
);
87+
}
88+
89+
/**
90+
* Child classes are expected to implement this instead of render().
91+
*/
92+
protected abstract renderInternal(): React.ReactElement;
93+
}

src/controls/MuiWrappedInput.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,5 @@ export const Input = styled(InputInternal)<InputProps>({
3838
marginTop: '10px',
3939
'& .MuiInputBase-input': {
4040
padding: '9.5px 14px'
41-
},
42-
'& .MuiOutlinedInput-notchedOutline': {
43-
borderColor: 'rgb(0,0,0)'
4441
}
4542
});

src/controls/MuiWrappedSelect.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,5 @@ export const Select = styled(SelectInternal)<SelectProps>({
4141
marginTop: '10px',
4242
'& .MuiInputBase-input': {
4343
padding: '9.5px 14px'
44-
},
45-
'& .MuiOutlinedInput-notchedOutline': {
46-
borderStyle: 'none'
4744
}
4845
});

src/controls/MuiWrappedTagsInput.tsx

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { styled } from '@mui/material';
18+
import { MuiChipsInput } from 'mui-chips-input';
19+
import React from 'react';
20+
import type { ReactTagsInputProps } from 'react-tagsinput';
21+
22+
function TagsInputInternal(props: ReactTagsInputProps) {
23+
const { className, value, onChange, addOnBlur, inputProps } = props;
24+
return (
25+
<MuiChipsInput
26+
className={className}
27+
onChange={tags => onChange(tags, [], [])}
28+
addOnBlur={!!addOnBlur}
29+
value={value}
30+
placeholder={inputProps?.placeholder}
31+
/>
32+
);
33+
}
34+
35+
export const TagsInput = styled(TagsInputInternal)<ReactTagsInputProps>({
36+
marginTop: '10px'
37+
});

src/dpms/databaseInfo.tsx

+8-10
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { ReactWidget } from '@jupyterlab/apputils';
1918
import React from 'react';
19+
import { IThemeManager } from '@jupyterlab/apputils';
20+
import { DataprocWidget } from '../controls/DataprocWidget';
2021

2122
interface IDatabaseProps {
2223
title: string;
@@ -63,21 +64,18 @@ const DatabaseInfo = ({
6364
);
6465
};
6566

66-
export class Database extends ReactWidget {
67-
dataprocMetastoreServices: string;
68-
databaseDetails: Record<string, string>;
67+
export class Database extends DataprocWidget {
6968
constructor(
7069
title: string,
71-
dataprocMetastoreServices: string,
72-
databaseDetails: Record<string, string>
70+
private dataprocMetastoreServices: string,
71+
private databaseDetails: Record<string, string>,
72+
themeManager: IThemeManager
7373
) {
74-
super();
74+
super(themeManager);
7575
this.title.label = title;
76-
this.dataprocMetastoreServices = dataprocMetastoreServices;
77-
this.databaseDetails = databaseDetails;
7876
}
7977

80-
render(): React.ReactElement {
78+
renderInternal(): React.ReactElement {
8179
return (
8280
<DatabaseInfo
8381
title={this.title.label}

src/dpms/dpmsWidget.tsx

+18-12
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { ReactWidget } from '@jupyterlab/apputils';
1918
import { JupyterLab } from '@jupyterlab/application';
2019
import React, { useEffect, useState } from 'react';
2120
import { Tree, NodeRendererProps } from 'react-arborist';
@@ -46,6 +45,8 @@ import { authApi, toastifyCustomStyle } from '../utils/utils';
4645
import { Table } from './tableInfo';
4746
import { ClipLoader } from 'react-spinners';
4847
import { toast } from 'react-toastify';
48+
import { DataprocWidget } from '../controls/DataprocWidget';
49+
import { IThemeManager } from '@jupyterlab/apputils';
4950

5051
const iconDatabase = new LabIcon({
5152
name: 'launcher:database-icon',
@@ -94,7 +95,13 @@ const calculateDepth = (node: any): number => {
9495
return depth;
9596
};
9697

97-
const DpmsComponent = ({ app }: { app: JupyterLab }): JSX.Element => {
98+
const DpmsComponent = ({
99+
app,
100+
themeManager
101+
}: {
102+
app: JupyterLab;
103+
themeManager: IThemeManager;
104+
}): JSX.Element => {
98105
const [searchTerm, setSearchTerm] = useState('');
99106
const [notebookValue, setNotebookValue] = useState<string>('');
100107
const [dataprocMetastoreServices, setDataprocMetastoreServices] =
@@ -265,7 +272,8 @@ fetching database name from fully qualified name structure */
265272
const content = new Database(
266273
node.data.name,
267274
dataprocMetastoreServices,
268-
databaseDetails
275+
databaseDetails,
276+
themeManager
269277
);
270278
const widget = new MainAreaWidget<Database>({ content });
271279
const widgetId = 'node-widget-db';
@@ -286,7 +294,8 @@ fetching database name from fully qualified name structure */
286294
dataprocMetastoreServices,
287295
database,
288296
column,
289-
tableDescription
297+
tableDescription,
298+
themeManager
290299
);
291300
const widget = new MainAreaWidget<Table>({ content });
292301
const widgetId = `node-widget-${uuidv4()}`;
@@ -707,15 +716,12 @@ fetching database name from fully qualified name structure */
707716
);
708717
};
709718

710-
export class dpmsWidget extends ReactWidget {
711-
app: JupyterLab;
712-
713-
constructor(app: JupyterLab) {
714-
super();
715-
this.app = app;
719+
export class dpmsWidget extends DataprocWidget {
720+
constructor(private app: JupyterLab, themeManager: IThemeManager) {
721+
super(themeManager);
716722
}
717723

718-
render(): JSX.Element {
719-
return <DpmsComponent app={this.app} />;
724+
renderInternal(): JSX.Element {
725+
return <DpmsComponent app={this.app} themeManager={this.themeManager} />;
720726
}
721727
}

0 commit comments

Comments
 (0)