Skip to content

Commit e1a6014

Browse files
committed
IRSA-924: fix issues found in new image search
- add select/deselect all at the dataproduct(project) level - fix mission filter now that mission info is available - make count returns the number of dataproduct instead of bands(images) - make CollapsiblePanel's header takes a string or a component. - make ImageSelect requires a way to register changeListener - fix firefly-dev.html not showing images - add option to FormPanel to allow unmounted fields in the request - set NewImageSearchPanel to take unmounted fields
1 parent 9c9fdf3 commit e1a6014

File tree

7 files changed

+133
-42
lines changed

7 files changed

+133
-42
lines changed

src/firefly/html/firefly-dev.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
window.firefly = {
1414
app: {
1515
template: 'FireflyViewer',
16-
views: 'tables',
1716
options : {
1817
MenuItemKeys: {maskOverlay:true},
1918
}

src/firefly/js/ui/CheckboxGroupInputField.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function getProps(params, fireValueChange) {
5555

5656
var {value,options}= params;
5757
value= convertValue(value,options);
58-
58+
5959
return Object.assign({}, params,
6060
{ value,
6161
onChange: (ev) => handleOnChange(ev,params, fireValueChange)
@@ -79,7 +79,6 @@ function handleOnChange(ev, params, fireValueChange) {
7979
else {
8080
if (idx > -1) curValueArr.splice(idx, 1); // remove val from the array
8181
}
82-
8382
var {valid,message} = params.validator(curValueArr.toString());
8483

8584
fireValueChange({ value: curValueArr.toString(), message, valid });

src/firefly/js/ui/FormPanel.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function createSuccessHandler(action, params={}, title, onSubmit) {
5454

5555
export const FormPanel = function (props) {
5656
var {children, onSubmit, onCancel, onError, groupKey, action, params, title,
57-
submitText='Search', help_id, changeMasking} = props;
57+
submitText='Search', help_id, changeMasking, includeUnmounted=false} = props;
5858

5959
const style = {
6060
backgroundColor: 'white',
@@ -74,6 +74,7 @@ export const FormPanel = function (props) {
7474
<div style={{flexGrow: 0, display: 'inline-flex', justifyContent: 'space-between', width: '100%', alignItems: 'flex-end', padding:'2px 0px 3px'}}>
7575
<div>
7676
<CompleteButton style={{display: 'inline-block', marginRight: 10}}
77+
includeUnmounted={includeUnmounted}
7778
groupKey={groupKey}
7879
onSuccess={createSuccessHandler(action, params, title, onSubmit)}
7980
onFail={onError || handleFailfure}
@@ -102,7 +103,8 @@ FormPanel.propTypes = {
102103
action: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
103104
params: PropTypes.object,
104105
help_id: PropTypes.string,
105-
changeMasking: PropTypes.func
106+
changeMasking: PropTypes.func,
107+
includeUnmounted: PropTypes.boolean
106108
};
107109

108110
function handleChartAdd(request) {

src/firefly/js/ui/ImageSelect.css

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,11 @@
8080
}
8181

8282
.DataProductList__item--header {
83-
margin-left: 5px;
84-
font-size: large;
85-
font-weight: bold;
86-
color: #005da4;
83+
display: inline-block;
84+
white-space: nowrap;
85+
overflow: hidden;
86+
text-overflow: ellipsis;
87+
margin: -5px;
8788
}
8889

8990
.DataProductList__item--details {
@@ -102,4 +103,7 @@
102103
white-space: nowrap;
103104
text-align: right;
104105
margin: 2px 2px 0 0;
106+
overflow: hidden;
107+
text-overflow: ellipsis;
108+
max-width: 100px;
105109
}

src/firefly/js/ui/ImageSelect.jsx

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
import React, {PureComponent} from 'react';
66
import PropTypes from 'prop-types';
7-
import {uniqBy, get, countBy} from 'lodash';
7+
import {uniqBy, get, countBy, isNil, xor} from 'lodash';
88

99

1010
import {CheckboxGroupInputField} from './CheckboxGroupInputField.jsx';
1111
import {CollapsiblePanel} from '../ui/panel/CollapsiblePanel.jsx';
1212
import FieldGroupUtils, {getFieldVal} from '../fieldGroup/FieldGroupUtils.js';
1313
import {dispatchComponentStateChange} from '../core/ComponentCntlr.js';
14+
import {updateSet} from '../util/WebUtil.js';
1415

1516
import './ImageSelect.css';
1617

@@ -19,17 +20,22 @@ export class ImageSelect extends PureComponent {
1920

2021
constructor(props) {
2122
super(props);
22-
this.state= {lastMod:new Date().getTime(), filteredImageData:undefined}; // can be open, close, or auto.
23+
this.state= {lastMod:new Date().getTime()};
24+
props.addChangeListener && props.addChangeListener('ImageSelect', fieldsReducer(props.imageMasterData));
2325
}
2426

2527
render() {
2628
const {style, imageMasterData, groupKey} = this.props;
29+
imageMasterData.forEach((d)=> {
30+
['missionId', 'project', 'subProject'].forEach((k) => d[k] = d[k] || '');
31+
});
32+
2733
var {filteredImageData=imageMasterData} = this.state;
2834
const filterMission = toFilterSelectAry(groupKey, 'mission');
2935
const filterProjectType = toFilterSelectAry(groupKey, 'projectType');
3036
const filterwaveBand = toFilterSelectAry(groupKey, 'waveBand');
3137

32-
filteredImageData = filterMission.length > 0 ? filteredImageData.filter( (d) => filterMission.includes(d.project)) : filteredImageData;
38+
filteredImageData = filterMission.length > 0 ? filteredImageData.filter( (d) => filterMission.includes(d.missionId)) : filteredImageData;
3339
filteredImageData = filterProjectType.length > 0 ? filteredImageData.filter( (d) => filterProjectType.includes(d.projectTypeKey)) : filteredImageData;
3440
filteredImageData = filterwaveBand.length > 0 ? filteredImageData.filter( (d) => filterwaveBand.includes(d.wavelength)) : filteredImageData;
3541

@@ -42,10 +48,47 @@ export class ImageSelect extends PureComponent {
4248
}
4349

4450
}
51+
function fieldsReducer(imageMasterData={}) {
52+
return (inFields, action) => {
53+
const {fieldKey='', options={}, value=''} = action.payload;
54+
55+
if (fieldKey.startsWith('PROJ_ALL_')) {
56+
// a project checkbox is clicked
57+
const proj = fieldKey.replace('PROJ_ALL_', '');
58+
Object.entries(inFields).forEach( ([k, f]) => {
59+
if (k.startsWith(`IMAGES_${proj}`)) {
60+
if (value === '_all_') {
61+
const allVals = f.options ? f.options.map((o) => o.value).join() : '';
62+
inFields = updateSet(inFields, [k, 'value'], allVals);
63+
} else {
64+
inFields = updateSet(inFields, [k, 'value'], '');
65+
}
66+
}
67+
});
68+
} else if (fieldKey.startsWith('IMAGES_')) {
69+
// one item changed, update project selectAll checkbox
70+
const matcher = fieldKey.split('||')[0];
71+
const cbGroups = Object.values(inFields).filter((f) => f.fieldKey.startsWith(matcher)); // array of subproject in this project
72+
const allSelected = cbGroups.reduce((p, f) => {
73+
const selAry = get(f,'value','').split(',');
74+
const allAry = get(f,'options',[]).map((o) => o.value);
75+
return p && xor(selAry, allAry).length === 0;
76+
}, true);
77+
const proj = matcher.substring(7);
78+
inFields = updateSet(inFields, ['PROJ_ALL_' + proj, 'value'], (allSelected ? '_all_' : ''));
79+
}
80+
81+
return inFields;
82+
};
83+
}
84+
4585

4686
ImageSelect.propTypes = {
47-
imageMasterData: PropTypes.arrayOf(PropTypes.object),
48-
groupKey: PropTypes.string,
87+
imageMasterData: PropTypes.arrayOf(PropTypes.object).isRequired,
88+
groupKey: PropTypes.string.isRequired,
89+
// this component needs to be wrapped by a FieldGroup. User of this component need to provide
90+
// a function so this component can handle field change even from reducing function.
91+
addChangeListener: PropTypes.func.isRequired,
4992
style: PropTypes.object,
5093
title: PropTypes.string
5194
};
@@ -67,9 +110,10 @@ function FilterPanel({imageMasterData, title='4. Select Data Set', onChange}) {
67110

68111
// eslint-disable-next-line
69112
function FilterPanelView({onChange, imageMasterData}) {
70-
const missions = toCatSummary(imageMasterData, 'project', 'project');
71-
const projectTypes = toCatSummary(imageMasterData, 'projectTypeKey', 'projectTypeDesc');
72-
const waveBands = toCatSummary(imageMasterData, 'wavelength', 'wavelengthDesc');
113+
const forSummary = uniqBy(imageMasterData, (t) => `${t.missionId};${t.project}`);
114+
const missions = toFilterSummary(forSummary, 'missionId', 'missionId');
115+
const projectTypes = toFilterSummary(forSummary, 'projectTypeKey', 'projectTypeDesc');
116+
const waveBands = toFilterSummary(forSummary, 'wavelength', 'wavelengthDesc');
73117

74118
return (
75119
<div className='FilterPanel__view'>
@@ -159,7 +203,7 @@ function FilterSelectExpanded({selections, onClose}) {
159203
}
160204

161205
const toFilterOptions = (a) => a.map ( (d) => ({label: `${d.label} (${d.count})`, value: d.name}));
162-
const toCatSummary = (master, key, desc) => Object.entries(countBy(master, (d) => `${get(d, key)};${get(d, desc)}`))
206+
const toFilterSummary = (master, key, desc) => Object.entries(countBy(master, (d) => `${get(d, key)};${get(d, desc)}`))
163207
.map( ([d, c]) => ({name: d.split(';')[0], label: d.split(';')[1], count: c}));
164208

165209

@@ -201,7 +245,7 @@ function DataProduct({groupKey, project, filteredImageData}) {
201245

202246
return (
203247
<div className='DataProductList__item'>
204-
<CollapsiblePanel componentKey={project} header={project} isOpen={isOpen}>
248+
<CollapsiblePanel componentKey={project} header={<Header {...{project}}/>} isOpen={isOpen}>
205249
<div className='DataProductList__item--details'>
206250
{
207251
subProjects.map((sp) =>
@@ -215,6 +259,26 @@ function DataProduct({groupKey, project, filteredImageData}) {
215259

216260
}
217261

262+
function Header({project}) {
263+
const fieldKey= `PROJ_ALL_${project}`;
264+
return (
265+
<div className='DataProductList__item--header' onClick={(e) => e.stopPropagation()}>
266+
<CheckboxGroupInputField
267+
key={fieldKey}
268+
fieldKey={fieldKey}
269+
initialState={{
270+
value: '', // workaround for _all_ for now
271+
tooltip: 'Please select some boxes',
272+
label : '' }}
273+
options={[{label:project, value:'_all_'}]}
274+
alignment='horizontal'
275+
labelWidth={35}
276+
wrapperStyle={{whiteSpace: 'normal'}}
277+
/>
278+
</div>
279+
);
280+
}
281+
218282
const hasImageSelection = (groupKey, proj) => {
219283
const fields = FieldGroupUtils.getGroupFields(groupKey) || {};
220284
return Object.values(fields).some((fld) => fld.fieldKey.startsWith(`IMAGES_${proj}`) && fld.value);
@@ -223,17 +287,24 @@ const hasImageSelection = (groupKey, proj) => {
223287

224288
// eslint-disable-next-line
225289
function BandSelect({subProject, projectData, labelMaxWidth}) {
226-
const fieldKey= `IMAGES_${get(projectData, [0, 'project'])}_${subProject}`;
290+
subProject = isNil(subProject) ? '' : subProject;
291+
const fieldKey= `IMAGES_${get(projectData, [0, 'project'])}||${subProject}`;
227292
const options = toImageOptions(projectData.filter( (p) => p.subProject === subProject));
228-
const label = subProject && <div style={{minWidth: labelMaxWidth+'ch'}} className='DataProductList__item--bandLabel'>{subProject + ':'}</div>;
293+
const label = subProject && (
294+
<div style={{display: 'inline-flex'}}>
295+
<div style={{width: labelMaxWidth+1+'ch'}} title={subProject}
296+
className='DataProductList__item--bandLabel'>{subProject}</div>
297+
<span>:</span>
298+
</div>);
229299
return (
230300
<div className='DataProductList__item--band'>
231301
{label}
232302
<CheckboxGroupInputField
233303
key={fieldKey}
234304
fieldKey={fieldKey}
235305
initialState={{
236-
value: '', // workaround for _all_ for now
306+
options, // Note: values in initialState are saved into fieldgroup. options are used in the reducer above to determine what 'all' means.
307+
value: '',
237308
tooltip: 'Please select some boxes',
238309
label : '' }}
239310
options={options}

src/firefly/js/ui/NewImageSearchPanel.jsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import React, {PureComponent} from 'react';
66
import PropTypes from 'prop-types';
7-
import {uniqBy, get} from 'lodash';
87

98
import {FormPanel} from './FormPanel.jsx';
109
import {FieldGroup} from '../ui/FieldGroup.jsx';
@@ -16,11 +15,9 @@ import {showInfoPopup} from './PopupUtil.jsx';
1615
import {dispatchHideDropDown} from '../core/LayoutCntlr.js';
1716

1817
import FieldGroupUtils from '../fieldGroup/FieldGroupUtils.js';
19-
import {CheckboxGroupInputField} from './CheckboxGroupInputField.jsx';
2018
import {parseWorldPt} from '../visualize/Point.js';
2119
import WebPlotRequest, {WPConst} from '../visualize/WebPlotRequest.js';
2220
import {dispatchPlotImage} from '../visualize/ImagePlotCntlr.js';
23-
import {imageMasterData} from '../data/ImageMasterData.js';
2421
import {getImageMasterData} from '../visualize/ui/AllImageSearchConfig.js';
2522
import {ImageSelect} from './ImageSelect.jsx';
2623

@@ -100,11 +97,13 @@ function renderPanel(imageMasterData) {
10097
<FormPanel
10198
width='640px' height='500px'
10299
groupKey={FG_KEY}
100+
includeUnmounted={true}
103101
params={{hideOnInvalid: false}}
104-
onSubmit={(request) => onSearchSubmit(request)}
102+
onSubmit={(request) => onSearchSubmit(request, imageMasterData)}
105103
onError={(request) => searchFailed(request)}
106104
onCancel={hideSearchPanel}>
107-
<FieldGroup groupKey={FG_KEY} validatorFunc={null} keepState={true}>
105+
<FieldGroup groupKey={FG_KEY} validatorFunc={null}
106+
reducerFunc={mainReducer} keepState={true}>
108107
<div style={{padding:'5px 0 0 0', display: 'flex', textAlign:'center', justifyContent: 'center' }}>
109108
<TargetPanel/>
110109
</div>
@@ -122,7 +121,7 @@ function renderPanel(imageMasterData) {
122121
label={'Choose Radius'}
123122
/>
124123

125-
<ImageSelect key='ImageSelect' {...{groupKey:FG_KEY, imageMasterData, style:{width: 700, height: 300}}} />
124+
<ImageSelect key='ImageSelect' {...{groupKey:FG_KEY, addChangeListener, imageMasterData, style:{width: 800, height: 400}}} />
126125

127126
</div>
128127
</FieldGroup>
@@ -141,20 +140,33 @@ NewImageSearchPanel.defaultProps = {
141140
};
142141

143142

143+
// map of key/listener
144+
const changeListeners = {};
145+
function addChangeListener(key, changeListener) {
146+
changeListeners[key] = changeListener;
147+
}
148+
function mainReducer(inFields, action) {
149+
// put reducing logic here is any
150+
151+
// call all listeners for
152+
inFields = Object.values(changeListeners).reduce( (p, l) => l(p, action), inFields);
153+
return inFields;
154+
};
155+
156+
144157
function hideSearchPanel() {
145158
dispatchHideDropDown();
146159
}
147160

148161

149-
function onSearchSubmit(request) {
162+
function onSearchSubmit(request, imageMasterData) {
150163
console.log(request);
151164
const validInfo= validateInput(request);
152165
if (!validInfo.valid) {
153166
showInfoPopup(validInfo.message);
154167
return false;
155168
}
156-
doImageSearch(request);
157-
console.log(request.imageSelection);
169+
doImageSearch(request, imageMasterData);
158170
}
159171

160172
function searchFailed(request) {
@@ -185,7 +197,7 @@ function validateInput(request) {
185197

186198

187199
//todo: after UI changes: needs to come up with list of plotRequestParams to make into WebPlotRequest
188-
function doImageSearch(request) {
200+
function doImageSearch(request, imageMasterData) {
189201
const wp = parseWorldPt(request[ServerParams.USER_TARGET_WORLD_PT]);
190202
const radius= request.conesize;
191203

@@ -198,6 +210,8 @@ function doImageSearch(request) {
198210

199211
const paramAry= imageMasterData.filter( (d) => imageIdList.includes(d.imageId));
200212
loadImages(wp,radius,paramAry);
213+
console.log(paramAry);
214+
201215
}
202216

203217
//-------------------------------------------------------------------------

0 commit comments

Comments
 (0)