Skip to content

Commit 6d72570

Browse files
committed
Attempt partial logic
1 parent 15b88c2 commit 6d72570

4 files changed

Lines changed: 167 additions & 67 deletions

File tree

assets/js/dashboard/components/graph.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ function InnerGraph<T extends ReadonlyArray<number | null>>({
235235
line.stopIndexExclusive !== undefined
236236
? i < line.stopIndexExclusive
237237
: true
238+
console.log(i, line.lineType, [line.startIndexInclusive,line.stopIndexExclusive],{valueDefined, atOrOverStart, beforeEnd})
238239
return valueDefined && atOrOverStart && beforeEnd
239240
},
240241
xAccessor: (_d, index) => x(index),
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {
2+
getChangeInPercentagePoints,
3+
getRelativeChange
4+
} from './main-graph-data'
5+
6+
describe(`${getChangeInPercentagePoints.name}`, () => {
7+
it('returns the difference', () => {
8+
expect(getChangeInPercentagePoints(70, 60)).toBe(10)
9+
})
10+
11+
it('returns a negative value when value is lower', () => {
12+
expect(getChangeInPercentagePoints(30, 50)).toBe(-20)
13+
})
14+
15+
it('returns 0 when both values are equal', () => {
16+
expect(getChangeInPercentagePoints(5, 5)).toBe(0)
17+
})
18+
})
19+
20+
describe(`${getRelativeChange.name}`, () => {
21+
it('returns the percentage change rounded to nearest integer', () => {
22+
expect(getRelativeChange(150, 100)).toBe(50)
23+
})
24+
25+
it('rounds fractional percentages', () => {
26+
expect(getRelativeChange(10, 3)).toBe(233) // (10-3)/3*100 = 233.33...
27+
})
28+
29+
it('returns 100 when comparison is 0 and value is positive', () => {
30+
expect(getRelativeChange(5, 0)).toBe(100)
31+
})
32+
33+
it('returns 0 when both are 0', () => {
34+
expect(getRelativeChange(0, 0)).toBe(0)
35+
})
36+
37+
it('returns a negative value for a decrease', () => {
38+
expect(getRelativeChange(50, 100)).toBe(-50)
39+
})
40+
})

assets/js/dashboard/stats/graph/main-graph-data.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export const remapAndFillData = ({
2727
yMax: number
2828
mainSeriesStartEndLabels: [string | null, string | null]
2929
comparisonSeriesStartEndLabels: [string | null, string | null]
30-
startOfLastPartialSlice: null | number
3130
} => {
3231
const totalBucketCount = Math.max(
3332
data.meta.comparison_time_label_result_indices?.length ?? 0,
@@ -41,8 +40,6 @@ export const remapAndFillData = ({
4140
let firstComparisonTimeLabel: null | string = null
4241
let lastComparisonTimeLabel: null | string = null
4342

44-
let startOfLastPartialSlice: null | number = null
45-
4643
const remappedData: GraphDatum[] = new Array(totalBucketCount)
4744
.fill(null)
4845
.map((_, index) => {
@@ -75,14 +72,6 @@ export const remapAndFillData = ({
7572
? true
7673
: false
7774

78-
if (isPartial) {
79-
startOfLastPartialSlice = index
80-
} else {
81-
// if there is a full period after a partial slice,
82-
// it's not a partial slice anchored at the end of the series
83-
startOfLastPartialSlice = null
84-
}
85-
8675
if (firstTimeLabel === null) {
8776
firstTimeLabel = timeLabel
8877
}
@@ -159,7 +148,6 @@ export const remapAndFillData = ({
159148
})
160149

161150
return {
162-
startOfLastPartialSlice,
163151
remappedData,
164152
yMax,
165153
mainSeriesStartEndLabels: [firstTimeLabel, lastTimeLabel],
@@ -170,21 +158,24 @@ export const remapAndFillData = ({
170158
}
171159
}
172160

173-
const METRICS_WITH_CHANGE_IN_PERCENTAGE_POINTS = [
161+
export const METRICS_WITH_CHANGE_IN_PERCENTAGE_POINTS = [
174162
'bounce_rate',
175163
'exit_rate',
176164
'conversion_rate'
177165
// 'group_conversion_rate'
178166
]
179167

180-
const getChangeInPercentagePoints = (
168+
export const getChangeInPercentagePoints = (
181169
value: number,
182170
comparisonValue: number
183171
): number => {
184172
return value - comparisonValue
185173
}
186174

187-
const getRelativeChange = (value: number, comparisonValue: number): number => {
175+
export const getRelativeChange = (
176+
value: number,
177+
comparisonValue: number
178+
): number => {
188179
if (comparisonValue === 0 && value > 0) {
189180
return 100
190181
}
@@ -195,6 +186,49 @@ const getRelativeChange = (value: number, comparisonValue: number): number => {
195186
return Math.round(((value - comparisonValue) / comparisonValue) * 100)
196187
}
197188

189+
type Slice = {
190+
startIndexInclusive: number
191+
endIndexExclusive: number
192+
isPartialLine: boolean
193+
}
194+
195+
// slices [B, A, A, A, A, B, B, B] to [0, 1], [1, 5], [5, 8]
196+
export function getSlices(
197+
data: { value: number | null; isPartial: boolean | null }[]
198+
): Slice[] {
199+
const slices: Slice[] = []
200+
let currentSlice: Slice | null = null
201+
202+
data.forEach((datum, index) => {
203+
if (datum.value !== null) {
204+
if (!currentSlice) {
205+
currentSlice = {
206+
startIndexInclusive: index,
207+
endIndexExclusive: index + 1,
208+
isPartialLine: datum.isPartial ?? false
209+
}
210+
} else {
211+
if (datum.isPartial === currentSlice.isPartialLine) {
212+
currentSlice.endIndexExclusive = index + 1
213+
} else {
214+
slices.push(currentSlice)
215+
currentSlice = {
216+
startIndexInclusive: index,
217+
endIndexExclusive: index + 1,
218+
isPartialLine: datum.isPartial ?? false
219+
}
220+
}
221+
}
222+
}
223+
})
224+
225+
if (currentSlice) {
226+
slices.push(currentSlice)
227+
}
228+
229+
return slices
230+
}
231+
198232
/**
199233
* A data point for the graph and tooltip.
200234
* It's x position is its index in `GraphDatum[]` array.

assets/js/dashboard/stats/graph/main-graph.tsx

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ export const MainGraph = ({
9191
const {
9292
remappedData,
9393
yMax,
94-
startOfLastPartialSlice,
9594
mainSeriesStartEndLabels,
9695
comparisonSeriesStartEndLabels
9796
} = remapAndFillData({
@@ -101,39 +100,86 @@ export const MainGraph = ({
101100

102101
const gradients = [primaryGradient, secondaryGradient]
103102

103+
const slices: {
104+
startIndexInclusive: number
105+
stopIndexExclusive: number
106+
lineType: 'solid' | 'dashed' | 'gap'
107+
}[] = []
108+
let slice: {
109+
startIndexInclusive: number
110+
stopIndexExclusive: number
111+
lineType: 'solid' | 'dashed' | 'gap'
112+
} | null = null
113+
// can't be done in a single pass with remapAndFillData
114+
// because we need the xLabels formatting parameters to be known
115+
const remappedDataInGraphFormat = remappedData.map((d, bucketIndex) => {
116+
const lineType =
117+
d.value === null ? 'gap' : d.isPartial ? 'dashed' : 'solid'
118+
console.log(lineType)
119+
120+
if (slice && slice.lineType !== lineType) {
121+
slice.stopIndexExclusive = bucketIndex
122+
slices.push(slice)
123+
slice = null
124+
}
125+
126+
if (slice) {
127+
slice.stopIndexExclusive = bucketIndex + 1
128+
} else {
129+
slice = {
130+
startIndexInclusive: bucketIndex,
131+
stopIndexExclusive: bucketIndex + 1,
132+
lineType
133+
}
134+
}
135+
136+
const dataPoint = {
137+
values: [d.value ?? null, d.comparisonValue ?? null] as const,
138+
xLabel:
139+
d.timeLabel !== null
140+
? getBucketLabel(d.timeLabel, {
141+
shouldShowDate: !isDateUnambiguous({
142+
startEndLabels: mainSeriesStartEndLabels
143+
}),
144+
shouldShowYear: !isYearUnambiguous({
145+
site,
146+
startEndLabels: mainSeriesStartEndLabels
147+
}),
148+
interval,
149+
period,
150+
bucketIndex,
151+
totalBuckets: remappedData.length
152+
})
153+
: ''
154+
}
155+
156+
return dataPoint
157+
})
158+
if (slice !== null) {
159+
slices.push(slice)
160+
}
161+
162+
console.log(slices)
104163
const mainSeries: SeriesConfig = {
105-
lines:
106-
startOfLastPartialSlice !== null && startOfLastPartialSlice > 0
107-
? [
108-
{
109-
lineClassName: classNames(
110-
sharedPathClass,
111-
mainPathClass,
112-
roundedPathClass
113-
),
114-
stopIndexExclusive: startOfLastPartialSlice
115-
},
116-
{
117-
lineClassName: classNames(
118-
sharedPathClass,
119-
mainPathClass,
120-
dashedPathClass
121-
),
122-
startIndexInclusive: startOfLastPartialSlice - 1
123-
}
164+
lines: slices
165+
.filter((s) => s.lineType === 'solid' || s.lineType === 'dashed')
166+
.map((s) => ({
167+
startIndexInclusive: s.startIndexInclusive,
168+
stopIndexExclusive: s.stopIndexExclusive + 1,
169+
lineClassName: classNames(
170+
sharedPathClass,
171+
mainPathClass,
172+
{ dashed: dashedPathClass, solid: roundedPathClass }[
173+
s.lineType as 'solid' | 'dashed'
124174
]
125-
: [
126-
{
127-
lineClassName: classNames(
128-
sharedPathClass,
129-
mainPathClass,
130-
roundedPathClass
131-
)
132-
}
133-
],
175+
),
176+
lineType: s.lineType
177+
})),
134178
underline: { gradientId: primaryGradient.id },
135179
dot: { dotClassName: classNames(sharedDotClass, mainDotClass) }
136180
}
181+
console.log(mainSeries.lines)
182+
137183
const comparisonSeries: SeriesConfig = {
138184
lines: [
139185
{
@@ -145,31 +191,10 @@ export const MainGraph = ({
145191
}
146192

147193
const settings: [SeriesConfig, SeriesConfig] = [
148-
mainSeries,
149-
comparisonSeries
194+
mainSeries
195+
// comparisonSeries
150196
]
151197

152-
// can't be done in a single pass with remapAndFillData
153-
// because we need the xLabels formatting parameters to be known
154-
const remappedDataInGraphFormat = remappedData.map((d, bucketIndex) => ({
155-
values: [d.value ?? null, d.comparisonValue ?? null] as const,
156-
xLabel:
157-
d.timeLabel !== null
158-
? getBucketLabel(d.timeLabel, {
159-
shouldShowDate: !isDateUnambiguous({
160-
startEndLabels: mainSeriesStartEndLabels
161-
}),
162-
shouldShowYear: !isYearUnambiguous({
163-
site,
164-
startEndLabels: mainSeriesStartEndLabels
165-
}),
166-
interval,
167-
period,
168-
bucketIndex,
169-
totalBuckets: remappedData.length
170-
})
171-
: ''
172-
}))
173198
const yearIsUnambiguous = isYearUnambiguous({
174199
site,
175200
startEndLabels: [

0 commit comments

Comments
 (0)