Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
0f8901d6
Commit
0f8901d6
authored
Feb 03, 2020
by
Martin Wortschack
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Migrate contribution analytics chart
- Ports the charts on the contributio analytics page from d3 to echarts
parent
28cfd6d0
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
231 additions
and
522 deletions
+231
-522
app/assets/javascripts/vue_shared/components/bar_chart.vue
app/assets/javascripts/vue_shared/components/bar_chart.vue
+0
-351
app/assets/javascripts/vue_shared/components/bar_chart_constants.js
.../javascripts/vue_shared/components/bar_chart_constants.js
+0
-4
changelogs/unreleased/37174-move-contribution-analytics-chart-to-echarts.yml
...ed/37174-move-contribution-analytics-chart-to-echarts.yml
+5
-0
ee/app/assets/javascripts/analytics/contribution_analytics/components/column_chart.vue
...lytics/contribution_analytics/components/column_chart.vue
+85
-0
ee/app/assets/javascripts/analytics/contribution_analytics/contribution_analytics_bundle.js
...s/contribution_analytics/contribution_analytics_bundle.js
+94
-0
ee/app/assets/javascripts/pages/groups/contribution_analytics/show/index.js
...scripts/pages/groups/contribution_analytics/show/index.js
+2
-88
ee/spec/frontend/analytics/contribution_analytics/components/__snapshots__/column_chart_spec.js.snap
...lytics/components/__snapshots__/column_chart_spec.js.snap
+15
-0
ee/spec/frontend/analytics/contribution_analytics/components/column_chart_spec.js
...cs/contribution_analytics/components/column_chart_spec.js
+30
-0
spec/javascripts/vue_shared/components/bar_chart_spec.js
spec/javascripts/vue_shared/components/bar_chart_spec.js
+0
-79
No files found.
app/assets/javascripts/vue_shared/components/bar_chart.vue
deleted
100644 → 0
View file @
28cfd6d0
<
script
>
import
*
as
d3
from
'
d3
'
;
import
tooltip
from
'
../directives/tooltip
'
;
import
Icon
from
'
./icon.vue
'
;
import
SvgGradient
from
'
./svg_gradient.vue
'
;
import
{
GRADIENT_COLORS
,
GRADIENT_OPACITY
,
INVERSE_GRADIENT_COLORS
,
INVERSE_GRADIENT_OPACITY
,
}
from
'
./bar_chart_constants
'
;
/**
* Renders a bar chart that can be dragged(scrolled) when the number
* of elements to renders surpasses that of the available viewport space
* while keeping even padding and a width of 24px (customizable)
*
* It can render data with the following format:
* graphData: [{
* name: 'element' // x domain data
* value: 1 // y domain data
* }]
*
* Used in:
* - Contribution analytics - all of the rows describing pushes, merge requests and issues
*/
export
default
{
directives
:
{
tooltip
,
},
components
:
{
Icon
,
SvgGradient
,
},
props
:
{
graphData
:
{
type
:
Array
,
required
:
true
,
},
barWidth
:
{
type
:
Number
,
required
:
false
,
default
:
24
,
},
yAxisLabel
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
minX
:
-
40
,
minY
:
0
,
vbWidth
:
0
,
vbHeight
:
0
,
vpWidth
:
0
,
vpHeight
:
200
,
preserveAspectRatioType
:
'
xMidYMin meet
'
,
containerMargin
:
{
leftRight
:
30
,
},
viewBoxMargin
:
{
topBottom
:
100
,
},
panX
:
0
,
xScale
:
{},
yScale
:
{},
zoom
:
{},
bars
:
{},
xGraphRange
:
0
,
isLoading
:
true
,
paddingThreshold
:
50
,
showScrollIndicator
:
false
,
showLeftScrollIndicator
:
false
,
isGrabbed
:
false
,
isPanAvailable
:
false
,
gradientColors
:
GRADIENT_COLORS
,
gradientOpacity
:
GRADIENT_OPACITY
,
inverseGradientColors
:
INVERSE_GRADIENT_COLORS
,
inverseGradientOpacity
:
INVERSE_GRADIENT_OPACITY
,
maxTextWidth
:
72
,
rectYAxisLabelDims
:
{},
xAxisTextElements
:
{},
yAxisRectTransformPadding
:
20
,
yAxisTextTransformPadding
:
10
,
yAxisTextRotation
:
90
,
};
},
computed
:
{
svgViewBox
()
{
return
`
${
this
.
minX
}
${
this
.
minY
}
${
this
.
vbWidth
}
${
this
.
vbHeight
}
`
;
},
xAxisLocation
()
{
return
`translate(
${
this
.
panX
}
,
${
this
.
vbHeight
}
)`
;
},
barTranslationTransform
()
{
return
`translate(
${
this
.
panX
}
, 0)`
;
},
scrollIndicatorTransform
()
{
return
`translate(
${
this
.
vbWidth
-
80
}
, 0)`
;
},
activateGrabCursor
()
{
return
{
'
svg-graph-container-with-grab
'
:
this
.
isPanAvailable
,
'
svg-graph-container-grabbed
'
:
this
.
isPanAvailable
&&
this
.
isGrabbed
,
};
},
yAxisLabelRectTransform
()
{
const
rectWidth
=
this
.
rectYAxisLabelDims
.
height
!=
null
?
this
.
rectYAxisLabelDims
.
height
/
2
:
0
;
const
yCoord
=
this
.
vbHeight
/
2
-
rectWidth
;
return
`translate(
${
this
.
minX
-
this
.
yAxisRectTransformPadding
}
,
${
yCoord
}
)`
;
},
yAxisLabelTextTransform
()
{
const
rectWidth
=
this
.
rectYAxisLabelDims
.
height
!=
null
?
this
.
rectYAxisLabelDims
.
height
/
2
:
0
;
const
yCoord
=
this
.
vbHeight
/
2
+
rectWidth
-
5
;
return
`translate(
${
this
.
minX
+
this
.
yAxisTextTransformPadding
}
,
${
yCoord
}
) rotate(-
${
this
.
yAxisTextRotation
}
)`
;
},
},
mounted
()
{
if
(
!
this
.
allValuesEmpty
)
{
this
.
draw
();
}
},
methods
:
{
draw
()
{
// update viewport
this
.
vpWidth
=
this
.
$refs
.
svgContainer
.
clientWidth
-
this
.
containerMargin
.
leftRight
;
// update viewbox
this
.
vbWidth
=
this
.
vpWidth
;
this
.
vbHeight
=
this
.
vpHeight
-
this
.
viewBoxMargin
.
topBottom
;
let
padding
=
0
;
if
(
this
.
graphData
.
length
*
this
.
barWidth
>
this
.
vbWidth
)
{
this
.
xGraphRange
=
this
.
graphData
.
length
*
this
.
barWidth
;
padding
=
this
.
calculatePadding
(
this
.
barWidth
);
this
.
showScrollIndicator
=
true
;
this
.
isPanAvailable
=
true
;
}
else
{
this
.
xGraphRange
=
this
.
vbWidth
-
Math
.
abs
(
this
.
minX
);
}
this
.
xScale
=
d3
.
scaleBand
()
.
range
([
0
,
this
.
xGraphRange
])
.
round
(
true
)
.
paddingInner
(
padding
);
this
.
yScale
=
d3
.
scaleLinear
().
rangeRound
([
this
.
vbHeight
,
0
]);
this
.
xScale
.
domain
(
this
.
graphData
.
map
(
d
=>
d
.
name
));
this
.
yScale
.
domain
([
0
,
d3
.
max
(
this
.
graphData
.
map
(
d
=>
d
.
value
))]);
// Zoom/Panning Function
this
.
zoom
=
d3
.
zoom
()
.
translateExtent
([[
0
,
0
],
[
this
.
xGraphRange
,
this
.
vbHeight
]])
.
on
(
'
zoom
'
,
this
.
panGraph
)
.
on
(
'
end
'
,
this
.
removeGrabStyling
);
const
xAxis
=
d3
.
axisBottom
().
scale
(
this
.
xScale
);
const
yAxis
=
d3
.
axisLeft
()
.
scale
(
this
.
yScale
)
.
ticks
(
4
);
const
renderedXAxis
=
d3
.
select
(
this
.
$refs
.
baseSvg
)
.
select
(
'
.x-axis
'
)
.
call
(
xAxis
);
this
.
xAxisTextElements
=
this
.
$refs
.
xAxis
.
querySelectorAll
(
'
text
'
);
renderedXAxis
.
select
(
'
.domain
'
).
remove
();
renderedXAxis
.
selectAll
(
'
text
'
)
.
style
(
'
text-anchor
'
,
'
end
'
)
.
attr
(
'
dx
'
,
'
-.3em
'
)
.
attr
(
'
dy
'
,
'
-.95em
'
)
.
attr
(
'
class
'
,
'
tick-text
'
)
.
attr
(
'
transform
'
,
'
rotate(-90)
'
);
renderedXAxis
.
selectAll
(
'
line
'
).
remove
();
const
{
maxTextWidth
}
=
this
;
renderedXAxis
.
selectAll
(
'
text
'
).
each
(
function
formatText
()
{
const
axisText
=
d3
.
select
(
this
);
let
textLength
=
axisText
.
node
().
getComputedTextLength
();
let
textContent
=
axisText
.
text
();
while
(
textLength
>
maxTextWidth
&&
textContent
.
length
>
0
)
{
textContent
=
textContent
.
slice
(
0
,
-
1
);
axisText
.
text
(
`
${
textContent
}
...`
);
textLength
=
axisText
.
node
().
getComputedTextLength
();
}
});
const
width
=
this
.
vbWidth
;
const
renderedYAxis
=
d3
.
select
(
this
.
$refs
.
baseSvg
)
.
select
(
'
.y-axis
'
)
.
call
(
yAxis
);
renderedYAxis
.
selectAll
(
'
.tick
'
).
each
(
function
createTickLines
(
d
,
i
)
{
if
(
i
>
0
)
{
d3
.
select
(
this
)
.
select
(
'
line
'
)
.
attr
(
'
x2
'
,
width
)
.
attr
(
'
class
'
,
'
axis-tick
'
);
}
});
// Add the panning capabilities
if
(
this
.
isPanAvailable
)
{
d3
.
select
(
this
.
$refs
.
baseSvg
)
.
call
(
this
.
zoom
)
.
on
(
'
wheel.zoom
'
,
null
);
// This disables the pan of the graph with the scroll of the mouse wheel
}
this
.
isLoading
=
false
;
// Update the yAxisLabel coordinates
const
labelDims
=
this
.
$refs
.
yAxisLabel
.
getBBox
();
this
.
rectYAxisLabelDims
=
{
height
:
labelDims
.
width
+
10
,
};
},
panGraph
()
{
const
allowedRightScroll
=
this
.
xGraphRange
-
this
.
vbWidth
-
this
.
paddingThreshold
;
const
graphMaxPan
=
Math
.
abs
(
d3
.
event
.
transform
.
x
)
<
allowedRightScroll
;
this
.
isGrabbed
=
true
;
this
.
panX
=
d3
.
event
.
transform
.
x
;
if
(
d3
.
event
.
transform
.
x
===
0
)
{
this
.
showLeftScrollIndicator
=
false
;
}
else
{
this
.
showLeftScrollIndicator
=
true
;
this
.
showScrollIndicator
=
true
;
}
if
(
!
graphMaxPan
)
{
this
.
panX
=
-
1
*
(
this
.
xGraphRange
-
this
.
vbWidth
+
this
.
paddingThreshold
);
this
.
showScrollIndicator
=
false
;
}
},
setTooltipTitle
(
data
)
{
return
data
!==
null
?
`
${
data
.
name
}
:
${
data
.
value
}
`
:
''
;
},
calculatePadding
(
desiredBarWidth
)
{
const
widthWithMargin
=
this
.
vbWidth
-
Math
.
abs
(
this
.
minX
);
const
dividend
=
widthWithMargin
-
this
.
graphData
.
length
*
desiredBarWidth
;
const
divisor
=
widthWithMargin
-
desiredBarWidth
;
return
dividend
/
divisor
;
},
removeGrabStyling
()
{
this
.
isGrabbed
=
false
;
},
barHoveredIn
(
index
)
{
this
.
xAxisTextElements
[
index
].
classList
.
add
(
'
x-axis-text
'
);
},
barHoveredOut
(
index
)
{
this
.
xAxisTextElements
[
index
].
classList
.
remove
(
'
x-axis-text
'
);
},
},
};
</
script
>
<
template
>
<div
ref=
"svgContainer"
:class=
"activateGrabCursor"
class=
"svg-graph-container"
>
<svg
ref=
"baseSvg"
class=
"svg-graph overflow-visible pt-5"
:width=
"vpWidth"
:height=
"vpHeight"
:viewBox=
"svgViewBox"
:preserveAspectRatio=
"preserveAspectRatioType"
>
<g
ref=
"xAxis"
:transform=
"xAxisLocation"
class=
"x-axis"
/>
<g
v-if=
"!isLoading"
>
<template
v-for=
"(data, index) in graphData"
>
<rect
:key=
"index"
v-tooltip
:width=
"xScale.bandwidth()"
:x=
"xScale(data.name)"
:y=
"yScale(data.value)"
:height=
"vbHeight - yScale(data.value)"
:transform=
"barTranslationTransform"
:title=
"setTooltipTitle(data)"
class=
"bar-rect"
data-placement=
"top"
@
mouseover=
"barHoveredIn(index)"
@
mouseout=
"barHoveredOut(index)"
/>
</
template
>
</g>
<rect
:height=
"vbHeight + 100"
transform=
"translate(-100, -5)"
width=
"100"
fill=
"#fff"
/>
<g
class=
"y-axis-label"
>
<line
:x1=
"0"
:x2=
"0"
:y1=
"0"
:y2=
"vbHeight"
transform=
"translate(-35, 0)"
stroke=
"black"
/>
<!-- Get text length and change the height of this rect accordingly -->
<rect
:height=
"rectYAxisLabelDims.height"
:transform=
"yAxisLabelRectTransform"
:width=
"30"
fill=
"#fff"
/>
<text
ref=
"yAxisLabel"
:transform=
"yAxisLabelTextTransform"
>
{{ yAxisLabel }}
</text>
</g>
<g
class=
"y-axis"
/>
<g
v-if=
"showScrollIndicator"
>
<rect
:height=
"vbHeight + 100"
:transform=
"`translate(${vpWidth - 60}, -5)`"
width=
"40"
fill=
"#fff"
/>
<icon
:x=
"vpWidth - 50"
:y=
"vbHeight / 2"
:width=
"14"
:height=
"14"
name=
"chevron-right"
class=
"animate-flicker"
/>
</g>
<!-- The line that shows up when the data elements surpass the available width -->
<g
v-if=
"showScrollIndicator"
:transform=
"scrollIndicatorTransform"
>
<rect
:height=
"vbHeight"
x=
"0"
y=
"0"
width=
"20"
fill=
"url(#shadow-gradient)"
/>
</g>
<!-- Left scroll indicator -->
<g
v-if=
"showLeftScrollIndicator"
transform=
"translate(0, 0)"
>
<rect
:height=
"vbHeight"
x=
"0"
y=
"0"
width=
"20"
fill=
"url(#left-shadow-gradient)"
/>
</g>
<svg-gradient
:colors=
"gradientColors"
:opacity=
"gradientOpacity"
identifier-name=
"shadow-gradient"
/>
<svg-gradient
:colors=
"inverseGradientColors"
:opacity=
"inverseGradientOpacity"
identifier-name=
"left-shadow-gradient"
/>
</svg>
</div>
</template>
app/assets/javascripts/vue_shared/components/bar_chart_constants.js
deleted
100644 → 0
View file @
28cfd6d0
export
const
GRADIENT_COLORS
=
[
'
#000
'
,
'
#a7a7a7
'
];
export
const
GRADIENT_OPACITY
=
[
'
0
'
,
'
0.4
'
];
export
const
INVERSE_GRADIENT_COLORS
=
[
'
#a7a7a7
'
,
'
#000
'
];
export
const
INVERSE_GRADIENT_OPACITY
=
[
'
0.4
'
,
'
0
'
];
changelogs/unreleased/37174-move-contribution-analytics-chart-to-echarts.yml
0 → 100644
View file @
0f8901d6
---
title
:
Move contribution analytics chart to echarts
merge_request
:
24272
author
:
type
:
other
ee/app/assets/javascripts/analytics/contribution_analytics/components/column_chart.vue
0 → 100644
View file @
0f8901d6
<
script
>
import
{
GlColumnChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
{
getSvgIconPathContent
}
from
'
~/lib/utils/icon_utils
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
const
CHART_HEIGHT
=
220
;
export
default
{
components
:
{
GlColumnChart
,
ResizableChartContainer
,
},
props
:
{
chartData
:
{
type
:
Array
,
required
:
true
,
},
xAxisTitle
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
yAxisTitle
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
data
()
{
return
{
svgs
:
{},
};
},
computed
:
{
dataZoomConfig
()
{
const
handleIcon
=
this
.
svgs
[
'
scroll-handle
'
];
return
handleIcon
?
{
handleIcon
}
:
{};
},
chartOptions
()
{
return
{
dataZoom
:
[
this
.
dataZoomConfig
],
};
},
seriesData
()
{
return
{
full
:
this
.
chartData
};
},
},
methods
:
{
setSvg
(
name
)
{
return
getSvgIconPathContent
(
name
)
.
then
(
path
=>
{
if
(
path
)
{
this
.
$set
(
this
.
svgs
,
name
,
`path://
${
path
}
`
);
}
})
.
catch
(
e
=>
{
// eslint-disable-next-line no-console, @gitlab/i18n/no-non-i18n-strings
console
.
error
(
'
SVG could not be rendered correctly:
'
,
e
);
});
},
onChartCreated
()
{
this
.
setSvg
(
'
scroll-handle
'
);
},
},
height
:
CHART_HEIGHT
,
};
</
script
>
<
template
>
<resizable-chart-container>
<gl-column-chart
slot-scope=
"
{ width }"
v-bind="$attrs"
:width="width"
:height="$options.height"
:data="seriesData"
:x-axis-title="xAxisTitle"
:y-axis-title="yAxisTitle"
x-axis-type="category"
:option="chartOptions"
@created="onChartCreated"
/>
</resizable-chart-container>
</
template
>
ee/app/assets/javascripts/analytics/contribution_analytics/contribution_analytics_bundle.js
0 → 100644
View file @
0f8901d6
import
Vue
from
'
vue
'
;
import
{
sortBy
}
from
'
lodash
'
;
import
ColumnChart
from
'
./components/column_chart.vue
'
;
import
{
__
}
from
'
~/locale
'
;
const
sortByValue
=
data
=>
sortBy
(
data
,
item
=>
item
[
1
]).
reverse
();
const
allValuesEmpty
=
graphData
=>
graphData
.
reduce
((
acc
,
data
)
=>
acc
+
Math
.
min
(
0
,
data
[
1
]),
0
)
===
0
;
export
default
dataEl
=>
{
if
(
!
dataEl
)
return
;
const
data
=
JSON
.
parse
(
dataEl
.
innerHTML
);
const
outputElIds
=
[
'
push
'
,
'
issues_closed
'
,
'
merge_requests_created
'
];
const
xAxisType
=
'
category
'
;
const
xAxisTitle
=
__
(
'
User
'
);
const
formattedData
=
{
push
:
[],
issues_closed
:
[],
merge_requests_created
:
[],
};
outputElIds
.
forEach
(
id
=>
{
data
[
id
].
data
.
forEach
((
d
,
index
)
=>
{
formattedData
[
id
].
push
([
data
.
labels
[
index
],
d
]);
});
});
const
pushesEl
=
document
.
getElementById
(
'
js_pushes_chart_vue
'
);
if
(
allValuesEmpty
(
formattedData
.
push
))
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
pushesEl
,
components
:
{
ColumnChart
,
},
render
(
h
)
{
return
h
(
ColumnChart
,
{
props
:
{
chartData
:
sortByValue
(
formattedData
.
push
),
xAxisTitle
,
yAxisTitle
:
__
(
'
Pushes
'
),
xAxisType
,
},
});
},
});
}
const
mergeRequestEl
=
document
.
getElementById
(
'
js_merge_requests_chart_vue
'
);
if
(
allValuesEmpty
(
formattedData
.
merge_requests_created
))
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
mergeRequestEl
,
components
:
{
ColumnChart
,
},
render
(
h
)
{
return
h
(
ColumnChart
,
{
props
:
{
chartData
:
sortByValue
(
formattedData
.
merge_requests_created
),
xAxisTitle
,
yAxisTitle
:
__
(
'
Merge Requests created
'
),
xAxisType
,
},
});
},
});
}
const
issueEl
=
document
.
getElementById
(
'
js_issues_chart_vue
'
);
if
(
allValuesEmpty
(
formattedData
.
issues_closed
))
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
issueEl
,
components
:
{
ColumnChart
,
},
render
(
h
)
{
return
h
(
ColumnChart
,
{
props
:
{
chartData
:
sortByValue
(
formattedData
.
issues_closed
),
xAxisTitle
,
yAxisTitle
:
__
(
'
Issues closed
'
),
xAxisType
,
},
});
},
});
}
};
ee/app/assets/javascripts/pages/groups/contribution_analytics/show/index.js
View file @
0f8901d6
import
Vue
from
'
vue
'
;
import
_
from
'
underscore
'
;
import
initContributionAanalyticsCharts
from
'
ee/analytics/contribution_analytics/contribution_analytics_bundle
'
;
import
initGroupMemberContributions
from
'
ee/group_member_contributions
'
;
import
BarChart
from
'
~/vue_shared/components/bar_chart.vue
'
;
import
{
__
}
from
'
~/locale
'
;
function
sortByValue
(
data
)
{
return
_
.
sortBy
(
data
,
'
value
'
).
reverse
();
}
function
allValuesEmpty
(
graphData
)
{
const
emptyCount
=
graphData
.
reduce
((
acc
,
data
)
=>
acc
+
Math
.
min
(
0
,
data
.
value
),
0
);
return
emptyCount
===
0
;
}
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
const
dataEl
=
document
.
getElementById
(
'
js-analytics-data
'
);
if
(
dataEl
)
{
const
data
=
JSON
.
parse
(
dataEl
.
innerHTML
);
const
outputElIds
=
[
'
push
'
,
'
issues_closed
'
,
'
merge_requests_created
'
];
const
formattedData
=
{
push
:
[],
issues_closed
:
[],
merge_requests_created
:
[],
};
outputElIds
.
forEach
(
id
=>
{
data
[
id
].
data
.
forEach
((
d
,
index
)
=>
{
formattedData
[
id
].
push
({
name
:
data
.
labels
[
index
],
value
:
d
,
});
});
});
initContributionAanalyticsCharts
(
dataEl
);
initGroupMemberContributions
();
const
pushesEl
=
document
.
getElementById
(
'
js_pushes_chart_vue
'
);
if
(
allValuesEmpty
(
formattedData
.
push
))
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
pushesEl
,
components
:
{
BarChart
,
},
render
(
createElement
)
{
return
createElement
(
'
bar-chart
'
,
{
props
:
{
graphData
:
sortByValue
(
formattedData
.
push
),
yAxisLabel
:
__
(
'
Pushes
'
),
},
});
},
});
}
const
mergeRequestEl
=
document
.
getElementById
(
'
js_merge_requests_chart_vue
'
);
if
(
allValuesEmpty
(
formattedData
.
merge_requests_created
))
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
mergeRequestEl
,
components
:
{
BarChart
,
},
render
(
createElement
)
{
return
createElement
(
'
bar-chart
'
,
{
props
:
{
graphData
:
sortByValue
(
formattedData
.
merge_requests_created
),
yAxisLabel
:
__
(
'
Merge Requests created
'
),
},
});
},
});
}
const
issueEl
=
document
.
getElementById
(
'
js_issues_chart_vue
'
);
if
(
allValuesEmpty
(
formattedData
.
issues_closed
))
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
issueEl
,
components
:
{
BarChart
,
},
render
(
createElement
)
{
return
createElement
(
'
bar-chart
'
,
{
props
:
{
graphData
:
sortByValue
(
formattedData
.
issues_closed
),
yAxisLabel
:
__
(
'
Issues closed
'
),
},
});
},
});
}
}
});
ee/spec/frontend/analytics/contribution_analytics/components/__snapshots__/column_chart_spec.js.snap
0 → 100644
View file @
0f8901d6
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Contribution Analytics Column Chart matches the snapshot 1`] = `
<div>
<gl-column-chart-stub
data="[object Object]"
height="220"
option="[object Object]"
width="0"
xaxistitle="Username"
xaxistype="category"
yaxistitle="Pushes"
/>
</div>
`;
ee/spec/frontend/analytics/contribution_analytics/components/column_chart_spec.js
0 → 100644
View file @
0f8901d6
import
{
mount
}
from
'
@vue/test-utils
'
;
import
Component
from
'
ee/analytics/contribution_analytics/components/column_chart.vue
'
;
const
mockChartData
=
[[
'
root
'
,
100
],
[
'
desiree
'
,
30
],
[
'
katlyn
'
,
70
],
[
'
myrtis
'
,
0
]];
describe
(
'
Contribution Analytics Column Chart
'
,
()
=>
{
let
wrapper
;
beforeEach
(()
=>
{
wrapper
=
mount
(
Component
,
{
propsData
:
{
chartData
:
mockChartData
,
xAxisTitle
:
'
Username
'
,
yAxisTitle
:
'
Pushes
'
,
},
stubs
:
{
'
gl-column-chart
'
:
true
,
},
});
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
matches the snapshot
'
,
()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
});
spec/javascripts/vue_shared/components/bar_chart_spec.js
deleted
100644 → 0
View file @
28cfd6d0
import
Vue
from
'
vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
BarChart
from
'
~/vue_shared/components/bar_chart.vue
'
;
function
getRandomArbitrary
(
min
,
max
)
{
return
Math
.
random
()
*
(
max
-
min
)
+
min
;
}
function
generateRandomData
(
dataNumber
)
{
const
randomGraphData
=
[];
for
(
let
i
=
1
;
i
<=
dataNumber
;
i
+=
1
)
{
randomGraphData
.
push
({
name
:
`random
${
i
}
`
,
value
:
parseInt
(
getRandomArbitrary
(
1
,
8
),
10
),
});
}
return
randomGraphData
;
}
describe
(
'
Bar chart component
'
,
()
=>
{
let
barChart
;
const
graphData
=
generateRandomData
(
10
);
beforeEach
(()
=>
{
const
BarChartComponent
=
Vue
.
extend
(
BarChart
);
barChart
=
mountComponent
(
BarChartComponent
,
{
graphData
,
yAxisLabel
:
'
data
'
,
});
});
afterEach
(()
=>
{
barChart
.
$destroy
();
});
it
(
'
calculates the padding for even distribution across bars
'
,
()
=>
{
barChart
.
vbWidth
=
1000
;
const
result
=
barChart
.
calculatePadding
(
30
);
// since padding can't be higher than 1 and lower than 0
// for more info: https://github.com/d3/d3-scale#band-scales
expect
(
result
).
not
.
toBeLessThan
(
0
);
expect
(
result
).
not
.
toBeGreaterThan
(
1
);
});
it
(
'
formats the tooltip title
'
,
()
=>
{
const
tooltipTitle
=
barChart
.
setTooltipTitle
(
barChart
.
graphData
[
0
]);
expect
(
tooltipTitle
).
toContain
(
'
random 1:
'
);
});
it
(
'
has a translates the bar graphs on across the X axis
'
,
()
=>
{
barChart
.
panX
=
100
;
expect
(
barChart
.
barTranslationTransform
).
toEqual
(
'
translate(100, 0)
'
);
});
it
(
'
translates the scroll indicator to the far right side
'
,
()
=>
{
barChart
.
vbWidth
=
500
;
expect
(
barChart
.
scrollIndicatorTransform
).
toEqual
(
'
translate(420, 0)
'
);
});
it
(
'
translates the x-axis to the bottom of the viewbox and pan coordinates
'
,
()
=>
{
barChart
.
panX
=
100
;
barChart
.
vbHeight
=
250
;
expect
(
barChart
.
xAxisLocation
).
toEqual
(
'
translate(100, 250)
'
);
});
it
(
'
rotates the x axis labels a total of 90 degress (CCW)
'
,
()
=>
{
const
xAxisLabel
=
barChart
.
$el
.
querySelector
(
'
.x-axis
'
).
querySelectorAll
(
'
text
'
)[
0
];
expect
(
xAxisLabel
.
getAttribute
(
'
transform
'
)).
toEqual
(
'
rotate(-90)
'
);
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment