Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add histogram mode #218

Merged
merged 43 commits into from
Jun 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b921dba
feat: add discover datasets & story
emmacunningham May 16, 2019
b49d91c
fix: fix tslint for test data
emmacunningham May 20, 2019
4d3c252
feat(bar_chart): add prop to enable histogram mode
emmacunningham May 20, 2019
fa43c4e
Merge branch 'master' into feat/discover
emmacunningham May 21, 2019
b6c307f
Merge branch 'feat/discover' into feat/histogram-mode
emmacunningham May 21, 2019
7d0634f
docs(bar_chart): add line annotation for time
emmacunningham May 21, 2019
ff4db28
feat: move enableHistogramMode to BarSeriesSpec
emmacunningham May 23, 2019
0356e24
chore: merge master into feat/histogram-mooe
emmacunningham May 23, 2019
58629ef
feat(bars): shift bars on enableHistogramMode
emmacunningham May 24, 2019
c61fad9
feat: update enableHistogramMode on state change
emmacunningham May 24, 2019
6393935
docs(bars): add theme for discovery story
emmacunningham May 24, 2019
f95ca82
feat: adjust chart dimensions for enableHistogramMode
emmacunningham May 25, 2019
32df0a3
feat: move histogram offset to axis
emmacunningham May 28, 2019
fe140e5
feat: shift line & area geometries if enableHistogramMode
emmacunningham May 28, 2019
59956ec
feat: adjust xScaleOffset by barsPadding
emmacunningham May 28, 2019
126d4d3
docs: add knob for rotation
emmacunningham May 29, 2019
b83c4aa
feat(histogram): control point alignment with HistogramModeAlignment
emmacunningham May 29, 2019
c9de668
feat(line_annotation): add alignment options
emmacunningham May 30, 2019
dab5dff
feat: add story for rect annotation
emmacunningham May 30, 2019
2b7ea96
test(utils): add test for computeXScaleOffset
emmacunningham May 30, 2019
282634d
test(histogram): add tests for enabling histogram mode
emmacunningham May 30, 2019
b3130fd
feat: add HistogramBarSeriesSpec and stack bar series
emmacunningham May 30, 2019
3169186
docs: simplify histogram stories data
emmacunningham Jun 3, 2019
3d3586a
test(axis_utils): add test for histogram mode
emmacunningham Jun 3, 2019
5ed2a6c
test: add tests for histogram mode series accessors
emmacunningham Jun 5, 2019
212189f
chore: resolve conflicts from master
emmacunningham Jun 6, 2019
3ac63cd
test(axis_utils): supply additional function argument for tests
emmacunningham Jun 6, 2019
fc15b97
fix(line_annotation): should always align to start in histogram mode
emmacunningham Jun 6, 2019
9a92256
feat(line/area): set default alignment to center
emmacunningham Jun 6, 2019
8bb39b4
feat: align line annotation to xAxis
emmacunningham Jun 7, 2019
3d7921c
docs: add ordinal domain stories
emmacunningham Jun 7, 2019
4fa24c9
test: add totalBarsInCluster argument
emmacunningham Jun 7, 2019
1329305
feat(rect_annotation): snap annotation unless histogram mode
emmacunningham Jun 7, 2019
374f00a
fix(rect_annotation): alignWithTick if bandwidth > 0
emmacunningham Jun 7, 2019
d5b44fd
feat: extend domain if !alignWithTick for histograms
emmacunningham Jun 7, 2019
4edd9e8
test(annotation_utils): remove .only
emmacunningham Jun 7, 2019
c9ae853
test(annotation_utils): fill in missing coverage for dimensions
emmacunningham Jun 7, 2019
1b72aa6
refactor: clean up unused code & imports
emmacunningham Jun 7, 2019
78e1bd0
style: remove comments and whitespace
emmacunningham Jun 7, 2019
db5a963
test: fix test with updated arguments
emmacunningham Jun 7, 2019
eaeb610
chore: resolve merge conflicts with master
emmacunningham Jun 10, 2019
a0c615d
fix(rect_annotation): extend end of domain for histograms
emmacunningham Jun 10, 2019
bc6820f
feat(theme): add histogramPadding to theme
emmacunningham Jun 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ export { timeFormatter, niceTimeFormatter, niceTimeFormatByDay } from './utils/d
export { DataGenerator } from './utils/data_generators/data_generator';
export { DataSeriesColorsValues } from './lib/series/series';
export {
AnnotationDomainType,
AnnotationDomainTypes,
CustomSeriesColorsMap,
HistogramModeAlignment,
HistogramModeAlignments,
LineAnnotationDatum,
LineAnnotationSpec,
RectAnnotationDatum,
Expand Down
51 changes: 35 additions & 16 deletions src/lib/axes/axis_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { XDomain } from '../series/domains/x_domain';
import { YDomain } from '../series/domains/y_domain';
import { AxisSpec, DomainRange, Position } from '../series/specs';
import { LIGHT_THEME } from '../themes/light_theme';
import { getAxisId, getGroupId, GroupId } from '../utils/ids';
import { AxisId, getAxisId, getGroupId, GroupId } from '../utils/ids';
import { ScaleType } from '../utils/scales/scales';
import {
AxisTick,
AxisTicksDimensions,
centerRotationOrigin,
computeAxisGridLinePositions,
computeAxisTicksDimensions,
Expand Down Expand Up @@ -65,8 +67,6 @@ describe('Axis computational utils', () => {
left: 0,
};
const axis1Dims = {
axisScaleType: ScaleType.Linear,
axisScaleDomain: [0, 1],
tickValues: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
tickLabels: ['0', '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1'],
maxLabelBboxWidth: 10,
Expand Down Expand Up @@ -223,7 +223,7 @@ describe('Axis computational utils', () => {

test('should compute available ticks', () => {
const scale = getScaleForAxisSpec(verticalAxisSpec, xDomain, [yDomain], 0, 0, 100, 0);
const axisPositions = getAvailableTicks(verticalAxisSpec, scale!, 0);
const axisPositions = getAvailableTicks(verticalAxisSpec, scale!, 0, false);
const expectedAxisPositions = [
{ label: '0', position: 100, value: 0 },
{ label: '0.1', position: 90, value: 0.1 },
Expand All @@ -238,6 +238,19 @@ describe('Axis computational utils', () => {
{ label: '1', position: 0, value: 1 },
];
expect(axisPositions).toEqual(expectedAxisPositions);

// histogram mode axis ticks should add an additional tick
const xBandDomain: XDomain = {
type: 'xDomain',
scaleType: ScaleType.Linear,
domain: [0, 100],
isBandScale: true,
minInterval: 10,
};
const xScale = getScaleForAxisSpec(horizontalAxisSpec, xBandDomain, [yDomain], 1, 0, 100, 0);
const histogramAxisPositions = getAvailableTicks(horizontalAxisSpec, xScale!, 1, true);
const histogramTickLabels = histogramAxisPositions.map((tick: AxisTick) => tick.label);
expect(histogramTickLabels).toEqual(['0', '10', '20', '30', '40', '50', '60', '70', '80', '90', '100', '110']);
});
test('should compute visible ticks for a vertical axis', () => {
const allTicks = [
Expand Down Expand Up @@ -723,7 +736,7 @@ describe('Axis computational utils', () => {
test('should compute axis ticks positions with title', () => {
const chartRotation = 0;
const showLegend = false;

// validate assumptions for test
expect(verticalAxisSpec.id).toEqual(verticalAxisSpecWTitle.id);

Expand All @@ -743,6 +756,7 @@ describe('Axis computational utils', () => {
xDomain,
[yDomain],
1,
false,
);

let left = 12 + 5 + 10 + 10; // font size + title padding + chart margin left + label width
Expand All @@ -763,6 +777,7 @@ describe('Axis computational utils', () => {
xDomain,
[yDomain],
1,
false,
);

left = 0 + 10 + 10; // no title + chart margin left + label width
Expand Down Expand Up @@ -918,10 +933,10 @@ describe('Axis computational utils', () => {
const showLegend = true;
const leftLegendPosition = Position.Left;

const axisSpecs = new Map();
const axisSpecs = new Map<AxisId, AxisSpec>();
axisSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const axisDims = new Map();
const axisDims = new Map<AxisId, AxisTicksDimensions>();
axisDims.set(getAxisId('not_a_mapped_one'), axis1Dims);

const axisTicksPosition = getAxisTicksPositions(
Expand All @@ -934,6 +949,7 @@ describe('Axis computational utils', () => {
xDomain,
[yDomain],
1,
false,
leftLegendPosition,
);
expect(axisTicksPosition.axisPositions.size).toBe(0);
Expand All @@ -948,10 +964,10 @@ describe('Axis computational utils', () => {
const leftLegendPosition = Position.Left;
const topLegendPosition = Position.Top;

const axisSpecs = new Map();
const axisSpecs = new Map<AxisId, AxisSpec>();
axisSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const axisDims = new Map();
const axisDims = new Map<AxisId, AxisTicksDimensions>();
axisDims.set(verticalAxisSpec.id, axis1Dims);

const axisTicksPosition = getAxisTicksPositions(
Expand All @@ -964,6 +980,7 @@ describe('Axis computational utils', () => {
xDomain,
[yDomain],
1,
false,
leftLegendPosition,
);

Expand Down Expand Up @@ -995,6 +1012,7 @@ describe('Axis computational utils', () => {
xDomain,
[yDomain],
1,
false,
topLegendPosition,
);

Expand All @@ -1010,7 +1028,7 @@ describe('Axis computational utils', () => {
expect(verticalAxisWithTopLegendPosition).toEqual(expectedPositionWithTopLegend);

const ungroupedAxisSpec = { ...verticalAxisSpec, groupId: getGroupId('foo') };
const invalidSpecs = new Map();
const invalidSpecs = new Map<AxisId, AxisSpec>();
invalidSpecs.set(verticalAxisSpec.id, ungroupedAxisSpec);
const computeScalelessSpec = () => {
getAxisTicksPositions(
Expand All @@ -1023,6 +1041,7 @@ describe('Axis computational utils', () => {
xDomain,
[yDomain],
1,
false,
leftLegendPosition,
);
};
Expand Down Expand Up @@ -1073,7 +1092,7 @@ describe('Axis computational utils', () => {

verticalAxisSpec.domain = domainRange1;

const axesSpecs = new Map();
const axesSpecs = new Map<AxisId, AxisSpec>();
axesSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

// Base case
Expand Down Expand Up @@ -1129,7 +1148,7 @@ describe('Axis computational utils', () => {

verticalAxisSpec.domain = domainRange1;

const axesSpecs = new Map();
const axesSpecs = new Map<AxisId, AxisSpec>();
axesSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const axis2 = { ...verticalAxisSpec, id: getAxisId('axis2') };
Expand Down Expand Up @@ -1157,7 +1176,7 @@ describe('Axis computational utils', () => {

verticalAxisSpec.domain = domainRange1;

const axesSpecs = new Map();
const axesSpecs = new Map<AxisId, AxisSpec>();
axesSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const axis2 = { ...verticalAxisSpec, id: getAxisId('axis2') };
Expand Down Expand Up @@ -1188,7 +1207,7 @@ describe('Axis computational utils', () => {

verticalAxisSpec.domain = domainRange1;

const axesSpecs = new Map();
const axesSpecs = new Map<AxisId, AxisSpec>();
axesSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const axis2 = { ...verticalAxisSpec, id: getAxisId('axis2') };
Expand Down Expand Up @@ -1224,7 +1243,7 @@ describe('Axis computational utils', () => {

verticalAxisSpec.domain = domainRange1;

const axesSpecs = new Map();
const axesSpecs = new Map<AxisId, AxisSpec>();
axesSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const axis2 = { ...verticalAxisSpec, id: getAxisId('axis2') };
Expand Down Expand Up @@ -1252,7 +1271,7 @@ describe('Axis computational utils', () => {

verticalAxisSpec.domain = domainRange1;

const axesSpecs = new Map();
const axesSpecs = new Map<AxisId, AxisSpec>();
axesSpecs.set(verticalAxisSpec.id, verticalAxisSpec);

const attemptToMerge = () => {
Expand Down
30 changes: 20 additions & 10 deletions src/lib/axes/axis_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import {
} from '../series/specs';
import { AxisConfig, Theme } from '../themes/theme';
import { Dimensions, Margins } from '../utils/dimensions';
import { Domain } from '../utils/domain';
import { AxisId, GroupId } from '../utils/ids';
import { Scale, ScaleType } from '../utils/scales/scales';
import { Scale } from '../utils/scales/scales';
import { BBox, BBoxCalculator } from './bbox_calculator';

export type AxisLinePosition = [number, number, number, number];
Expand All @@ -27,8 +26,6 @@ export interface AxisTick {
}

export interface AxisTicksDimensions {
axisScaleType: ScaleType;
axisScaleDomain: Domain;
tickValues: string[] | number[];
tickLabels: string[];
maxLabelBboxWidth: number;
Expand Down Expand Up @@ -90,8 +87,6 @@ export function computeAxisTicksDimensions(
);

return {
axisScaleDomain: xDomain.domain,
axisScaleType: xDomain.scaleType,
...dimensions,
};
}
Expand Down Expand Up @@ -291,7 +286,7 @@ export function getTickLabelProps(
}

return {
x: tickPosition - maxLabelBboxWidth / 2,
x: (tickPosition - maxLabelBboxWidth / 2),
y: isAxisTop ? 0 : tickSize + tickPadding,
align,
verticalAlign,
Expand Down Expand Up @@ -393,10 +388,24 @@ export function getLeftAxisMinMaxRange(chartRotation: Rotation, height: number)
}
}

export function getAvailableTicks(axisSpec: AxisSpec, scale: Scale, totalBarsInCluster: number) {
export function getAvailableTicks(
axisSpec: AxisSpec,
scale: Scale,
totalBarsInCluster: number,
enableHistogramMode: boolean,
): AxisTick[] {
const ticks = scale.ticks();

if (enableHistogramMode && scale.bandwidth > 0) {
const finalTick = ticks[ticks.length - 1] + scale.minInterval;
ticks.push(finalTick);
}

const shift = totalBarsInCluster > 0 ? totalBarsInCluster : 1;
const offset = (scale.bandwidth * shift) / 2;

const band = scale.bandwidth / (1 - scale.barsPadding);
const halfPadding = (band - scale.bandwidth) / 2;
const offset = enableHistogramMode ? -halfPadding : (scale.bandwidth * shift) / 2;
return ticks.map((tick) => {
return {
value: tick,
Expand Down Expand Up @@ -507,6 +516,7 @@ export function getAxisTicksPositions(
xDomain: XDomain,
yDomain: YDomain[],
totalGroupsCount: number,
enableHistogramMode: boolean,
legendPosition?: Position,
barsPadding?: number,
) {
Expand Down Expand Up @@ -567,7 +577,7 @@ export function getAxisTicksPositions(
throw new Error(`Cannot compute scale for axis spec ${axisSpec.id}`);
}

const allTicks = getAvailableTicks(axisSpec, scale, totalGroupsCount);
const allTicks = getAvailableTicks(axisSpec, scale, totalGroupsCount, enableHistogramMode);
const visibleTicks = getVisibleTicks(allTicks, axisSpec, axisDim);

if (axisSpec.showGridLines) {
Expand Down
Loading