Skip to content

Commit 29c588d

Browse files
committed
Fix graph axis handling
1 parent 654b948 commit 29c588d

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

src/components/Chart/Chart.react.js

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default class Chart extends React.Component {
5959
}
6060

6161
render() {
62-
const { width, height, data } = this.props;
62+
const { width, height, data, xAxisType = 'time', hideXAxisLabels = false } = this.props;
6363
const plotting = {};
6464
let minX = Infinity;
6565
let maxX = -Infinity;
@@ -83,7 +83,12 @@ export default class Chart extends React.Component {
8383
}
8484
plotting[key] = { data: ordered, index: data[key].index };
8585
}
86-
const timeBuckets = Charting.timeAxisBuckets(minX, maxX);
86+
let timeBuckets;
87+
if (xAxisType === 'index') {
88+
timeBuckets = Charting.numericAxisBuckets(minX, maxX);
89+
} else {
90+
timeBuckets = Charting.timeAxisBuckets(minX, maxX);
91+
}
8792
const valueBuckets = Charting.valueAxisBuckets(maxY || 10);
8893
const groups = [];
8994
for (const key in plotting) {
@@ -134,23 +139,28 @@ export default class Chart extends React.Component {
134139
t =>
135140
(chartWidth * (t - timeBuckets[0])) / (timeBuckets[timeBuckets.length - 1] - timeBuckets[0])
136141
);
137-
let last = null;
138-
const tickLabels = timeBuckets.map((t, i) => {
139-
let text = '';
140-
if (timeBuckets.length > 20 && i % 2 === 0) {
141-
return '';
142-
}
143-
if (!last || t.getMonth() !== last.getMonth()) {
144-
text += shortMonth(t.getMonth()) + ' ';
145-
}
146-
if (!last || t.getDate() !== last.getDate()) {
147-
text += t.getDate();
148-
} else if (last && t.getHours() !== last.getHours()) {
149-
text += t.getHours() + ':00';
150-
}
151-
last = t;
152-
return text;
153-
});
142+
let tickLabels;
143+
if (xAxisType === 'index') {
144+
tickLabels = timeBuckets.map(t => t);
145+
} else {
146+
let last = null;
147+
tickLabels = timeBuckets.map((t, i) => {
148+
let text = '';
149+
if (timeBuckets.length > 20 && i % 2 === 0) {
150+
return '';
151+
}
152+
if (!last || t.getMonth() !== last.getMonth()) {
153+
text += shortMonth(t.getMonth()) + ' ';
154+
}
155+
if (!last || t.getDate() !== last.getDate()) {
156+
text += t.getDate();
157+
} else if (last && t.getHours() !== last.getHours()) {
158+
text += t.getHours() + ':00';
159+
}
160+
last = t;
161+
return text;
162+
});
163+
}
154164
let popup = null;
155165
if (this.state.hoverValue !== null) {
156166
const style = {
@@ -191,17 +201,19 @@ export default class Chart extends React.Component {
191201
</div>
192202
))}
193203
</div>
194-
<div className={styles.xAxis}>
195-
{tickLabels.map((t, i) => (
196-
<div
197-
key={t + '_' + i}
198-
className={styles.tick}
199-
style={{ left: tickPoints[i] + MARGIN_LEFT }}
200-
>
201-
{t}
202-
</div>
203-
))}
204-
</div>
204+
{!hideXAxisLabels && (
205+
<div className={styles.xAxis}>
206+
{tickLabels.map((t, i) => (
207+
<div
208+
key={t + '_' + i}
209+
className={styles.tick}
210+
style={{ left: tickPoints[i] + MARGIN_LEFT }}
211+
>
212+
{t}
213+
</div>
214+
))}
215+
</div>
216+
)}
205217
<svg width={chartWidth + 10} height={chartHeight + 10}>
206218
<g>
207219
{labelHeights.map(h => (
@@ -245,4 +257,6 @@ Chart.propTypes = {
245257
'It receives the numeric value of a point and label, and should return a string. ' +
246258
'This is ideally used for providing descriptive units like "active installations."'
247259
),
260+
xAxisType: PropTypes.string.describe('Axis type: "time" or "index"'),
261+
hideXAxisLabels: PropTypes.bool.describe('Hide labels on the x-axis'),
248262
};

src/components/GraphPanel/GraphPanel.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ export default function GraphPanel({ selectedCells, order, data, columns, width
2929
const columnNames = order.slice(colStart, colEnd + 1).map(o => o.name);
3030
const columnTypes = columnNames.map(name => columns[name]?.type);
3131

32+
const isSingleColumn = columnNames.length === 1;
33+
3234
const initialUseXAxis =
33-
columnNames.length > 1 &&
35+
!isSingleColumn &&
3436
(columnTypes[0] === 'Date' || columnTypes[0] === 'Number') &&
3537
columnTypes.slice(1).some(t => t === 'Number');
3638

@@ -100,6 +102,7 @@ export default function GraphPanel({ selectedCells, order, data, columns, width
100102
}
101103

102104
const chartWidth = width - 20;
105+
const xAxisType = useXAxis ? 'time' : 'index';
103106
return (
104107
<div className={styles.graphPanel}>
105108
<div className={styles.options}>
@@ -108,11 +111,18 @@ export default function GraphPanel({ selectedCells, order, data, columns, width
108111
type="checkbox"
109112
checked={useXAxis}
110113
onChange={() => setUseXAxis(!useXAxis)}
114+
disabled={isSingleColumn}
111115
/>{' '}
112116
Use first column as X-axis
113117
</label>
114118
</div>
115-
<Chart width={chartWidth} height={400} data={chartData} />
119+
<Chart
120+
width={chartWidth}
121+
height={400}
122+
data={chartData}
123+
xAxisType={xAxisType}
124+
hideXAxisLabels={isSingleColumn}
125+
/>
116126
</div>
117127
);
118128
}

src/lib/Charting.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ export function valueAxisBuckets(max) {
7373
return buckets;
7474
}
7575

76+
// Determines the points on a numeric x-axis for sequential data
77+
export function numericAxisBuckets(min, max) {
78+
if (min === max) {
79+
return [min];
80+
}
81+
return [min, max];
82+
}
83+
7684
// Determines the x,y points on the chart for each data point
7785
export function getDataPoints(chartWidth, chartHeight, timeBuckets, valueBuckets, dataPoints) {
7886
const xLength = timeBuckets[timeBuckets.length - 1] - timeBuckets[0];

0 commit comments

Comments
 (0)