-
Notifications
You must be signed in to change notification settings - Fork 113
/
initial-state.js
271 lines (247 loc) · 8.25 KB
/
initial-state.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
import deepmerge from 'deepmerge';
import { loadLocalStorage } from './helpers';
import normalizeData from './normalize-data';
import { getFlagsFromUrl, Flags } from '../utils/flags';
import { mapNodeType, isValidBoolean } from '../utils';
import {
settings,
sidebarWidth,
localStorageName,
localStorageRunsMetadata,
params,
BANNER_KEYS,
} from '../config';
/**
* Create new default state instance for properties that aren't overridden
* when the pipeline is reset with new data via the App component's data prop
* @return {Object} state
*/
export const createInitialState = () => ({
chartSize: {},
flags: Flags.defaults(),
textLabels: true,
theme: 'dark',
expandAllPipelines: false,
isPrettyName: settings.isPrettyName.default,
showFeatureHints: settings.showFeatureHints.default,
showDatasetPreviews: settings.showDatasetPreviews.default,
ignoreLargeWarning: false,
showBanner: {
[BANNER_KEYS.LITE]: false,
},
loading: {
graph: false,
pipeline: false,
node: false,
},
visible: {
code: false,
exportModal: false,
graph: true,
metadataModal: false,
miniMap: true,
modularPipelineFocusMode: null,
settingsModal: false,
shareableUrlModal: false,
sidebar: window.innerWidth > sidebarWidth.breakpoint,
slicing: true,
},
display: {
globalNavigation: true,
sidebar: true,
miniMap: true,
expandPipelinesBtn: true,
exportBtn: true,
labelBtn: true,
layerBtn: true,
zoomToolbar: true,
metadataPanel: true,
},
behaviour: {
reFocus: true,
},
zoom: {},
runsMetadata: {},
});
export const parseUrlParameters = () => {
const search = new URLSearchParams(window.location.search);
return {
pipelineIdFromURL: search.get(params.pipeline),
nodeIdFromUrl: search.get(params.selected),
nodeNameFromUrl: search.get(params.selectedName),
nodeTypeInUrl: search.get(params.types)
? search.get(params.types).split(',')
: [],
nodeTagInUrl: search.get(params.tags)
? search.get(params.tags).split(',')
: [],
expandAllPipelinesInUrl: search.get(params.expandAll),
};
};
/**
* Applies URL parameters to the application pipeline state.
* This function modifies the state based on the URL parameters such as
* pipeline ID, node ID, node name, node type presence, and tag presence.
*
* @param {Object} state The current application pipeline state.
* @param {Object} urlParams An object containing parsed URL parameters.
* @returns {Object} The new state with modifications applied based on the URL parameters.
*/
const applyUrlParametersToPipelineState = (state, urlParams) => {
const {
pipelineIdFromURL,
nodeIdFromUrl,
nodeNameFromUrl,
nodeTypeInUrl,
nodeTagInUrl,
} = urlParams;
let newState = { ...state };
const nodeTypes = ['parameters', 'task', 'data'];
// Use main pipeline if pipeline from URL isn't recognised
if (pipelineIdFromURL) {
newState.pipeline.active = newState.pipeline.ids.includes(pipelineIdFromURL)
? pipelineIdFromURL
: newState.pipeline.main;
}
// Ensure data tags are on to allow redirection back to the selected node
if (nodeNameFromUrl) {
newState.nodeType.disabled.data = false;
}
if (nodeTypeInUrl.length) {
Object.keys(newState.nodeType.disabled).forEach((key) => {
newState.nodeType.disabled[key] = true;
});
nodeTypeInUrl.forEach((key) => {
newState.nodeType.disabled[mapNodeType(key)] = false;
});
}
// Enable node types based on presence in URL and current node type settings
if (nodeIdFromUrl && nodeTypes.includes(state.node.type[nodeIdFromUrl])) {
newState.nodeType.disabled[newState.node.type[nodeIdFromUrl]] = false;
}
if (nodeTagInUrl.length) {
// Set all tags to false initially
Object.keys(newState.tag.enabled).forEach((key) => {
newState.tag.enabled[key] = false;
});
nodeTagInUrl.forEach((tag) => {
newState.tag.enabled[tag] = true;
});
}
return newState;
};
/**
* Applies URL parameters to the application non-pipeline state.
* This function modifies the state based on the URL parameters such as
* expandAllPipelines presence.
*
* @param {Object} state The current application non-pipeline state.
* @param {Object} urlParams An object containing parsed URL parameters.
* @returns {Object} The new state with modifications applied based on the URL parameters.
*/
const applyUrlParametersToNonPipelineState = (state, urlParams) => {
const { expandAllPipelinesInUrl } = urlParams;
let newState = { ...state };
if (expandAllPipelinesInUrl && isValidBoolean(expandAllPipelinesInUrl)) {
newState.expandAllPipelines = JSON.parse(expandAllPipelinesInUrl);
}
return newState;
};
/**
* Load values from localStorage and combine with existing state,
* but filter out any unused values from localStorage
* @param {Object} state Initial/extant state
* @return {Object} Combined state from localStorage
*/
export const mergeLocalStorage = (state) => {
const localStorageState = loadLocalStorage(localStorageName);
const localStorageRunsMetadataState = loadLocalStorage(
localStorageRunsMetadata
);
Object.keys(localStorageState).forEach((key) => {
if (!(key in state)) {
delete localStorageState[key];
}
});
const allLocalStorageState = {
...localStorageState,
...{ runsMetadata: localStorageRunsMetadataState },
};
return deepmerge(state, allLocalStorageState);
};
/**
* Prepare the pipeline data part of the state by normalizing the raw data,
* and applying saved state from localStorage.
* This part is separated so that it can be reset without overriding user settings,
* because it can be run both on initial state load and again later on.
* The applyFixes part should only ever be run once, on first load.
* Exactly when it runs depends on whether the data is loaded asynchronously or not.
* @param {Object} data Data prop passed to App component
* @param {Boolean} applyFixes Whether to override initialState
* @param {Boolean} expandAllPipelines Whether to expand all the modular pipelines
* @param {Object} urlParams An object containing parsed URL parameters.
* @returns {Object} The new pipeline state with modifications applied.
*/
export const preparePipelineState = (
data,
applyFixes,
expandAllPipelines,
urlParams
) => {
let state = mergeLocalStorage(normalizeData(data, expandAllPipelines));
if (applyFixes) {
// Use main pipeline if active pipeline from localStorage isn't recognised
if (!state.pipeline.ids.includes(state.pipeline.active)) {
state.pipeline.active = state.pipeline.main;
}
}
if (urlParams) {
state = applyUrlParametersToPipelineState(state, urlParams);
}
return state;
};
/**
* Prepare the non-pipeline data part of the state. This part is separated so that it
* will persist if the pipeline data is reset.
* Merge local storage and add custom state overrides from props etc
* @param {object} props Props passed to App component
* @param {Object} urlParams An object containing parsed URL parameters.
* @returns {Object} The new non-pipeline state with modifications applied.
*/
export const prepareNonPipelineState = (urlParams) => {
let state = mergeLocalStorage(createInitialState());
if (urlParams) {
state = applyUrlParametersToNonPipelineState(state, urlParams);
}
return {
...state,
flags: { ...state.flags, ...getFlagsFromUrl() },
};
};
/**
* Configure the redux store's initial state, by merging default values
* with normalised pipeline data and localStorage.
* If parameters flag is set to true, then disable parameters on initial load
* @param {Object} props App component props
* @return {Object} Initial state
*/
const getInitialState = (props = {}) => {
const urlParams = parseUrlParameters();
const nonPipelineState = prepareNonPipelineState(urlParams);
let expandAllPipelines = nonPipelineState.expandAllPipelines;
if (props.options) {
expandAllPipelines =
props.options.expandAllPipelines || nonPipelineState.expandAllPipelines;
}
const pipelineState = preparePipelineState(
props.data,
props.data !== 'json',
expandAllPipelines
);
const initialState = {
...nonPipelineState,
...pipelineState,
};
return props.options ? deepmerge(initialState, props.options) : initialState;
};
export default getInitialState;