Skip to content

Commit 68f6e41

Browse files
committed
Use page props to load react apps
This is like we do it in ListenBrainz - a json blob containing information about what view to load, and config items for each view. Pass in parameters to the component using props instead of reading from the rendered HTML.
1 parent f80e87a commit 68f6e41

File tree

13 files changed

+174
-111
lines changed

13 files changed

+174
-111
lines changed

webpack.config.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ module.exports = function (env) {
3333
context: path.resolve(__dirname, 'webserver', 'static'),
3434
entry: {
3535
common: ['./scripts/common.ts'],
36-
datasets: ['./scripts/datasets.ts'],
36+
datasets: ['./scripts/datasets.tsx'],
3737
similarity: ['./scripts/similarity.ts'],
38-
global: ['./scripts/global.ts'],
3938
homepage: ['./scripts/homepage.ts'],
4039
profile: ['./scripts/profile.ts'],
4140
stats: ['./scripts/stats.ts'],
@@ -48,6 +47,9 @@ module.exports = function (env) {
4847
publicPath: '/static/build/'
4948
},
5049
mode: production ? 'production' : 'development',
50+
resolve: {
51+
extensions: ['.js', '.jsx', '.ts', '.tsx']
52+
},
5153
module: {
5254
rules: [
5355
{

webserver/static/scripts/common.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// @ts-ignore
2-
import global from "./global.ts";
1+
import jquery from "jquery";
32

43
// @ts-ignore
5-
global.$ = require("jquery");
4+
(global ?? window).$ = jquery;
5+
// @ts-ignore
6+
(global ?? window).jQuery = jquery;
67

78
require("bootstrap");

webserver/static/scripts/datasets.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import ReactDOM from "react-dom";
2+
3+
import React from "react";
4+
import DatasetEditor from "./datasets/editor";
5+
import EvaluationJobsViewer from "./datasets/eval-jobs-viewer";
6+
import Dataset from "./datasets/class-viewer";
7+
8+
interface DatasetPageProps {
9+
dataset_mode: string;
10+
data: Record<string, any>;
11+
}
12+
13+
const container = document.getElementById("dataset-react-container");
14+
15+
const propsElement = document.getElementById("page-react-props");
16+
let reactProps: DatasetPageProps | undefined;
17+
if (propsElement?.innerHTML) {
18+
reactProps = JSON.parse(propsElement!.innerHTML);
19+
}
20+
21+
if (container && reactProps) {
22+
if (reactProps.dataset_mode === "create") {
23+
console.debug("Loading dataset editor in create mode");
24+
ReactDOM.render(<DatasetEditor mode="create" />, container);
25+
} else if (reactProps.dataset_mode === "edit") {
26+
ReactDOM.render(
27+
<DatasetEditor mode="edit" datasetId={reactProps.data.datasetId} />,
28+
container
29+
);
30+
} else if (reactProps.dataset_mode === "view") {
31+
ReactDOM.render(
32+
<Dataset datasetId={reactProps.data.datasetId} />,
33+
container
34+
);
35+
} else if (reactProps.dataset_mode === "eval-info") {
36+
ReactDOM.render(
37+
<EvaluationJobsViewer datasetId={reactProps.data.datasetId} />,
38+
container
39+
);
40+
}
41+
}

webserver/static/scripts/datasets/class-viewer.tsx

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
/*
22
This is a viewer for classes in existing datasets.
3-
4-
Attribute "data-dataset-id" which references existing dataset by its ID need
5-
to be specified on container element. When Dataset component is mounted, it
6-
fetches existing dataset from the server.
3+
The Dataset class is the main component which is used to show the dataset
74
*/
85
import React, { Component } from "react";
9-
import ReactDOM from "react-dom";
10-
11-
const CONTAINER_ELEMENT_ID = "dataset-class-viewer";
12-
const container = document.getElementById(CONTAINER_ELEMENT_ID)!;
136

147
const SECTION_DATASET_DETAILS = "dataset_details";
158
const SECTION_CLASS_DETAILS = "class_details";
@@ -84,6 +77,7 @@ const RECORDING_STATUS_ERROR = "error"; // failed to load info about recording
8477
const RECORDING_STATUS_LOADED = "loaded"; // info has been loaded
8578

8679
interface RecordingProps {
80+
datasetId: string;
8781
mbid: string;
8882
}
8983

@@ -106,7 +100,7 @@ class Recording extends Component<RecordingProps, RecordingState> {
106100
componentDidMount() {
107101
$.ajax({
108102
type: "GET",
109-
url: `/datasets/metadata/dataset/${container.dataset.datasetId}/${this.props.mbid}`,
103+
url: `/datasets/metadata/dataset/${this.props.datasetId}/${this.props.mbid}`,
110104
success: (data) => {
111105
this.setState({
112106
details: data.recording,
@@ -158,12 +152,17 @@ class Recording extends Component<RecordingProps, RecordingState> {
158152
}
159153

160154
interface RecordingListProps {
155+
datasetId: string;
161156
recordings: string[];
162157
}
163158

164159
function RecordingList(props: RecordingListProps) {
165160
const items = props.recordings.map((recording) => (
166-
<Recording key={recording} mbid={recording} />
161+
<Recording
162+
datasetId={props.datasetId}
163+
key={recording}
164+
mbid={recording}
165+
/>
167166
));
168167
if (items.length > 0) {
169168
return (
@@ -182,6 +181,7 @@ function RecordingList(props: RecordingListProps) {
182181
}
183182

184183
interface ClassDetailsProps {
184+
datasetId: string;
185185
id: number;
186186
name: string;
187187
description: string;
@@ -210,7 +210,10 @@ function ClassDetails(props: ClassDetailsProps) {
210210
</a>
211211
</p>
212212
<p>{props.description}</p>
213-
<RecordingList recordings={props.recordings} />
213+
<RecordingList
214+
datasetId={props.datasetId}
215+
recordings={props.recordings}
216+
/>
214217
</div>
215218
);
216219
}
@@ -236,14 +239,18 @@ function ClassDetails(props: ClassDetailsProps) {
236239
active_class_index variable to be set in Dataset state)
237240
*/
238241

242+
interface DatasetProps {
243+
datasetId: string;
244+
}
245+
239246
interface DatasetState {
240247
active_section: any;
241248
active_class_index?: number;
242249
data: any;
243250
}
244251

245-
class Dataset extends Component<{}, DatasetState> {
246-
constructor(props: Readonly<{}>) {
252+
class Dataset extends Component<DatasetProps, DatasetState> {
253+
constructor(props: Readonly<DatasetProps>) {
247254
super(props);
248255
this.state = {
249256
active_section: SECTION_DATASET_DETAILS,
@@ -253,22 +260,9 @@ class Dataset extends Component<{}, DatasetState> {
253260
}
254261

255262
componentDidMount() {
256-
// Do not confuse property called "dataset" with our own datasets. See
257-
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
258-
// for more info about it.
259-
if (!container.dataset.datasetId) {
260-
console.error(
261-
"ID of existing dataset needs to be specified" +
262-
"in data-dataset-id property."
263-
);
264-
return;
265-
}
266-
$.get(
267-
`/datasets/service/${container.dataset.datasetId}/json`,
268-
(result) => {
269-
this.setState({ data: result });
270-
}
271-
);
263+
$.get(`/datasets/service/${this.props.datasetId}/json`, (result) => {
264+
this.setState({ data: result });
265+
});
272266
}
273267

274268
handleViewDetails = (index: number) => {
@@ -296,11 +290,12 @@ class Dataset extends Component<{}, DatasetState> {
296290
/>
297291
);
298292
} // SECTION_CLASS_DETAILS
299-
if (this.state.active_class_index) {
293+
if (this.state.active_class_index !== undefined) {
300294
const active_class =
301295
this.state.data.classes[this.state.active_class_index];
302296
return (
303297
<ClassDetails
298+
datasetId={this.props.datasetId}
304299
id={this.state.active_class_index}
305300
name={active_class.name}
306301
description={active_class.description}
@@ -315,4 +310,4 @@ class Dataset extends Component<{}, DatasetState> {
315310
}
316311
}
317312

318-
if (container) ReactDOM.render(<Dataset />, container);
313+
export default Dataset;

webserver/static/scripts/datasets/editor.tsx

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,17 @@
33
- create (creates new dataset from scratch)
44
- edit (edits existing dataset)
55
6-
Mode is set by defining "data-mode" attribute on the container element which is
7-
referenced in CONTAINER_ELEMENT_ID. Value of this attribute is either "create"
8-
or "edit" (see definitions below: MODE_CREATE and MODE_EDIT).
6+
The DatasetEditor class is the main entrypoint. It has 2 props:
7+
- mode
8+
- datasetId
99
10-
When mode is set to "edit", attribute "data-edit-id" need to be specified. This
11-
attribute references existing dataset by its ID. When Dataset component is
12-
mounted, it pull existing dataset for editing from the server.
10+
When mode is set to "edit", the prop "datasetId" needs to be specified. This
11+
attribute references existing dataset by its ID. When the DataseEditor component is
12+
mounted, it pulls existing dataset for editing from the server.
1313
*/
1414
import React, { ChangeEvent, Component, FormEvent } from "react";
1515
import ReactDOM from "react-dom";
1616

17-
const CONTAINER_ELEMENT_ID = "dataset-editor";
18-
const container = document.getElementById(CONTAINER_ELEMENT_ID)!;
19-
2017
const MODE_CREATE = "create";
2118
const MODE_EDIT = "edit";
2219

@@ -63,6 +60,7 @@ function DatasetDetails(props: DatasetDetailsProps) {
6360

6461
interface DatasetControlButtonsProps {
6562
mode: string;
63+
datasetId?: string;
6664
data: any;
6765
}
6866

@@ -94,7 +92,7 @@ class DatasetControlButtons extends Component<
9492
submitEndpoint = "/datasets/service/create";
9593
} else {
9694
// MODE_EDIT
97-
submitEndpoint = `/datasets/service/${container.dataset.editId}/edit`;
95+
submitEndpoint = `/datasets/service/${this.props.datasetId}/edit`;
9896
}
9997
$.ajax({
10098
type: "POST",
@@ -676,20 +674,23 @@ class ClassDetails extends Component<ClassDetailsProps> {
676674
- SECTION_CLASS_DETAILS (editing specific class; this also requires
677675
active_class_index variable to be set in Dataset state)
678676
*/
679-
interface DatasetState {
677+
interface DatasetEditorState {
680678
autoAddRecording: boolean;
681-
mode: string;
682679
active_section: string;
683680
active_class_index?: number;
684681
data?: any;
685682
}
686683

687-
class Dataset extends Component<{}, DatasetState> {
688-
constructor(props: Readonly<{}>) {
684+
interface DatasetEditorProps {
685+
mode: string;
686+
datasetId?: string;
687+
}
688+
689+
class DatasetEditor extends Component<DatasetEditorProps, DatasetEditorState> {
690+
constructor(props: Readonly<DatasetEditorProps>) {
689691
super(props);
690692
this.state = {
691693
autoAddRecording: false,
692-
mode: container.dataset.mode!,
693694
active_section: SECTION_DATASET_DETAILS,
694695
data: undefined,
695696
};
@@ -699,31 +700,20 @@ class Dataset extends Component<{}, DatasetState> {
699700
// This function is invoked when Dataset component is originally
700701
// mounted. Here we need to check what mode dataset editor is in, and
701702
// pull data from the server if mode is MODE_EDIT.
702-
// Do not confuse property called "dataset" with our own datasets. See
703-
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
704-
// for more info about it.
705-
if (this.state.mode === MODE_EDIT) {
706-
if (!container.dataset.editId) {
707-
console.error(
708-
"ID of existing dataset needs to be specified" +
709-
"in data-edit-id property."
710-
);
711-
return;
712-
}
703+
if (this.props.mode === MODE_EDIT) {
713704
$.get(
714-
`/datasets/service/${container.dataset.editId}/json`,
705+
`/datasets/service/${this.props.datasetId}/json`,
715706
(result) => {
716707
this.setState({ data: result });
717708
}
718709
);
719710
} else {
720-
if (this.state.mode !== MODE_CREATE) {
711+
if (this.props.mode !== MODE_CREATE) {
721712
console.warn(
722713
"Unknown dataset editor mode! Using default: MODE_CREATE."
723714
);
724715
}
725716
this.setState({
726-
mode: MODE_CREATE,
727717
data: {
728718
name: "",
729719
description: "",
@@ -829,13 +819,14 @@ class Dataset extends Component<{}, DatasetState> {
829819
</label>
830820
</p>
831821
<DatasetControlButtons
832-
mode={this.state.mode}
822+
mode={this.props.mode}
823+
datasetId={this.props.datasetId}
833824
data={this.state.data}
834825
/>
835826
</div>
836827
);
837828
} // SECTION_CLASS_DETAILS
838-
if (this.state.active_class_index) {
829+
if (this.state.active_class_index !== undefined) {
839830
const active_class =
840831
this.state.data.classes[this.state.active_class_index];
841832
return (
@@ -859,4 +850,4 @@ class Dataset extends Component<{}, DatasetState> {
859850
}
860851
}
861852

862-
if (container) ReactDOM.render(<Dataset />, container);
853+
export default DatasetEditor;

0 commit comments

Comments
 (0)