Skip to content

Commit 9289767

Browse files
committed
Merge branch 'develop' into task/new-design-dashboard
# Conflicts: # src/chart/parameter/component/NodePropertyParameterSelect.tsx # yarn.lock
2 parents 5d1db15 + 6608417 commit 9289767

20 files changed

+384
-74
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,6 @@ node_modules
3737
jspm_packages
3838

3939
# Serverless directories
40-
.serverless
40+
.serverless
41+
# Sentry Auth Token
42+
.env.sentry-build-plugin

docs/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
** xref:developer-guide/standalone-mode.adoc[Standalone Mode]
3939
** xref:developer-guide/component-overview.adoc[Component Overview]
4040
** xref:developer-guide/design.adoc[Design]
41+
** xref:developer-guide/style-configuration.adoc[Style Configuration]
4142
** xref:developer-guide/adding-visualizations.adoc[Adding Visualizations]
4243
** xref:developer-guide/state-management.adoc[State Management]
4344
** xref:developer-guide/session-storage.adoc[Session Storage]

docs/modules/ROOT/pages/developer-guide/index.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ This guide contains information for developers looking to deploy NeoDash, or ext
66
- link:configuration[Configuration]
77
- link:standalone-mode[Standalone Mode]
88
- link:component-overview[Component Overview]
9-
- link:design[Design]
9+
- link:design[Design]
10+
- link:style-configuration[Style Configuration]
1011
- link:adding-visualizations[Adding Visualizations]
1112
- link:state-management[State Management]
1213
- link:testing[Testing]
File renamed without changes.

docs/modules/ROOT/pages/user-guide/reports/parameter-select.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ global parameter completely when the field is cleared. This may break
5555
some visualizations. If disabled, sets the parameter value to “” (empty
5656
string) when the input field is cleared.
5757

58+
|Multiple Selection |on/off |off |If enabled, allows user to select multiple choices. Parameter will be then an array of selections.
59+
60+
|Manual Parameter Save |on/off |off |If enabled, adds a confirmation button in order to propagate the selection into the dashboard parameter.
61+
5862
|Enable Manual Label/Property Name Specification |on/off |off |If
5963
enabled, does not enforce you to select a node label/property using an
6064
auto-complete field, instead, you can enter any value. This is useful

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"@nivo/sunburst": "^0.83.0",
6060
"@nivo/treemap": "^0.83.0",
6161
"@sentry/react": "^7.57.0",
62+
"@sentry/webpack-plugin": "^2.5.0",
6263
"babel-runtime": "^6.26.0",
6364
"chroma-js": "^2.4.2",
6465
"classnames": "^2.3.1",

src/chart/parameter/ParameterSelectionChart.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
2828
const setParameterDisplayValue = (value) => setGlobalParameter(parameterDisplayName, value);
2929
const allParameters = props.parameters;
3030
const multiSelector = props?.settings?.multiSelector;
31-
31+
const manualParameterSave = props?.settings?.manualParameterSave;
3232
// in NeoDash 2.2.1 or earlier, there was no means to have a different display value in the selector. This condition handles that.
3333
const compatibilityMode = !query?.toLowerCase().includes('as display') || false;
3434

@@ -50,6 +50,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
5050
settings={props.settings}
5151
allParameters={allParameters}
5252
compatibilityMode={compatibilityMode}
53+
manualParameterSave={manualParameterSave}
5354
/>
5455
);
5556
} else if (type == 'Node Property') {
@@ -67,6 +68,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
6768
allParameters={allParameters}
6869
compatibilityMode={compatibilityMode}
6970
multiSelector={multiSelector}
71+
manualParameterSave={manualParameterSave}
7072
/>
7173
);
7274
} else if (type == 'Relationship Property') {
@@ -84,6 +86,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
8486
allParameters={allParameters}
8587
compatibilityMode={compatibilityMode}
8688
multiSelector={multiSelector}
89+
manualParameterSave={manualParameterSave}
8790
/>
8891
);
8992
} else if (type == 'Date Picker') {
@@ -100,6 +103,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
100103
settings={props.settings}
101104
allParameters={allParameters}
102105
compatibilityMode={compatibilityMode}
106+
manualParameterSave={manualParameterSave}
103107
/>
104108
);
105109
} else if (type == 'Custom Query') {
@@ -117,6 +121,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
117121
allParameters={allParameters}
118122
compatibilityMode={compatibilityMode}
119123
multiSelector={multiSelector}
124+
manualParameterSave={manualParameterSave}
120125
/>
121126
);
122127
}

src/chart/parameter/component/FreeTextParameterSelect.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { debounce, CircularProgress } from '@mui/material';
22
import React, { useCallback, useEffect } from 'react';
33
import { ParameterSelectProps } from './ParameterSelect';
44
import NeoField from '../../../component/field/Field';
5+
import { SelectionConfirmationButton } from './SelectionConfirmationButton';
56

67
const FreeTextParameterSelectComponent = (props: ParameterSelectProps) => {
8+
const { manualParameterSave } = props;
79
const setParameterTimeout =
810
props.settings && props.settings.setParameterTimeout ? props.settings.setParameterTimeout : 1000;
911
const defaultValue =
@@ -17,38 +19,66 @@ const FreeTextParameterSelectComponent = (props: ParameterSelectProps) => {
1719
const clearParameterOnFieldClear =
1820
props.settings && props.settings.clearParameterOnFieldClear ? props.settings.clearParameterOnFieldClear : false;
1921
const [running, setRunning] = React.useState(false);
22+
const [paramValueLocal, setParamValueLocal] = React.useState(null);
23+
2024
const setParameterValue = (value) => {
2125
setRunning(false);
2226
props.setParameterValue(value);
2327
};
2428
const debouncedSetParameterValue = useCallback(debounce(setParameterValue, setParameterTimeout), []);
2529

30+
const manualHandleParametersUpdate = () => {
31+
handleParametersUpdate(paramValueLocal, false);
32+
};
33+
34+
const handleParametersUpdate = (value, manual = false) => {
35+
setParamValueLocal(value);
36+
37+
if (manual) {
38+
// setRunning(false);
39+
return;
40+
}
41+
42+
if (value == null && clearParameterOnFieldClear) {
43+
debouncedSetParameterValue(defaultValue);
44+
} else {
45+
debouncedSetParameterValue(value);
46+
}
47+
};
48+
2649
// If the user hasn't typed, and the parameter value mismatches the input value --> it was changed externally --> refresh the input value.
2750
if (running == false && inputText !== props.parameterValue) {
2851
setInputText(props.parameterValue);
2952
}
3053

3154
return (
32-
<div style={{ width: '100%', marginTop: '5px' }}>
55+
<div className={'n-flex n-flex-row n-flex-wrap n-items-center'} style={{ width: '100%', marginTop: '5px' }}>
3356
<NeoField
3457
key={'freetext'}
3558
label={helperText ? helperText : `${label} ${property}`}
3659
defaultValue={defaultValue}
3760
value={inputText}
3861
variant='outlined'
3962
placeholder={'Enter text here...'}
40-
style={{ marginBottom: '10px', marginRight: '10px', marginLeft: '15px', width: 'calc(100% - 80px)' }}
63+
style={{
64+
marginBottom: '20px',
65+
marginRight: '10px',
66+
marginLeft: '15px',
67+
minWidth: `calc(100% - ${manualParameterSave ? '80' : '30'}px)`,
68+
maxWidth: 'calc(100% - 30px)',
69+
}}
4170
onChange={(newValue) => {
4271
setRunning(true);
4372
setInputText(newValue);
4473

45-
if (newValue == null && clearParameterOnFieldClear) {
46-
debouncedSetParameterValue(defaultValue);
47-
} else {
48-
debouncedSetParameterValue(newValue);
49-
}
74+
handleParametersUpdate(newValue, manualParameterSave);
5075
}}
5176
/>
77+
{manualParameterSave ? (
78+
<SelectionConfirmationButton onClick={() => manualHandleParametersUpdate()} key={`selectionConfirmation`} />
79+
) : (
80+
<></>
81+
)}
5282
{running ? <CircularProgress size={26} style={{ marginTop: '20px', marginLeft: '5px' }} /> : <></>}
5383
</div>
5484
);

src/chart/parameter/component/NodePropertyParameterSelect.tsx

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { debounce, TextField } from '@mui/material';
33
import Autocomplete from '@mui/material/Autocomplete';
44
import { ParameterSelectProps } from './ParameterSelect';
55
import { RenderSubValue } from '../../../report/ReportRecordProcessing';
6+
import { SelectionConfirmationButton } from './SelectionConfirmationButton';
67

78
const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
89
const suggestionsUpdateTimeout =
@@ -20,14 +21,17 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
2021
}
2122
return multi ? [] : value;
2223
};
23-
const { multiSelector } = props;
24+
const { multiSelector, manualParameterSave } = props;
2425
const allParameters = props.allParameters ? props.allParameters : {};
2526
const [extraRecords, setExtraRecords] = React.useState([]);
2627
const [inputDisplayText, setInputDisplayText] = React.useState(
2728
props.parameterDisplayValue && multiSelector ? '' : props.parameterDisplayValue
2829
);
2930
const [inputValue, setInputValue] = React.useState(getInitialValue(props.parameterDisplayValue, multiSelector));
3031

32+
const [paramValueLocal, setParamValueLocal] = React.useState(props.parameterValue);
33+
const [paramValueDisplayLocal, setParamValueDisplayLocal] = React.useState(props.parameterDisplayValue);
34+
3135
const debouncedQueryCallback = useCallback(debounce(props.queryCallback, suggestionsUpdateTimeout), []);
3236
const label = props.settings && props.settings.entityType ? props.settings.entityType : '';
3337
const propertyType = props.settings && props.settings.propertyType ? props.settings.propertyType : '';
@@ -42,92 +46,119 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
4246

4347
const realValueRowIndex = props.compatibilityMode ? 0 : 1 - displayValueRowIndex;
4448

49+
const manualHandleParametersUpdate = () => {
50+
handleParametersUpdate(paramValueLocal, paramValueDisplayLocal, false);
51+
};
52+
const handleParametersUpdate = (value, displayValue, manual = false) => {
53+
setParamValueLocal(value);
54+
setParamValueDisplayLocal(displayValue);
55+
56+
if (manual) {
57+
return;
58+
}
59+
60+
props.setParameterValue(value);
61+
props.setParameterDisplayValue(displayValue);
62+
};
4563
const handleCrossClick = (isMulti, value) => {
4664
if (isMulti) {
4765
if (value.length == 0 && clearParameterOnFieldClear) {
4866
setInputValue([]);
49-
props.setParameterValue(undefined);
50-
props.setParameterDisplayValue(undefined);
51-
return;
67+
handleParametersUpdate(undefined, undefined, manualParameterSave);
68+
return true;
5269
}
5370
if (value.length == 0) {
5471
setInputValue([]);
55-
props.setParameterValue([]);
56-
props.setParameterDisplayValue([]);
72+
handleParametersUpdate([], [], manualParameterSave);
73+
return true;
5774
}
5875
} else {
5976
if (value && clearParameterOnFieldClear) {
6077
setInputValue(null);
61-
props.setParameterValue(undefined);
62-
props.setParameterDisplayValue(undefined);
63-
return;
78+
handleParametersUpdate(undefined, undefined, manualParameterSave);
79+
return true;
6480
}
6581
if (value == null) {
6682
setInputValue(null);
67-
props.setParameterValue(defaultValue);
68-
props.setParameterDisplayValue(defaultValue);
83+
handleParametersUpdate(defaultValue, defaultValue, manualParameterSave);
84+
return true;
6985
}
86+
return false;
7087
}
7188
};
7289
const propagateSelection = (event, newDisplay) => {
7390
const isMulti = Array.isArray(newDisplay);
74-
handleCrossClick(isMulti, newDisplay);
91+
if (handleCrossClick(isMulti, newDisplay)) {
92+
return;
93+
}
7594
let newValue;
95+
let valReference = manualParameterSave ? paramValueLocal : props.parameterValue;
96+
let valDisplayReference = manualParameterSave ? paramValueDisplayLocal : props.parameterDisplayValue;
7697
// Multiple and new entry
7798
if (isMulti && inputValue.length < newDisplay.length) {
78-
newValue = Array.isArray(props.parameterValue) ? [...props.parameterValue] : [props.parameterValue];
99+
newValue = Array.isArray(valReference) ? [...valReference] : [valReference];
79100
const newDisplayValue = [...newDisplay].slice(-1)[0];
80101

81102
let val = extraRecords.filter((r) => r._fields[displayValueRowIndex].toString() == newDisplayValue)[0]._fields[
82103
realValueRowIndex
83104
];
84105

85-
newValue.push(val?.low ?? val);
106+
newValue.push(RenderSubValue(val));
86107
} else if (!isMulti) {
87-
newValue = extraRecords.filter((r) => r._fields[displayValueRowIndex].toString() == newDisplay)[0]._fields[
88-
realValueRowIndex
89-
];
108+
newValue = extraRecords.filter((r) => (r?._fields?.[displayValueRowIndex]?.toString() || null) == newDisplay)[0]
109+
._fields[realValueRowIndex];
90110

91-
newValue = newValue?.low || newValue;
111+
newValue = RenderSubValue(newValue);
92112
} else {
93-
let ele = props.parameterDisplayValue.filter((x) => !newDisplay.includes(x))[0];
94-
newValue = [...props.parameterValue];
95-
newValue.splice(props.parameterDisplayValue.indexOf(ele), 1);
113+
let ele = valDisplayReference.filter((x) => !newDisplay.includes(x))[0];
114+
newValue = [...valReference];
115+
newValue.splice(valDisplayReference.indexOf(ele), 1);
96116
}
97117

98118
setInputDisplayText(isMulti ? '' : newDisplay);
99119
setInputValue(newDisplay);
100120

101-
props.setParameterValue(newValue);
102-
props.setParameterDisplayValue(newDisplay);
121+
handleParametersUpdate(newValue, newDisplay, manualParameterSave);
103122
};
104123
return (
105-
<Autocomplete
106-
id='autocomplete'
107-
multiple={multiSelector}
108-
options={extraRecords.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)').sort()}
109-
style={{ maxWidth: 'calc(100% - 30px)', marginLeft: '15px', marginTop: '5px' }}
110-
inputValue={inputDisplayText}
111-
onInputChange={(event, value) => {
112-
setInputDisplayText(value);
113-
debouncedQueryCallback(props.query, { input: `${value}`, ...allParameters }, setExtraRecords);
114-
}}
115-
isOptionEqualToValue={(option, value) => {
116-
return (option && option.toString()) === (value && value.toString());
117-
}}
118-
value={inputValue}
119-
onChange={propagateSelection}
120-
renderInput={(params) => (
121-
<TextField
122-
{...params}
123-
InputLabelProps={{ shrink: true }}
124-
placeholder='Start typing...'
125-
label={helperText ? helperText : `${label} ${propertyType}`}
126-
variant='outlined'
127-
/>
124+
<div className={'n-flex n-flex-row n-flex-wrap n-items-center'}>
125+
<Autocomplete
126+
id='autocomplete'
127+
multiple={multiSelector}
128+
options={extraRecords.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)').sort()}
129+
style={{
130+
maxWidth: 'calc(100% - 30px)',
131+
minWidth: `calc(100% - ${manualParameterSave ? '80' : '30'}px)`,
132+
marginLeft: '15px',
133+
marginTop: '5px',
134+
}}
135+
inputValue={inputDisplayText || ''}
136+
onInputChange={(event, value) => {
137+
setInputDisplayText(value);
138+
debouncedQueryCallback(props.query, { input: `${value}`, ...allParameters }, setExtraRecords);
139+
}}
140+
isOptionEqualToValue={(option, value) => {
141+
return (option && option.toString()) === (value && value.toString());
142+
}}
143+
value={inputValue}
144+
onChange={propagateSelection}
145+
renderInput={(params) => (
146+
<TextField
147+
{...params}
148+
InputLabelProps={{ shrink: true }}
149+
placeholder='Start typing...'
150+
label={helperText ? helperText : `${label} ${propertyType}`}
151+
variant='outlined'
152+
/>
153+
)}
154+
getOptionLabel={(option) => RenderSubValue(option)}
155+
/>
156+
{manualParameterSave ? (
157+
<SelectionConfirmationButton onClick={() => manualHandleParametersUpdate()} key={`selectionConfirmation`} />
158+
) : (
159+
<></>
128160
)}
129-
getOptionLabel={(option) => RenderSubValue(option)}
130-
/>
161+
</div>
131162
);
132163
};
133164

src/chart/parameter/component/ParameterSelect.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,8 @@ export interface ParameterSelectProps {
5050
* Add the possibility for multiple selections
5151
*/
5252
multiSelector?: boolean;
53+
/**
54+
* Add the possibility for manual selection confirmation
55+
*/
56+
manualParameterSave?: boolean;
5357
}

0 commit comments

Comments
 (0)