Skip to content

Commit f092f19

Browse files
authored
DM-8302 merge opposite axis location pr #265
DM-8302 options for opposite axis location: X on top, Y on right
2 parents 2d64994 + d938872 commit f092f19

File tree

6 files changed

+186
-169
lines changed

6 files changed

+186
-169
lines changed

src/firefly/js/charts/chartTypes/HistogramTblView.jsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
33
*/
44

5-
import {get} from 'lodash';
5+
import {get, set} from 'lodash';
66
import React, {PropTypes} from 'react';
77
import * as TblUtil from '../../tables/TableUtil.js';
88

@@ -41,18 +41,20 @@ function Chart(props) {
4141
const { isDataReady, data:histogramData, options:histogramParams} = ChartsCntlr.getChartDataElement(chartId);
4242

4343
if (isDataReady) {
44-
var logs, reversed;
44+
var logs;
45+
const xAxis = histogramParams.xAxis || {reversed: false, opposite: false};
46+
const yAxis = histogramParams.yAxis || {reversed: false, opposite: false};
4547
if (histogramParams) {
4648
var logvals = '';
4749
if (histogramParams.x.includes('log')) { logvals += 'x';}
4850
if (histogramParams.y.includes('log')) { logvals += 'y';}
4951
if (logvals.length>0) { logs = logvals;}
5052

51-
var rvals = '';
52-
if (histogramParams.x.includes('flip')) { rvals += 'x';}
53-
if (histogramParams.y.includes('flip')) { rvals += 'y';}
54-
if (rvals.length>0) { reversed = rvals;}
53+
if (histogramParams.x.includes('flip')) { set(xAxis, 'reversed', true); }
54+
if (histogramParams.y.includes('flip')) { set(yAxis, 'reversed', true); }
5555

56+
if (histogramParams.x.includes('opposite')) { set(xAxis, 'opposite', true); }
57+
if (histogramParams.y.includes('opposite')) { set(yAxis, 'opposite', true); }
5658
}
5759

5860
return (
@@ -62,7 +64,8 @@ function Chart(props) {
6264
height={heightPx}
6365
width={widthPx}
6466
logs={logs}
65-
reversed={reversed}
67+
xAxis={xAxis}
68+
yAxis={yAxis}
6669
/>
6770
);
6871
} else {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
3+
*/
4+
import React, {PropTypes} from 'react';
5+
6+
import {get} from 'lodash';
7+
import {dispatchValueChange} from '../../fieldGroup/FieldGroupCntlr.js';
8+
import {TextButton} from '../../ui/TextButton.jsx';
9+
import {SuggestBoxInputField} from '../../ui/SuggestBoxInputField.jsx';
10+
import ColValuesStatistics from '../ColValuesStatistics.js';
11+
import {showColSelectPopup} from './ColSelectView.jsx';
12+
13+
14+
/*
15+
* Split content into prior content and the last alphanumeric token in the text
16+
* @param {string} text - current content of suggest box
17+
* @return {Object} with token and priorContent properties
18+
*/
19+
function parseSuggestboxContent(text) {
20+
let token='', priorContent='';
21+
if (text && text.length) {
22+
// [entireMatch, firstCature, secondCapture] or null
23+
const match = text.match(/^(.*[^A-Za-z\d_]|)([A-Za-z\d_]*)$/);
24+
if (match && match.length === 3) {
25+
priorContent = match[1];
26+
token = match[2];
27+
}
28+
}
29+
return {token, priorContent};
30+
}
31+
32+
export function ColumnOrExpression({colValStats,params,groupKey,fldPath,label,labelWidth=30,tooltip,nullAllowed}) {
33+
34+
// the suggestions are indexes in the colValStats array - it makes it easier to render then with labels
35+
const allSuggestions = colValStats.map((colVal,idx)=>{return idx;});
36+
37+
const getSuggestions = (val)=>{
38+
const {token} = parseSuggestboxContent(val);
39+
const matches = allSuggestions.filter( (idx)=>{return colValStats[idx].name.startsWith(token);} );
40+
return matches.length ? matches : allSuggestions;
41+
};
42+
43+
const renderSuggestion = (idx)=>{
44+
const colVal = colValStats[idx];
45+
return colVal.name + (colVal.unit && colVal.unit !== 'null' ? ', '+colVal.unit : ' ');
46+
};
47+
48+
const valueOnSuggestion = (prevVal, idx)=>{
49+
const {priorContent} = parseSuggestboxContent(prevVal);
50+
return priorContent+colValStats[idx].name;
51+
};
52+
53+
var val = get(params, fldPath);
54+
const onColSelected = (colName) => {
55+
val = colName;
56+
dispatchValueChange({fieldKey: fldPath, groupKey, value: colName, valid: true});
57+
};
58+
return (
59+
<div style={{whiteSpace: 'nowrap'}}>
60+
<SuggestBoxInputField
61+
inline={true}
62+
initialState= {{
63+
value: get(params, fldPath),
64+
tooltip: `Column or expression for ${tooltip}`,
65+
label: `${label}:`,
66+
nullAllowed
67+
}}
68+
getSuggestions={getSuggestions}
69+
renderSuggestion={renderSuggestion}
70+
valueOnSuggestion={valueOnSuggestion}
71+
fieldKey={fldPath}
72+
groupKey={groupKey}
73+
labelWidth={labelWidth}
74+
/>
75+
<TextButton style={{display: 'inline-block', paddingLeft: 3, verticalAlign: 'bottom'}}
76+
groupKey={groupKey}
77+
text='Cols'
78+
tip={`Select ${label} column`}
79+
onClick={() => showColSelectPopup(colValStats, onColSelected,`Choose ${label}`,`Set ${label}`,val)}
80+
/>
81+
</div>
82+
);
83+
}
84+
85+
86+
ColumnOrExpression.propTypes = {
87+
colValStats: PropTypes.arrayOf(PropTypes.instanceOf(ColValuesStatistics)).isRequired,
88+
params: PropTypes.object,
89+
groupKey: PropTypes.string.isRequired,
90+
fldPath: PropTypes.string.isRequired,
91+
label: PropTypes.string.isRequired,
92+
labelWidth: PropTypes.number,
93+
tooltip: PropTypes.string,
94+
nullAllowed: PropTypes.bool
95+
};

src/firefly/js/charts/ui/Histogram.jsx

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import React, {PropTypes} from 'react';
66
import ReactHighcharts from 'react-highcharts';
77
import numeral from 'numeral';
88
import {get, set} from 'lodash';
9+
import shallowequal from 'shallowequal';
910
import {getFormatString} from '../../util/MathUtil.js';
1011
import {logError} from '../../util/WebUtil.js';
1112

@@ -51,6 +52,9 @@ export class Histogram extends React.Component {
5152
* @param {number} props.width - width of the chart in pixels
5253
* @param {number} props.height - height of the chart in pixels
5354
* @param {string} [props.logs] - can have values 'x', 'y', or 'xy'
55+
* @param {Object} [props.xAxis] - Highcharts xAxis properties
56+
* @param {Object} [props.yAxis] = Highcharts yAxis properties
57+
* @param {string} [props.opposite] - can have values 'x', 'y', or 'xy'
5458
* @param {string} [props.binColor='#d1d1d1'] - darker bin color
5559
* @param {string} props.desc - description
5660
* @public
@@ -63,41 +67,39 @@ export class Histogram extends React.Component {
6367
}
6468

6569
shouldComponentUpdate(nextProps) {
66-
const {series, data, width, height, logs, reversed, desc, binColor} = this.props;
67-
// should rerender only if data or bin color has changed
70+
const {series, data, width, height, logs, xAxis, yAxis, desc, binColor} = this.props;
71+
// should rerender if data, logs, or bin color has changed
6872
// otherwise just change the existing chart
69-
if (series !== nextProps.series || data !== nextProps.data || binColor !== nextProps.binColor) { return true; }
73+
if (this.error || series !== nextProps.series || data !== nextProps.data || logs !== nextProps.logs || binColor !== nextProps.binColor) { return true; }
7074
const chart = this.refs.chart && this.refs.chart.getChart();
7175
if (chart) {
72-
let doUpdate = false;
73-
if (height !== nextProps.height || width !== nextProps.width ) {
74-
chart.setSize(nextProps.width, nextProps.height, false);
75-
return false;
76-
}
76+
try {
77+
let doUpdate = false;
78+
if (height !== nextProps.height || width !== nextProps.width ) {
79+
chart.setSize(nextProps.width, nextProps.height, false);
80+
}
7781

78-
if (desc !== nextProps.desc) {
79-
chart.xAxis[0].setTitle(nextProps.desc, false);
80-
doUpdate = true;
81-
}
82-
const nreversed = nextProps.reversed;
83-
if (reversed !== nreversed){
84-
const yReversed = Boolean(nreversed && nreversed.indexOf('y')>-1);
85-
const xReversed = Boolean(nreversed && nreversed.indexOf('x')>-1);
86-
chart.xAxis[0].update({reversed : xReversed, opposite: yReversed}, false);
87-
chart.yAxis[0].update({reversed : yReversed}, false);
88-
doUpdate = true;
89-
}
90-
const nlogs = nextProps.logs;
91-
if (logs !== nextProps.logs){
92-
const xtype = nlogs && nlogs.indexOf('x')>-1 ? 'logarithmic' : 'linear';
93-
const ytype = nlogs && nlogs.indexOf('y')>-1 ? 'logarithmic' : 'linear';
94-
chart.xAxis[0].update({type : xtype}, false);
95-
chart.yAxis[0].update({type : ytype}, false);
96-
doUpdate = true;
82+
if (desc !== nextProps.desc) {
83+
chart.xAxis[0].setTitle(nextProps.desc, false);
84+
doUpdate = true;
85+
}
86+
87+
if (!shallowequal(xAxis, nextProps.xAxis)) {
88+
chart.xAxis[0].update(nextProps.xAxis, false);
89+
doUpdate = true;
90+
}
91+
if (!shallowequal(yAxis, nextProps.yAxis)) {
92+
chart.yAxis[0].update(nextProps.yAxis, false);
93+
doUpdate = true;
94+
}
95+
96+
if (doUpdate) { chart.redraw(false); }
97+
} catch (error) {
98+
this.error = error;
99+
chart.showLoading(error);
97100
}
98-
if (doUpdate) { chart.redraw(false); }
99-
return false;
100101
}
102+
return false;
101103
}
102104

103105
/*
@@ -326,14 +328,13 @@ export class Histogram extends React.Component {
326328
}
327329

328330
render() {
331+
this.error = undefined;
329332

330-
const { binColor, data, desc, width, height, logs, reversed}= this.props;
333+
const { binColor, data, desc, width, height, logs, xAxis, yAxis}= this.props;
331334
let series = this.props.series;
332335
if (!series) {
333336
series = [{data, binColor, name: 'data points'}];
334337
}
335-
const yReversed = (reversed && reversed.indexOf('y')>-1 ? true : false);
336-
337338

338339
let minY = 0;
339340
// what should be the minimum y value be?
@@ -415,17 +416,15 @@ export class Histogram extends React.Component {
415416
}
416417
},
417418
series: [],
418-
xAxis: {
419+
xAxis: Object.assign({
419420
lineColor: '#999',
420421
tickColor: '#ccc',
421422
title: {
422423
text: desc
423424
},
424-
opposite: yReversed,
425-
reversed: (reversed && reversed.indexOf('x')>-1 ? true : false),
426425
type: (logs && logs.indexOf('x')>-1 ? 'logarithmic' : 'linear')
427-
},
428-
yAxis: {
426+
}, xAxis),
427+
yAxis: Object.assign({
429428
gridLineColor: '#e9e9e9',
430429
tickWidth: 1,
431430
tickLength: 3,
@@ -436,9 +435,8 @@ export class Histogram extends React.Component {
436435
title: {
437436
text: ''
438437
},
439-
reversed: yReversed,
440438
type: (logs && logs.indexOf('y')>-1 ? 'logarithmic' : 'linear')
441-
},
439+
}, yAxis),
442440
credits: {
443441
enabled: false // removes a reference to Highcharts.com from the chart
444442
}
@@ -459,17 +457,20 @@ export class Histogram extends React.Component {
459457

460458
Histogram.defaultProps = {
461459
desc: 'Sample Distribution',
462-
binColor: '#d1d1d1'
460+
binColor: '#d1d1d1',
461+
xAxis: {},
462+
yAxis: {}
463463
};
464464

465465

466466
// when more than one histogram is defined use series
467467
Histogram.propTypes = {
468468
series: PropTypes.arrayOf(PropTypes.object), // array of objects with data, binColor, and name properties
469+
xAxis: PropTypes.object,
470+
yAxis: PropTypes.object,
469471
width: PropTypes.number,
470472
height: PropTypes.number,
471473
logs: PropTypes.oneOf(['x','y','xy']),
472-
reversed: PropTypes.oneOf(['x','y','xy']),
473474
desc: PropTypes.string,
474475
data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), // array of numbers [0] - nInBin, [1] - binMin, [2] - binMax
475476
binColor(props, propName, componentName) {

src/firefly/js/charts/ui/HistogramOptions.jsx

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import {InputGroup} from '../../ui/InputGroup.jsx';
1111
import Validate from '../../util/Validate.js';
1212
import {ValidationField} from '../../ui/ValidationField.jsx';
1313
import {CheckboxGroupInputField} from '../../ui/CheckboxGroupInputField.jsx';
14-
import {ListBoxInputField} from '../../ui/ListBoxInputField.jsx';
1514
import {RadioGroupInputField} from '../../ui/RadioGroupInputField.jsx';
1615
import {FieldGroupCollapsible} from '../../ui/panel/CollapsiblePanel.jsx';
16+
import {ColumnOrExpression} from './ColumnOrExpression.jsx';
1717

1818
export const histogramParamsShape = PropTypes.shape({
1919
algorithm : PropTypes.oneOf(['fixedSizeBins','bayesianBlocks']),
@@ -125,6 +125,7 @@ export class HistogramOptions extends React.Component {
125125

126126
render() {
127127
const { colValStats, groupKey, histogramParams, defaultParams, onOptionsSelected}= this.props;
128+
const xProps = {colValStats,params:histogramParams,groupKey,fldPath:'columnOrExpr',label:'Column or expression', labelWidth:120, tooltip:'X Axis',nullAllowed:false};
128129
return (
129130
<div style={{padding:'0 5px'}}>
130131
<FieldGroup groupKey={groupKey} validatorFunc={null} keepState={true}>
@@ -142,26 +143,8 @@ export class HistogramOptions extends React.Component {
142143
<button type='button' className='button std' onClick={() => setOptions(groupKey, defaultParams)}>Reset</button>
143144
</div>
144145
</div>}
146+
<ColumnOrExpression {...xProps}/>
145147

146-
<ListBoxInputField
147-
initialState= {{
148-
value: get(histogramParams, 'columnOrExpr'),
149-
tooltip: 'Please select a column',
150-
label : 'Column or expression:'
151-
}}
152-
options={
153-
colValStats.map((colVal) => {
154-
return {
155-
label: colVal.name + ' ' + (colVal.unit && colVal.unit !== 'null' ? colVal.unit : ''),
156-
value: colVal.name
157-
};
158-
})
159-
}
160-
multiple={false}
161-
fieldKey='columnOrExpr'
162-
groupKey={groupKey}
163-
labelWidth={120}
164-
/>
165148
<FieldGroupCollapsible header='Options'
166149
initialState= {{ value:'closed' }}
167150
fieldKey='plotoptions'>
@@ -173,8 +156,9 @@ export class HistogramOptions extends React.Component {
173156
label : 'X:'
174157
}}
175158
options={[
176-
{label: 'log', value: 'log'},
177-
{label: 'flip', value: 'flip'}
159+
{label: 'reverse', value: 'flip'},
160+
{label: 'top', value: 'opposite'},
161+
{label: 'log', value: 'log'}
178162
]}
179163
fieldKey='x'
180164
/>
@@ -185,8 +169,9 @@ export class HistogramOptions extends React.Component {
185169
label : 'Y:'
186170
}}
187171
options={[
188-
{label: 'log', value: 'log'},
189-
{label: 'flip', value: 'flip'}
172+
{label: 'reverse', value: 'flip'},
173+
{label: 'right', value: 'opposite'},
174+
{label: 'log', value: 'log'}
190175
]}
191176
fieldKey='y'
192177
/>

0 commit comments

Comments
 (0)