Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8987cae846 |
15
README.md
15
README.md
@@ -115,7 +115,7 @@ const data = ref<WaveformData>({
|
|||||||
| `yDomain` | `[number, number]` | 未设置 | 所有波形的固定 Y 轴范围 |
|
| `yDomain` | `[number, number]` | 未设置 | 所有波形的固定 Y 轴范围 |
|
||||||
| `yDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置固定范围 |
|
| `yDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置固定范围 |
|
||||||
| `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 |
|
| `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 |
|
||||||
| `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线显隐与 X 轴 label 格式化 |
|
| `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线、Y 轴分割数与 X 轴 label 格式化 |
|
||||||
| `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 |
|
| `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 |
|
||||||
| `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 |
|
| `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 |
|
||||||
| `frameNumber` | `string \| number` | 未设置 | 图框水印内容 |
|
| `frameNumber` | `string \| number` | 未设置 | 图框水印内容 |
|
||||||
@@ -208,7 +208,8 @@ import 'waveform-analysis/style.css'
|
|||||||
|
|
||||||
范围优先级为 `trackId` 配置、`seriesId` 配置、全局 `yDomain`、数据自动范围。上下限必须
|
范围优先级为 `trackId` 配置、`seriesId` 配置、全局 `yDomain`、数据自动范围。上下限必须
|
||||||
是两个有限且不相等的数字;倒序范围会自动调整为升序,无效配置会回退到下一优先级。
|
是两个有限且不相等的数字;倒序范围会自动调整为升序,无效配置会回退到下一优先级。
|
||||||
固定范围会精确作为坐标域使用,不会经过 D3 的 `nice()` 扩展。
|
固定范围作为 Y 轴刻度算法的初始范围;轴会按漂亮步长经过 D3 的 `nice()` 扩展,确保主刻度
|
||||||
|
等间距。原始数据和传入的范围值不会被修改。
|
||||||
|
|
||||||
单值轴叠加模式会合并同一根轴上所有可见系列的有效范围;多值轴模式按系列分别使用配置,
|
单值轴叠加模式会合并同一根轴上所有可见系列的有效范围;多值轴模式按系列分别使用配置,
|
||||||
超过四根轴后复用第 4 根轴的系列会取范围并集。隐藏系列不参与公共范围合并。
|
超过四根轴后复用第 4 根轴的系列会取范围并集。隐藏系列不参与公共范围合并。
|
||||||
@@ -216,6 +217,16 @@ import 'waveform-analysis/style.css'
|
|||||||
固定范围存在时,对应图框的 Y 轴不会被平移或视口重置覆盖;X 轴缩放、平移和重置保持原有
|
固定范围存在时,对应图框的 Y 轴不会被平移或视口重置覆盖;X 轴缩放、平移和重置保持原有
|
||||||
行为。运行时更新或移除 `yDomain` / `yDomains` 会立即重新布局,移除后恢复自动范围。
|
行为。运行时更新或移除 `yDomain` / `yDomains` 会立即重新布局,移除后恢复自动范围。
|
||||||
|
|
||||||
|
### Y 轴自动等分
|
||||||
|
|
||||||
|
Y 轴默认显示 5 个主刻度(包含上下端点),并使用类似 ECharts 的 `1 / 2 / 5 × 10ⁿ` 漂亮
|
||||||
|
步长,保证主刻度之间数值等间距。可以通过 `axes.y.splitNumber` 指定主刻度总数;每条
|
||||||
|
Y 轴会独立计算:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<WaveformChart :data="chartData" :axes="{ y: { splitNumber: 5 } }" />
|
||||||
|
```
|
||||||
|
|
||||||
### 缩放后按可视区间加载数据
|
### 缩放后按可视区间加载数据
|
||||||
|
|
||||||
组件支持 Plotly 风格的矩形框选缩放:在 zoom 模式下按住鼠标左键拖拽,松开后同时缩放
|
组件支持 Plotly 风格的矩形框选缩放:在 zoom 模式下按住鼠标左键拖拽,松开后同时缩放
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "waveform-analysis",
|
"name": "waveform-analysis",
|
||||||
"version": "0.1.40",
|
"version": "0.1.41",
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
"types": "./dist/types/index.d.ts",
|
"types": "./dist/types/index.d.ts",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function track(seriesList: DisplaySeries[]): DisplayTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('fixed Y-domain layout', () => {
|
describe('fixed Y-domain layout', () => {
|
||||||
it('uses an exact global fixed domain without applying nice bounds', () => {
|
it('expands a global fixed domain to nice equal intervals', () => {
|
||||||
const sourceTrack = track([series('a', 0, 100)])
|
const sourceTrack = track([series('a', 0, 100)])
|
||||||
const result = buildTrackLayouts({
|
const result = buildTrackLayouts({
|
||||||
cells: [
|
cells: [
|
||||||
@@ -66,10 +66,80 @@ describe('fixed Y-domain layout', () => {
|
|||||||
showCompactEmptyTracks: false,
|
showCompactEmptyTracks: false,
|
||||||
})[0]
|
})[0]
|
||||||
|
|
||||||
expect(result?.yScale.domain()).toEqual([3, 97])
|
expect(result?.yScale.domain()).toEqual([0, 100])
|
||||||
expect(result?.yAxes[0]?.tickValues).toContain(3)
|
expect(result?.yAxes[0]?.tickValues).toContain(0)
|
||||||
expect(result?.yAxes[0]?.tickValues).toContain(97)
|
expect(result?.yAxes[0]?.tickValues).toContain(100)
|
||||||
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([3, 97])
|
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([0, 100])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses the configured split number for fixed domains', () => {
|
||||||
|
const result = buildTrackLayouts({
|
||||||
|
cells: [
|
||||||
|
{
|
||||||
|
slotIndex: 0,
|
||||||
|
row: 0,
|
||||||
|
column: 0,
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 120,
|
||||||
|
height: 100,
|
||||||
|
plotHeight: 100,
|
||||||
|
cellHeight: 130,
|
||||||
|
xAxisBand: 30,
|
||||||
|
series: track([series('a', 0, 100)]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
grid: { rowCount: 1, columnCount: 1, showPagination: false, trackLines: {} },
|
||||||
|
displayMode: 'independent',
|
||||||
|
overlayMode: 'single-axis',
|
||||||
|
independentTransforms: [zoomIdentity],
|
||||||
|
sharedZoomDomain: [0, 1],
|
||||||
|
fixedYDomain: [3, 97],
|
||||||
|
yAxisSplitNumber: 5,
|
||||||
|
timeUnit: 'ms',
|
||||||
|
rendering: DEFAULT_WAVEFORM_RENDERING_OPTIONS,
|
||||||
|
hideSecondaryLabels: false,
|
||||||
|
yAxisLabelX: -50,
|
||||||
|
showCompactEmptyTracks: false,
|
||||||
|
})[0]
|
||||||
|
|
||||||
|
expect(result?.yAxes[0]?.majorTicks).toEqual([0, 25, 50, 75, 100])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('defaults to five ticks and supports a two-tick axis', () => {
|
||||||
|
const build = (splitNumber?: number) =>
|
||||||
|
buildTrackLayouts({
|
||||||
|
cells: [
|
||||||
|
{
|
||||||
|
slotIndex: 0,
|
||||||
|
row: 0,
|
||||||
|
column: 0,
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 120,
|
||||||
|
height: 100,
|
||||||
|
plotHeight: 100,
|
||||||
|
cellHeight: 130,
|
||||||
|
xAxisBand: 30,
|
||||||
|
series: track([series('a', 3, 97)]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
grid: { rowCount: 1, columnCount: 1, showPagination: false, trackLines: {} },
|
||||||
|
displayMode: 'independent',
|
||||||
|
overlayMode: 'single-axis',
|
||||||
|
independentTransforms: [zoomIdentity],
|
||||||
|
sharedZoomDomain: [0, 1],
|
||||||
|
fixedYDomain: [3, 97],
|
||||||
|
yAxisSplitNumber: splitNumber,
|
||||||
|
timeUnit: 'ms',
|
||||||
|
rendering: DEFAULT_WAVEFORM_RENDERING_OPTIONS,
|
||||||
|
hideSecondaryLabels: false,
|
||||||
|
yAxisLabelX: -50,
|
||||||
|
showCompactEmptyTracks: false,
|
||||||
|
})[0]?.yAxes[0]?.majorTicks
|
||||||
|
|
||||||
|
expect(build()).toHaveLength(5)
|
||||||
|
expect(build(2)).toEqual([0, 100])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('resolves track, series, and global fixed-domain precedence', () => {
|
it('resolves track, series, and global fixed-domain precedence', () => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
findClosestTrackAtPointer,
|
findClosestTrackAtPointer,
|
||||||
MAX_MULTI_Y_AXIS_COUNT,
|
MAX_MULTI_Y_AXIS_COUNT,
|
||||||
measureYAxisGroupClearance,
|
measureYAxisGroupClearance,
|
||||||
|
resolveYAxisTickCount,
|
||||||
} from './layout'
|
} from './layout'
|
||||||
|
|
||||||
function series(id: string, minimum: number, maximum: number): DisplaySeries {
|
function series(id: string, minimum: number, maximum: number): DisplaySeries {
|
||||||
@@ -119,8 +120,15 @@ describe('multi-value Y-axis grouping', () => {
|
|||||||
showCompactEmptyTracks: false,
|
showCompactEmptyTracks: false,
|
||||||
})[0]
|
})[0]
|
||||||
|
|
||||||
expect(result?.yScale.domain()).toEqual([25, 75])
|
expect(result?.yScale.domain()).toEqual([20, 80])
|
||||||
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([25, 75])
|
expect(result?.seriesPaths[0]?.yScale.domain()).toEqual([20, 80])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('normalizes configured Y-axis split counts', () => {
|
||||||
|
expect(resolveYAxisTickCount(100, 0)).toBe(2)
|
||||||
|
expect(resolveYAxisTickCount(100, 4.8)).toBe(4)
|
||||||
|
expect(resolveYAxisTickCount(100, Number.NaN)).toBe(5)
|
||||||
|
expect(resolveYAxisTickCount(220)).toBe(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('keeps every overlaid series on one axis in single-axis mode', () => {
|
it('keeps every overlaid series on one axis in single-axis mode', () => {
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ const Y_AXIS_OUTER_PADDING = 4
|
|||||||
const Y_AXIS_LABEL_GAP = 6
|
const Y_AXIS_LABEL_GAP = 6
|
||||||
const Y_AXIS_LABEL_BAND_WIDTH = 24
|
const Y_AXIS_LABEL_BAND_WIDTH = 24
|
||||||
|
|
||||||
|
export function resolveYAxisTickCount(_plotHeight: number, splitNumber?: number): number {
|
||||||
|
if (typeof splitNumber === 'number' && Number.isFinite(splitNumber)) {
|
||||||
|
return Math.max(2, Math.floor(splitNumber))
|
||||||
|
}
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
|
||||||
export interface YAxisSeriesGroup {
|
export interface YAxisSeriesGroup {
|
||||||
index: number
|
index: number
|
||||||
side: 'left' | 'right'
|
side: 'left' | 'right'
|
||||||
@@ -104,11 +111,16 @@ export function axisTextMetrics(
|
|||||||
nice = true,
|
nice = true,
|
||||||
tickValues?: number[],
|
tickValues?: number[],
|
||||||
unit?: string,
|
unit?: string,
|
||||||
|
tickCount = 10,
|
||||||
): { tickTextWidth: number } {
|
): { tickTextWidth: number } {
|
||||||
|
const effectiveTickCount =
|
||||||
|
typeof tickCount === 'number' && Number.isFinite(tickCount)
|
||||||
|
? Math.max(2, Math.floor(tickCount))
|
||||||
|
: 10
|
||||||
const scale = scaleLinear(domain, [1, 0])
|
const scale = scaleLinear(domain, [1, 0])
|
||||||
if (nice) scale.nice()
|
if (nice) scale.nice(effectiveTickCount)
|
||||||
const [axisMin, axisMax] = scale.domain()
|
const [axisMin, axisMax] = scale.domain()
|
||||||
const values = tickValues ?? Array.from(new Set([axisMin, ...scale.ticks(10), axisMax]))
|
const values = tickValues ?? scale.ticks(effectiveTickCount)
|
||||||
const topTickValue = Math.max(...values)
|
const topTickValue = Math.max(...values)
|
||||||
const maximumTickCharacters = Math.max(
|
const maximumTickCharacters = Math.max(
|
||||||
1,
|
1,
|
||||||
@@ -121,9 +133,9 @@ export function axisTextMetrics(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function measureYAxisGroupClearance(group: YAxisSeriesGroup): number {
|
export function measureYAxisGroupClearance(group: YAxisSeriesGroup, tickCount?: number): number {
|
||||||
return (
|
return (
|
||||||
axisTextMetrics(group.domain, !group.fixed, undefined, group.seriesList[0]?.unit)
|
axisTextMetrics(group.domain, true, undefined, group.seriesList[0]?.unit, tickCount)
|
||||||
.tickTextWidth +
|
.tickTextWidth +
|
||||||
Y_AXIS_TICK_PADDING +
|
Y_AXIS_TICK_PADDING +
|
||||||
Y_AXIS_LABEL_GAP +
|
Y_AXIS_LABEL_GAP +
|
||||||
@@ -132,9 +144,9 @@ export function measureYAxisGroupClearance(group: YAxisSeriesGroup): number {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function measureYAxisGroupTickClearance(group: YAxisSeriesGroup): number {
|
function measureYAxisGroupTickClearance(group: YAxisSeriesGroup, tickCount?: number): number {
|
||||||
return (
|
return (
|
||||||
axisTextMetrics(group.domain, !group.fixed, undefined, group.seriesList[0]?.unit)
|
axisTextMetrics(group.domain, true, undefined, group.seriesList[0]?.unit, tickCount)
|
||||||
.tickTextWidth +
|
.tickTextWidth +
|
||||||
Y_AXIS_TICK_PADDING +
|
Y_AXIS_TICK_PADDING +
|
||||||
Y_AXIS_OUTER_PADDING
|
Y_AXIS_OUTER_PADDING
|
||||||
@@ -146,13 +158,14 @@ export function measureTrackYAxisClearance(
|
|||||||
overlayMode: WaveformOverlayMode,
|
overlayMode: WaveformOverlayMode,
|
||||||
yDomain?: WaveformYDomain,
|
yDomain?: WaveformYDomain,
|
||||||
yDomains?: Record<string, WaveformYDomain>,
|
yDomains?: Record<string, WaveformYDomain>,
|
||||||
|
tickCount?: number,
|
||||||
): { left: number; right: number } {
|
): { left: number; right: number } {
|
||||||
return resolveYAxisSeriesGroups(track, overlayMode, yDomain, yDomains).reduce(
|
return resolveYAxisSeriesGroups(track, overlayMode, yDomain, yDomains).reduce(
|
||||||
(clearance, group) => {
|
(clearance, group) => {
|
||||||
clearance[group.side] +=
|
clearance[group.side] +=
|
||||||
overlayMode === 'multi-axis' || track.visibleSeries.length === 1
|
overlayMode === 'multi-axis' || track.visibleSeries.length === 1
|
||||||
? measureYAxisGroupClearance(group)
|
? measureYAxisGroupClearance(group, tickCount)
|
||||||
: measureYAxisGroupTickClearance(group)
|
: measureYAxisGroupTickClearance(group, tickCount)
|
||||||
return clearance
|
return clearance
|
||||||
},
|
},
|
||||||
{ left: 0, right: 0 },
|
{ left: 0, right: 0 },
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
type GridCellGeometry,
|
type GridCellGeometry,
|
||||||
type NormalizedWaveformGridOptions,
|
type NormalizedWaveformGridOptions,
|
||||||
} from './grid'
|
} from './grid'
|
||||||
import { axisTextMetrics, resolveYAxisSeriesGroups } from './layout'
|
import { axisTextMetrics, resolveYAxisSeriesGroups, resolveYAxisTickCount } from './layout'
|
||||||
import type { DisplaySeries, DisplayTrack, TrackLayout, WaveformYAxisLayout } from './types'
|
import type { DisplaySeries, DisplayTrack, TrackLayout, WaveformYAxisLayout } from './types'
|
||||||
import { applyXDomainStrategy } from './xDomain'
|
import { applyXDomainStrategy } from './xDomain'
|
||||||
import {
|
import {
|
||||||
@@ -54,6 +54,7 @@ export interface BuildTrackLayoutsOptions {
|
|||||||
yDomains?: Record<string, [number, number]>
|
yDomains?: Record<string, [number, number]>
|
||||||
timeUnit: 's' | 'ms'
|
timeUnit: 's' | 'ms'
|
||||||
xAxisLabelFormatter?: WaveformXAxisLabelFormatter
|
xAxisLabelFormatter?: WaveformXAxisLabelFormatter
|
||||||
|
yAxisSplitNumber?: number
|
||||||
rendering: ResolvedWaveformRenderingOptions
|
rendering: ResolvedWaveformRenderingOptions
|
||||||
hideSecondaryLabels: boolean
|
hideSecondaryLabels: boolean
|
||||||
yAxisLabelX: number
|
yAxisLabelX: number
|
||||||
@@ -124,22 +125,26 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
|||||||
)
|
)
|
||||||
const sideOffsets = { left: 0, right: 0 }
|
const sideOffsets = { left: 0, right: 0 }
|
||||||
const yAxes: WaveformYAxisLayout[] = yAxisGroups.map((group) => {
|
const yAxes: WaveformYAxisLayout[] = yAxisGroups.map((group) => {
|
||||||
|
const tickCount = resolveYAxisTickCount(cell.plotHeight, options.yAxisSplitNumber)
|
||||||
|
const niceCount = Math.max(1, tickCount - 1)
|
||||||
const scale = scaleLinear(group.domain, [cell.plotHeight, 0])
|
const scale = scaleLinear(group.domain, [cell.plotHeight, 0])
|
||||||
if (!group.fixed) scale.nice()
|
scale.nice(niceCount)
|
||||||
const majorTicks = scale.ticks(Math.max(2, Math.floor(cell.plotHeight / 55)))
|
|
||||||
const [axisStart, axisEnd] = scale.domain()
|
const [axisStart, axisEnd] = scale.domain()
|
||||||
|
const majorTicks = Array.from(
|
||||||
|
{ length: tickCount },
|
||||||
|
(_, index) => axisStart + ((axisEnd - axisStart) * index) / (tickCount - 1),
|
||||||
|
)
|
||||||
const showAxisEnd = options.displayMode !== 'compact' || cell.row === 0
|
const showAxisEnd = options.displayMode !== 'compact' || cell.row === 0
|
||||||
const visibleMajorTicks = showAxisEnd
|
const visibleMajorTicks = showAxisEnd
|
||||||
? majorTicks
|
? majorTicks
|
||||||
: majorTicks.filter((tick) => tick !== axisEnd)
|
: majorTicks.filter((tick) => tick !== axisEnd)
|
||||||
const tickValues = Array.from(
|
const tickValues = visibleMajorTicks
|
||||||
new Set([axisStart, ...visibleMajorTicks, ...(showAxisEnd ? [axisEnd] : [])]),
|
|
||||||
)
|
|
||||||
const { tickTextWidth } = axisTextMetrics(
|
const { tickTextWidth } = axisTextMetrics(
|
||||||
group.domain,
|
scale.domain() as [number, number],
|
||||||
!group.fixed,
|
false,
|
||||||
tickValues,
|
tickValues,
|
||||||
group.seriesList[0]?.unit,
|
group.seriesList[0]?.unit,
|
||||||
|
tickCount,
|
||||||
)
|
)
|
||||||
const clearance =
|
const clearance =
|
||||||
tickTextWidth +
|
tickTextWidth +
|
||||||
|
|||||||
@@ -114,8 +114,13 @@ export function useWaveformLayout(context: LayoutContext) {
|
|||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(group) =>
|
(group) =>
|
||||||
axisTextMetrics(group.domain, !group.fixed, undefined, group.seriesList[0]?.unit)
|
axisTextMetrics(
|
||||||
.tickTextWidth,
|
group.domain,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
group.seriesList[0]?.unit,
|
||||||
|
props.axes?.y?.splitNumber,
|
||||||
|
).tickTextWidth,
|
||||||
)
|
)
|
||||||
const tickTextWidth = Math.max(Y_AXIS_CHARACTER_WIDTH, ...axisText)
|
const tickTextWidth = Math.max(Y_AXIS_CHARACTER_WIDTH, ...axisText)
|
||||||
const tickClearance = tickTextWidth + Y_AXIS_TICK_PADDING + Y_AXIS_OUTER_PADDING
|
const tickClearance = tickTextWidth + Y_AXIS_TICK_PADDING + Y_AXIS_OUTER_PADDING
|
||||||
@@ -161,6 +166,7 @@ export function useWaveformLayout(context: LayoutContext) {
|
|||||||
props.overlayMode,
|
props.overlayMode,
|
||||||
props.yDomain,
|
props.yDomain,
|
||||||
props.yDomains,
|
props.yDomains,
|
||||||
|
props.axes?.y?.splitNumber,
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
left: Math.max(maximum.left, clearance.left),
|
left: Math.max(maximum.left, clearance.left),
|
||||||
@@ -302,6 +308,7 @@ export function useWaveformLayout(context: LayoutContext) {
|
|||||||
: sharedYDomains.value,
|
: sharedYDomains.value,
|
||||||
timeUnit: props.timeUnit,
|
timeUnit: props.timeUnit,
|
||||||
xAxisLabelFormatter: props.axes?.x?.labelFormatter,
|
xAxisLabelFormatter: props.axes?.x?.labelFormatter,
|
||||||
|
yAxisSplitNumber: props.axes?.y?.splitNumber,
|
||||||
rendering: renderingOptions.value,
|
rendering: renderingOptions.value,
|
||||||
hideSecondaryLabels: isCleanView.value || yAxisLayout.value.hideSecondaryLabels,
|
hideSecondaryLabels: isCleanView.value || yAxisLayout.value.hideSecondaryLabels,
|
||||||
yAxisLabelX: yAxisMetrics.value.labelCenterX,
|
yAxisLabelX: yAxisMetrics.value.labelCenterX,
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ describe('WaveformChart', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(endTicks[0]).toHaveLength(1)
|
expect(endTicks[0]).toHaveLength(1)
|
||||||
expect(Number(endTicks[0][0].text())).toBe(0.9)
|
expect(Number(endTicks[0][0].text())).toBe(1)
|
||||||
expect(endTicks[1]).toHaveLength(1)
|
expect(endTicks[1]).toHaveLength(1)
|
||||||
expect(Number(endTicks[1][0].text())).toBe(1.9)
|
expect(Number(endTicks[1][0].text())).toBe(2)
|
||||||
expect(endTicks[2]).toHaveLength(0)
|
expect(endTicks[2]).toHaveLength(0)
|
||||||
expect(endTicks[3]).toHaveLength(0)
|
expect(endTicks[3]).toHaveLength(0)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ describe('WaveformChart', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(startTicks).toHaveLength(1)
|
expect(startTicks).toHaveLength(1)
|
||||||
expect(Number(startTicks[0].text())).toBe(0.1)
|
expect(Number(startTicks[0].text())).toBe(0)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ describe('WaveformChart', () => {
|
|||||||
{ displayMode },
|
{ displayMode },
|
||||||
)
|
)
|
||||||
|
|
||||||
const expectedEndValues = [0.9, 4.9]
|
const expectedEndValues = [1, 5]
|
||||||
wrapper.findAll('.waveform-chart__track').forEach((track, index) => {
|
wrapper.findAll('.waveform-chart__track').forEach((track, index) => {
|
||||||
const endTicks = track.findAll('.waveform-chart__axis--y .tick').filter((tick) => {
|
const endTicks = track.findAll('.waveform-chart__axis--y .tick').filter((tick) => {
|
||||||
const match = tick.attributes('transform')?.match(/translate\(0,\s*([\d.]+)\)/)
|
const match = tick.attributes('transform')?.match(/translate\(0,\s*([\d.]+)\)/)
|
||||||
|
|||||||
@@ -6,6 +6,26 @@ import { resizeObservers } from '../../test/setup'
|
|||||||
import { gridSeries, mountSizedChart } from '../../test/waveformChart'
|
import { gridSeries, mountSizedChart } from '../../test/waveformChart'
|
||||||
|
|
||||||
describe('WaveformChart', () => {
|
describe('WaveformChart', () => {
|
||||||
|
it('applies the configured Y-axis split number', async () => {
|
||||||
|
const wrapper = await mountSizedChart(
|
||||||
|
{
|
||||||
|
kind: 'points',
|
||||||
|
points: [
|
||||||
|
{ x: 0, y: 3 },
|
||||||
|
{ x: 1, y: 97 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ axes: { y: { splitNumber: 5 } } },
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.get('.waveform-chart__axis--y')
|
||||||
|
.findAll('.tick text')
|
||||||
|
.map((tick) => tick.text()),
|
||||||
|
).toEqual(['0', '25', '50', '75', '100'])
|
||||||
|
})
|
||||||
|
|
||||||
it('expands the Y-axis label gutter for signed values and long exponents', async () => {
|
it('expands the Y-axis label gutter for signed values and long exponents', async () => {
|
||||||
const wrapper = await mountSizedChart(
|
const wrapper = await mountSizedChart(
|
||||||
{
|
{
|
||||||
@@ -296,7 +316,7 @@ describe('WaveformChart', () => {
|
|||||||
expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe('1000')
|
expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe('1000')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reacts to exact fixed Y-domain props and returns to automatic bounds', async () => {
|
it('reacts to nice fixed Y-domain props and returns to automatic bounds', async () => {
|
||||||
const wrapper = await mountSizedChart({
|
const wrapper = await mountSizedChart({
|
||||||
kind: 'points',
|
kind: 'points',
|
||||||
points: [
|
points: [
|
||||||
@@ -312,8 +332,8 @@ describe('WaveformChart', () => {
|
|||||||
|
|
||||||
await wrapper.setProps({ yDomain: [3, 97] })
|
await wrapper.setProps({ yDomain: [3, 97] })
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
expect(yTickLabels()).toContain('3')
|
expect(yTickLabels()).toContain('0')
|
||||||
expect(yTickLabels()).toContain('97')
|
expect(yTickLabels()).toContain('100')
|
||||||
|
|
||||||
await wrapper.setProps({ yDomain: undefined })
|
await wrapper.setProps({ yDomain: undefined })
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ export interface WaveformAxesOptions {
|
|||||||
}
|
}
|
||||||
y?: {
|
y?: {
|
||||||
lineVisible?: boolean
|
lineVisible?: boolean
|
||||||
|
/** Target total number of equal Y-axis ticks, including endpoints. Defaults to 5. */
|
||||||
|
splitNumber?: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user