forked from apache-superset/superset-ui-plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(encodable): implement axis functions for ChannelEncoder (apache-…
…superset#247) * feat: add axis encoder * test: add unit test * fix: params * refactor: rename * fix: address comments * fix: update import * fix: error * fix: lint
- Loading branch information
Showing
5 changed files
with
285 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
packages/superset-ui-encodable/src/encoders/ChannelEncoderAxis.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import ChannelEncoder from './ChannelEncoder'; | ||
import createFormatterFromFieldTypeAndFormat from '../parsers/format/createFormatterFromFieldTypeAndFormat'; | ||
import { CompleteAxisConfig } from '../fillers/completeAxisConfig'; | ||
import { ChannelDef } from '../types/ChannelDef'; | ||
import { Value, isDateTime } from '../types/VegaLite'; | ||
import { CompleteFieldDef } from '../fillers/completeChannelDef'; | ||
import { ChannelInput } from '../types/Channel'; | ||
import { HasToString } from '../types/Base'; | ||
import parseDateTime from '../parsers/parseDateTime'; | ||
import inferElementTypeFromUnionOfArrayTypes from '../utils/inferElementTypeFromUnionOfArrayTypes'; | ||
|
||
export default class ChannelEncoderAxis< | ||
Def extends ChannelDef<Output>, | ||
Output extends Value = Value | ||
> { | ||
readonly channelEncoder: ChannelEncoder<Def, Output>; | ||
readonly config: Exclude<CompleteAxisConfig, false>; | ||
readonly formatValue: (value: ChannelInput | HasToString) => string; | ||
|
||
constructor(channelEncoder: ChannelEncoder<Def, Output>) { | ||
this.channelEncoder = channelEncoder; | ||
this.config = channelEncoder.definition.axis as Exclude<CompleteAxisConfig, false>; | ||
this.formatValue = createFormatterFromFieldTypeAndFormat( | ||
(channelEncoder.definition as CompleteFieldDef<Output>).type, | ||
this.config.format || '', | ||
); | ||
} | ||
|
||
getTitle() { | ||
return this.config.title; | ||
} | ||
|
||
hasTitle() { | ||
const { title } = this.config; | ||
|
||
return title !== null && typeof title !== 'undefined' && title !== ''; | ||
} | ||
|
||
getTickLabels() { | ||
const { tickCount, values } = this.config; | ||
|
||
if (typeof values !== 'undefined') { | ||
return inferElementTypeFromUnionOfArrayTypes(values).map(v => | ||
this.formatValue(isDateTime(v) ? parseDateTime(v) : v), | ||
); | ||
} | ||
|
||
const { scale } = this.channelEncoder; | ||
if (scale && 'domain' in scale) { | ||
return ('ticks' in scale ? scale.ticks(tickCount) : scale.domain()).map(this.formatValue); | ||
} | ||
|
||
return []; | ||
} | ||
} |
3 changes: 1 addition & 2 deletions
3
packages/superset-ui-encodable/src/parsers/parseDateTimeIfPossible.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
// Types imported from vega-lite | ||
|
||
export { ValueDef, Value } from 'vega-lite/build/src/channeldef'; | ||
export { DateTime } from 'vega-lite/build/src/datetime'; | ||
export { isDateTime, DateTime } from 'vega-lite/build/src/datetime'; | ||
export { SchemeParams, ScaleType, Scale, NiceTime } from 'vega-lite/build/src/scale'; | ||
export { Axis } from 'vega-lite/build/src/axis'; | ||
export { Type } from 'vega-lite/build/src/type'; |
216 changes: 216 additions & 0 deletions
216
packages/superset-ui-encodable/test/encoders/ChannelEncoderAxis.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
import { ChannelEncoder } from '../../src'; | ||
|
||
describe('ChannelEncoderAxis', () => { | ||
describe('new ChannelEncoderAxis(channelEncoder)', () => { | ||
it('completes the definition and creates an encoder for it', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
}, | ||
}); | ||
expect(encoder.axis).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe('.formatValue()', () => { | ||
it('formats value', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
axis: { | ||
format: '.2f', | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.formatValue(200)).toEqual('200.00'); | ||
}); | ||
it('fallsback to field formatter', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
format: '.3f', | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.formatValue(200)).toEqual('200.000'); | ||
}); | ||
it('fallsback to default formatter', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.formatValue(200)).toEqual('200'); | ||
}); | ||
}); | ||
|
||
describe('.getTitle()', () => { | ||
it('returns the axis title', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
title: 'Speed', | ||
axis: { | ||
title: 'Speed!', | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTitle()).toEqual('Speed!'); | ||
}); | ||
it('returns the field title when not specified', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
title: 'Speed', | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTitle()).toEqual('Speed'); | ||
}); | ||
it('returns the field name when no title is specified', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTitle()).toEqual('speed'); | ||
}); | ||
}); | ||
|
||
describe('.hasTitle()', () => { | ||
it('returns true if the title is not empty', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
title: 'Speed', | ||
axis: { | ||
title: 'Speed!', | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.hasTitle()).toBeTruthy(); | ||
}); | ||
it('returns false otherwise', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
title: 'Speed', | ||
axis: { | ||
title: '', | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.hasTitle()).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('.getTickLabels()', () => { | ||
it('handles hard-coded tick values', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
axis: { | ||
values: [1, 2, 3], | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTickLabels()).toEqual(['1', '2', '3']); | ||
}); | ||
it('handles hard-coded DateTime object', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'temporal', | ||
field: 'time', | ||
axis: { | ||
format: '%Y', | ||
values: [{ year: 2018 }, { year: 2019 }], | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTickLabels()).toEqual(['2018', '2019']); | ||
}); | ||
describe('uses information from scale', () => { | ||
it('uses ticks when available', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
scale: { | ||
type: 'linear', | ||
domain: [0, 100], | ||
}, | ||
axis: { | ||
tickCount: 5, | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTickLabels()).toEqual([ | ||
'0', | ||
'20', | ||
'40', | ||
'60', | ||
'80', | ||
'100', | ||
]); | ||
}); | ||
it('or uses domain', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'nominal', | ||
field: 'brand', | ||
scale: { | ||
domain: ['honda', 'toyota'], | ||
}, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTickLabels()).toEqual(['honda', 'toyota']); | ||
}); | ||
}); | ||
it('returns empty array otherwise', () => { | ||
const encoder = new ChannelEncoder({ | ||
name: 'x', | ||
channelType: 'X', | ||
definition: { | ||
type: 'quantitative', | ||
field: 'speed', | ||
scale: false, | ||
}, | ||
}); | ||
expect(encoder.axis && encoder.axis.getTickLabels()).toEqual([]); | ||
}); | ||
}); | ||
}); |