Skip to content

Commit 427073c

Browse files
committed
IRSA-1008: New image search needs a single selection mode
1 parent 5656cbc commit 427073c

File tree

3 files changed

+86
-49
lines changed

3 files changed

+86
-49
lines changed

src/firefly/js/ui/ImageSelect.jsx

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {uniqBy, uniq, get, countBy, isNil, xor} from 'lodash';
88

99

1010
import {CheckboxGroupInputField} from './CheckboxGroupInputField.jsx';
11+
import {RadioGroupInputField} from './RadioGroupInputField.jsx';
1112
import {CollapsiblePanel} from '../ui/panel/CollapsiblePanel.jsx';
1213
import FieldGroupUtils, {getFieldVal} from '../fieldGroup/FieldGroupUtils.js';
1314
import {dispatchComponentStateChange} from '../core/ComponentCntlr.js';
@@ -25,7 +26,7 @@ export class ImageSelect extends PureComponent {
2526
}
2627

2728
render() {
28-
const {style, imageMasterData, groupKey} = this.props;
29+
const {style, imageMasterData, groupKey, multiSelect=true} = this.props;
2930
imageMasterData.forEach((d)=> {
3031
['missionId', 'project', 'subProject'].forEach((k) => d[k] = d[k] || '');
3132
});
@@ -46,12 +47,26 @@ export class ImageSelect extends PureComponent {
4647
return (
4748
<div style={style} className='ImageSelect'>
4849
<FilterPanel {...{imageMasterData}}/>
49-
<DataProductList {...{filteredImageData, groupKey, onChange: () => this.setState({lastMod:new Date().getTime()})}}/>
50+
<DataProductList {...{filteredImageData, groupKey, multiSelect, onChange: () => this.setState({lastMod:new Date().getTime()})}}/>
5051
</div>
5152
);
5253
}
53-
5454
}
55+
56+
ImageSelect.propTypes = {
57+
imageMasterData: PropTypes.arrayOf(PropTypes.object).isRequired,
58+
groupKey: PropTypes.string.isRequired,
59+
// this component needs to be wrapped by a FieldGroup. User of this component need to provide
60+
// a function so this component can handle field change event from reducing function.
61+
addChangeListener: PropTypes.func.isRequired,
62+
style: PropTypes.object,
63+
title: PropTypes.string,
64+
multiSelect: PropTypes.bool
65+
};
66+
67+
const toFilterSelectAry = (groupKey, s) => getFieldVal(groupKey, `Filter_${s}`, '').split(',').map((d) => d.trim()).filter((d) => d);
68+
69+
5570
function fieldsReducer(imageMasterData={}) {
5671
return (inFields, action) => {
5772
const {fieldKey='', options={}, value=''} = action.payload;
@@ -72,7 +87,7 @@ function fieldsReducer(imageMasterData={}) {
7287
} else if (fieldKey.startsWith('IMAGES_')) {
7388
// one item changed, update project selectAll checkbox
7489
const matcher = fieldKey.split('||')[0];
75-
const cbGroups = Object.values(inFields).filter((f) => f.fieldKey.startsWith(matcher)); // array of subproject in this project
90+
const cbGroups = Object.values(inFields).filter((f) => get(f, 'fieldKey', '').startsWith(matcher)); // array of subproject in this project
7691
const allSelected = cbGroups.reduce((p, f) => {
7792
const selAry = get(f,'value','').split(',');
7893
const allAry = get(f,'options',[]).map((o) => o.value);
@@ -87,18 +102,6 @@ function fieldsReducer(imageMasterData={}) {
87102
}
88103

89104

90-
ImageSelect.propTypes = {
91-
imageMasterData: PropTypes.arrayOf(PropTypes.object).isRequired,
92-
groupKey: PropTypes.string.isRequired,
93-
// this component needs to be wrapped by a FieldGroup. User of this component need to provide
94-
// a function so this component can handle field change even from reducing function.
95-
addChangeListener: PropTypes.func.isRequired,
96-
style: PropTypes.object,
97-
title: PropTypes.string
98-
};
99-
100-
const toFilterSelectAry = (groupKey, s) => getFieldVal(groupKey, `Filter_${s}`, '').split(',').map((d) => d.trim()).filter((d) => d);
101-
102105

103106
/*--------------------------- Filter Panel ---------------------------------------------*/
104107

@@ -214,7 +217,7 @@ const toFilterSummary = (master, key, desc) => Object.entries(countBy(master, (d
214217
/*--------------------------- Data Product List ---------------------------------------*/
215218

216219
// eslint-disable-next-line
217-
function DataProductList({filteredImageData, groupKey, onChange}) {
220+
function DataProductList({filteredImageData, groupKey, multiSelect, onChange}) {
218221
const projects= uniqBy(filteredImageData, 'project').map( (d) => d.project);
219222
const setDSListMode = (flg) => {
220223
projects.forEach((k) => dispatchComponentStateChange(k, {isOpen:flg}));
@@ -230,7 +233,7 @@ function DataProductList({filteredImageData, groupKey, onChange}) {
230233
<div className='DataProductList__view'>
231234
{
232235
projects.map((p) =>
233-
<DataProduct key={p} {...{groupKey, project:p, filteredImageData}}/>
236+
<DataProduct key={p} {...{groupKey, project:p, filteredImageData, multiSelect}}/>
234237
)
235238
}
236239
</div>
@@ -239,7 +242,7 @@ function DataProductList({filteredImageData, groupKey, onChange}) {
239242
}
240243

241244
// eslint-disable-next-line
242-
function DataProduct({groupKey, project, filteredImageData}) {
245+
function DataProduct({groupKey, project, filteredImageData, multiSelect}) {
243246

244247
// filter projects ... projects is like dataproduct or dataset.. i.e SEIP
245248
const projectData= filteredImageData.filter((d) => d.project === project);
@@ -249,11 +252,11 @@ function DataProduct({groupKey, project, filteredImageData}) {
249252

250253
return (
251254
<div className='DataProductList__item'>
252-
<CollapsiblePanel componentKey={project} header={<Header {...{project}}/>} isOpen={isOpen}>
255+
<CollapsiblePanel componentKey={project} header={<Header {...{project, multiSelect}}/>} isOpen={isOpen}>
253256
<div className='DataProductList__item--details'>
254257
{
255258
subProjects.map((sp) =>
256-
<BandSelect key={'sub_' + sp} {...{subProject:sp, projectData, labelMaxWidth}}/>
259+
<BandSelect key={'sub_' + sp} {...{groupKey, subProject:sp, projectData, labelMaxWidth, multiSelect}}/>
257260
)
258261
}
259262
</div>
@@ -263,8 +266,10 @@ function DataProduct({groupKey, project, filteredImageData}) {
263266

264267
}
265268

266-
function Header({project}) {
269+
function Header({project, multiSelect}) {
267270
const fieldKey= `PROJ_ALL_${project}`;
271+
272+
if (!multiSelect) return <div style={{display: 'inline-block'}}>{project}</div>;
268273
return (
269274
<div className='DataProductList__item--header' onClick={(e) => e.stopPropagation()}>
270275
<CheckboxGroupInputField
@@ -285,12 +290,12 @@ function Header({project}) {
285290

286291
const hasImageSelection = (groupKey, proj) => {
287292
const fields = FieldGroupUtils.getGroupFields(groupKey) || {};
288-
return Object.values(fields).some((fld) => fld.fieldKey.startsWith(`IMAGES_${proj}`) && fld.value);
293+
return Object.values(fields).some((fld) => get(fld, 'fieldKey', '').startsWith(`IMAGES_${proj}`) && fld.value);
289294
};
290295

291296

292297
// eslint-disable-next-line
293-
function BandSelect({subProject, projectData, labelMaxWidth}) {
298+
function BandSelect({groupKey, subProject, projectData, labelMaxWidth, multiSelect}) {
294299
subProject = isNil(subProject) ? '' : subProject;
295300
const fieldKey= `IMAGES_${get(projectData, [0, 'project'])}||${subProject}`;
296301
const options = toImageOptions(projectData.filter( (p) => p.subProject === subProject));
@@ -300,24 +305,46 @@ function BandSelect({subProject, projectData, labelMaxWidth}) {
300305
className='DataProductList__item--bandLabel'>{subProject}</div>
301306
<span>:</span>
302307
</div>);
303-
return (
304-
<div className='DataProductList__item--band'>
305-
{label}
306-
<CheckboxGroupInputField
307-
key={fieldKey}
308-
fieldKey={fieldKey}
309-
initialState={{
308+
if (multiSelect) {
309+
return (
310+
<div className='DataProductList__item--band'>
311+
{label}
312+
<CheckboxGroupInputField
313+
key={fieldKey}
314+
fieldKey={fieldKey}
315+
initialState={{
310316
options, // Note: values in initialState are saved into fieldgroup. options are used in the reducer above to determine what 'all' means.
311317
value: '',
312318
tooltip: 'Please select some boxes',
313319
label : '' }}
314-
options={options}
315-
alignment='horizontal'
316-
labelWidth={35}
317-
wrapperStyle={{whiteSpace: 'normal'}}
318-
/>
319-
</div>
320-
);
320+
options={options}
321+
alignment='horizontal'
322+
labelWidth={35}
323+
wrapperStyle={{whiteSpace: 'normal'}}
324+
/>
325+
</div>
326+
);
327+
} else {
328+
return (
329+
<div className='DataProductList__item--band'>
330+
{label}
331+
<RadioGroupInputField
332+
key={fieldKey}
333+
fieldKey={`IMAGES_${groupKey}`}
334+
initialState={{
335+
options, // Note: values in initialState are saved into fieldgroup. options are used in the reducer above to determine what 'all' means.
336+
value: '',
337+
tooltip: 'Please select some boxes',
338+
label : '' }}
339+
options={options}
340+
defaultValue=''
341+
alignment='horizontal'
342+
labelWidth={35}
343+
wrapperStyle={{whiteSpace: 'normal'}}
344+
/>
345+
</div>
346+
);
347+
}
321348
}
322349

323350
const toImageOptions= (a) => a.map ( (d) => ({label: d.title, value: d.imageId}));

src/firefly/js/ui/NewImageSearchPanel.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function renderPanel(imageMasterData) {
121121
label={'Choose Radius'}
122122
/>
123123

124-
<ImageSelect key='ImageSelect' {...{groupKey:FG_KEY, addChangeListener, imageMasterData, style:{width: 800, height: 400}}} />
124+
<ImageSelect key='ImageSelect' {...{groupKey:FG_KEY, addChangeListener, imageMasterData, multiSelect: false, style:{width: 800, height: 400}}} />
125125

126126
</div>
127127
</FieldGroup>

src/firefly/js/ui/RadioGroupInputField.jsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import {isEmpty} from 'lodash';
3+
import {isEmpty, isUndefined, get} from 'lodash';
44
import {RadioGroupInputFieldView} from './RadioGroupInputFieldView.jsx';
55
import {fieldGroupConnector} from './FieldGroupConnector.jsx';
66

77

8-
const convertValue= (value,options) => (!value) ? options[0].value : value;
8+
const convertValue= (value,options,defaultValue) => {
9+
if (!value) {
10+
return isUndefined(defaultValue) ? options[0].value : defaultValue;
11+
} else {
12+
return value;
13+
}
14+
};
915

1016
function getProps(params, fireValueChange) {
1117

12-
var {value,options}= params;
13-
value= convertValue(value,options);
18+
var {value,options,defaultValue}= params;
19+
value= convertValue(value,options,defaultValue);
1420

1521
return Object.assign({}, params,
1622
{
@@ -20,8 +26,8 @@ function getProps(params, fireValueChange) {
2026
}
2127

2228
function handleOnChange(ev, params, fireValueChange) {
23-
var val = ev.target.value;
24-
var checked = ev.target.checked;
29+
var val = get(ev, 'target.value', '');
30+
var checked = get(ev, 'target.checked', false);
2531

2632
if (checked) {
2733
fireValueChange({ value: val, valid: true});
@@ -32,15 +38,19 @@ function handleOnChange(ev, params, fireValueChange) {
3238
const propTypes= {
3339
inline : PropTypes.bool,
3440
options: PropTypes.array.isRequired,
41+
defaultValue: PropTypes.string,
3542
alignment: PropTypes.string,
3643
labelWidth : PropTypes.number
3744
};
3845

3946
function checkForUndefined(v,props) {
40-
var optionContain = (v) => props.options.find((op) => op.value === v);
41-
42-
return isEmpty(props.options) ? v :
43-
(!v ? props.options[0].value : (optionContain(v) ? v : props.options[0].value));
47+
const {options, defaultValue} = props;
48+
var optionContain = (v) => v && options.find((op) => op.value === v);
49+
if (isEmpty(options) || optionContain(v)) {
50+
return v;
51+
} else {
52+
return isUndefined(defaultValue) ? options[0].value : defaultValue;
53+
}
4454
}
4555

4656

0 commit comments

Comments
 (0)