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
375cbbfe
Commit
375cbbfe
authored
Jun 24, 2020
by
Michael Lunøe
Committed by
Illya Klymov
Jun 24, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix(contribution analytics): tilt x-axis in charts
Fixes #6830
parent
e1c83c24
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
197 additions
and
14 deletions
+197
-14
app/assets/javascripts/lib/utils/constants.js
app/assets/javascripts/lib/utils/constants.js
+2
-0
app/assets/javascripts/lib/utils/text_utility.js
app/assets/javascripts/lib/utils/text_utility.js
+79
-2
app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
...ts/javascripts/pages/admin/abuse_reports/abuse_reports.js
+1
-1
ee/app/assets/javascripts/analytics/contribution_analytics/components/column_chart.vue
...lytics/contribution_analytics/components/column_chart.vue
+37
-9
ee/app/assets/javascripts/analytics/contribution_analytics/constants.js
...javascripts/analytics/contribution_analytics/constants.js
+4
-0
ee/changelogs/unreleased/6830-tilt-axis-and-adjust-spacing-in-contribution-analytics-charts.yml
...s-and-adjust-spacing-in-contribution-analytics-charts.yml
+5
-0
ee/spec/frontend/analytics/contribution_analytics/components/__snapshots__/column_chart_spec.js.snap
...lytics/components/__snapshots__/column_chart_spec.js.snap
+1
-1
ee/spec/frontend/analytics/contribution_analytics/components/column_chart_spec.js
...cs/contribution_analytics/components/column_chart_spec.js
+17
-0
spec/frontend/diffs/components/diff_gutter_avatars_spec.js
spec/frontend/diffs/components/diff_gutter_avatars_spec.js
+1
-1
spec/frontend/lib/utils/text_utility_spec.js
spec/frontend/lib/utils/text_utility_spec.js
+50
-0
No files found.
app/assets/javascripts/lib/utils/constants.js
View file @
375cbbfe
export
const
BYTES_IN_KIB
=
1024
;
export
const
HIDDEN_CLASS
=
'
hidden
'
;
export
const
TRUNCATE_WIDTH_DEFAULT_WIDTH
=
80
;
export
const
TRUNCATE_WIDTH_DEFAULT_FONT_SIZE
=
12
;
export
const
DATETIME_RANGE_TYPES
=
{
fixed
:
'
fixed
'
,
...
...
app/assets/javascripts/lib/utils/text_utility.js
View file @
375cbbfe
import
{
isString
}
from
'
lodash
'
;
import
{
isString
,
memoize
}
from
'
lodash
'
;
import
{
TRUNCATE_WIDTH_DEFAULT_WIDTH
,
TRUNCATE_WIDTH_DEFAULT_FONT_SIZE
,
}
from
'
~/lib/utils/constants
'
;
/**
* Adds a , to a string composed by numbers, at every 3 chars.
...
...
@@ -73,7 +78,79 @@ export const slugifyWithUnderscore = str => slugify(str, '_');
* @param {Number} maxLength
* @returns {String}
*/
export
const
truncate
=
(
string
,
maxLength
)
=>
`
${
string
.
substr
(
0
,
maxLength
-
3
)}
...`
;
export
const
truncate
=
(
string
,
maxLength
)
=>
{
if
(
string
.
length
-
1
>
maxLength
)
{
return
`
${
string
.
substr
(
0
,
maxLength
-
1
)}
…`
;
}
return
string
;
};
/**
* This function calculates the average char width. It does so by placing a string in the DOM and measuring the width.
* NOTE: This will cause a reflow and should be used sparsely!
* The default fontFamily is 'sans-serif' and 12px in ECharts, so that is the default basis for calculating the average with.
* https://echarts.apache.org/en/option.html#xAxis.nameTextStyle.fontFamily
* https://echarts.apache.org/en/option.html#xAxis.nameTextStyle.fontSize
* @param {Object} options
* @param {Number} options.fontSize style to size the text for measurement
* @param {String} options.fontFamily style of font family to measure the text with
* @param {String} options.chars string of chars to use as a basis for calculating average width
* @return {Number}
*/
const
getAverageCharWidth
=
memoize
(
function
getAverageCharWidth
(
options
=
{})
{
const
{
fontSize
=
12
,
fontFamily
=
'
sans-serif
'
,
// eslint-disable-next-line @gitlab/require-i18n-strings
chars
=
'
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
'
,
}
=
options
;
const
div
=
document
.
createElement
(
'
div
'
);
div
.
style
.
fontFamily
=
fontFamily
;
div
.
style
.
fontSize
=
`
${
fontSize
}
px`
;
// Place outside of view
div
.
style
.
position
=
'
absolute
'
;
div
.
style
.
left
=
-
1000
;
div
.
style
.
top
=
-
1000
;
div
.
innerHTML
=
chars
;
document
.
body
.
appendChild
(
div
);
const
width
=
div
.
clientWidth
;
document
.
body
.
removeChild
(
div
);
return
width
/
chars
.
length
/
fontSize
;
});
/**
* This function returns a truncated version of `string` if its estimated rendered width is longer than `maxWidth`,
* otherwise it will return the original `string`
* Inspired by https://bl.ocks.org/tophtucker/62f93a4658387bb61e4510c37e2e97cf
* @param {String} string text to truncate
* @param {Object} options
* @param {Number} options.maxWidth largest rendered width the text may have
* @param {Number} options.fontSize size of the font used to render the text
* @return {String} either the original string or a truncated version
*/
export
const
truncateWidth
=
(
string
,
options
=
{})
=>
{
const
{
maxWidth
=
TRUNCATE_WIDTH_DEFAULT_WIDTH
,
fontSize
=
TRUNCATE_WIDTH_DEFAULT_FONT_SIZE
,
}
=
options
;
const
{
truncateIndex
}
=
string
.
split
(
''
).
reduce
(
(
memo
,
char
,
index
)
=>
{
let
newIndex
=
index
;
if
(
memo
.
width
>
maxWidth
)
{
newIndex
=
memo
.
truncateIndex
;
}
return
{
width
:
memo
.
width
+
getAverageCharWidth
()
*
fontSize
,
truncateIndex
:
newIndex
};
},
{
width
:
0
,
truncateIndex
:
0
},
);
return
truncate
(
string
,
truncateIndex
);
};
/**
* Truncate SHA to 8 characters
...
...
app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
View file @
375cbbfe
...
...
@@ -32,7 +32,7 @@ export default class AbuseReports {
$messageCellElement
.
text
(
originalMessage
);
}
else
{
$messageCellElement
.
data
(
'
messageTruncated
'
,
'
true
'
);
$messageCellElement
.
text
(
`
${
originalMessage
.
substr
(
0
,
MAX_MESSAGE_LENGTH
-
3
)}
...`
);
$messageCellElement
.
text
(
truncate
(
originalMessage
,
MAX_MESSAGE_LENGTH
)
);
}
}
}
ee/app/assets/javascripts/analytics/contribution_analytics/components/column_chart.vue
View file @
375cbbfe
<
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
'
;
import
{
truncateWidth
}
from
'
~/lib/utils/text_utility
'
;
import
{
GlResizeObserverDirective
}
from
'
@gitlab/ui
'
;
const
CHART_HEIGHT
=
220
;
import
{
CHART_HEIGHT
,
CHART_X_AXIS_NAME_TOP_PADDING
,
CHART_X_AXIS_ROTATE
,
INNER_CHART_HEIGHT
,
}
from
'
../constants
'
;
export
default
{
components
:
{
GlColumnChart
,
ResizableChartContainer
,
},
directives
:
{
GlResizeObserverDirective
,
},
props
:
{
chartData
:
{
...
...
@@ -28,6 +36,8 @@ export default {
},
data
()
{
return
{
width
:
0
,
height
:
CHART_HEIGHT
,
svgs
:
{},
};
},
...
...
@@ -40,6 +50,18 @@ export default {
chartOptions
()
{
return
{
dataZoom
:
[
this
.
dataZoomConfig
],
height
:
INNER_CHART_HEIGHT
,
xAxis
:
{
axisLabel
:
{
rotate
:
CHART_X_AXIS_ROTATE
,
formatter
(
value
)
{
return
truncateWidth
(
value
);
},
},
nameTextStyle
:
{
padding
:
[
CHART_X_AXIS_NAME_TOP_PADDING
,
0
,
0
,
0
],
},
},
};
},
seriesData
()
{
...
...
@@ -59,21 +81,27 @@ export default {
console
.
error
(
'
SVG could not be rendered correctly:
'
,
e
);
});
},
onChartCreated
()
{
onResize
()
{
const
{
columnChart
}
=
this
.
$refs
;
if
(
!
columnChart
)
return
;
const
{
width
}
=
columnChart
.
$el
.
getBoundingClientRect
();
this
.
width
=
width
;
},
onChartCreated
(
columnChart
)
{
this
.
setSvg
(
'
scroll-handle
'
);
columnChart
.
on
(
'
datazoom
'
,
this
.
updateAxisNamePadding
);
},
},
height
:
CHART_HEIGHT
,
};
</
script
>
<
template
>
<
resizable-chart-container
>
<
div
v-gl-resize-observer-directive=
"onResize"
>
<gl-column-chart
slot-scope=
"
{ width }
"
ref=
"columnChart
"
v-bind=
"$attrs"
:width=
"width"
:height="
$options.
height"
:height=
"height"
:data=
"seriesData"
:x-axis-title=
"xAxisTitle"
:y-axis-title=
"yAxisTitle"
...
...
@@ -81,5 +109,5 @@ export default {
:option=
"chartOptions"
@
created=
"onChartCreated"
/>
</
resizable-chart-container
>
</
div
>
</
template
>
ee/app/assets/javascripts/analytics/contribution_analytics/constants.js
0 → 100644
View file @
375cbbfe
export
const
CHART_HEIGHT
=
350
;
export
const
INNER_CHART_HEIGHT
=
200
;
export
const
CHART_X_AXIS_ROTATE
=
45
;
export
const
CHART_X_AXIS_NAME_TOP_PADDING
=
55
;
ee/changelogs/unreleased/6830-tilt-axis-and-adjust-spacing-in-contribution-analytics-charts.yml
0 → 100644
View file @
375cbbfe
---
title
:
'
Tilt
contribution
analytics
x-axis
chart
titles'
merge_request
:
33692
author
:
type
:
fixed
ee/spec/frontend/analytics/contribution_analytics/components/__snapshots__/column_chart_spec.js.snap
View file @
375cbbfe
...
...
@@ -4,7 +4,7 @@ exports[`Contribution Analytics Column Chart matches the snapshot 1`] = `
<div>
<gl-column-chart-stub
data="[object Object]"
height="
22
0"
height="
35
0"
option="[object Object]"
width="0"
xaxistitle="Username"
...
...
ee/spec/frontend/analytics/contribution_analytics/components/column_chart_spec.js
View file @
375cbbfe
...
...
@@ -27,4 +27,21 @@ describe('Contribution Analytics Column Chart', () => {
it
(
'
matches the snapshot
'
,
()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
it
(
'
matches properties
'
,
()
=>
{
const
{
height
,
xAxis
:
{
axisLabel
:
{
formatter
,
...
axisLabel
},
nameTextStyle
,
},
}
=
wrapper
.
find
(
'
gl-column-chart-stub
'
).
props
().
option
;
expect
(
height
).
toEqual
(
200
);
expect
(
nameTextStyle
).
toEqual
({
padding
:
[
55
,
0
,
0
,
0
],
});
expect
(
axisLabel
).
toEqual
({
rotate
:
45
,
});
});
});
spec/frontend/diffs/components/diff_gutter_avatars_spec.js
View file @
375cbbfe
...
...
@@ -110,7 +110,7 @@ describe('DiffGutterAvatars', () => {
it
(
'
returns truncated version of comment if it is longer than max length
'
,
()
=>
{
const
note
=
wrapper
.
vm
.
discussions
[
0
].
notes
[
1
];
expect
(
wrapper
.
vm
.
getTooltipText
(
note
)).
toEqual
(
'
Fatih Acet: comment 2 is r
...
'
);
expect
(
wrapper
.
vm
.
getTooltipText
(
note
)).
toEqual
(
'
Fatih Acet: comment 2 is r
ea…
'
);
});
});
});
spec/frontend/lib/utils/text_utility_spec.js
View file @
375cbbfe
...
...
@@ -145,6 +145,56 @@ describe('text_utility', () => {
});
});
describe
(
'
truncate
'
,
()
=>
{
it
(
'
returns the original string when str length is less than maxLength
'
,
()
=>
{
const
str
=
'
less than 20 chars
'
;
expect
(
textUtils
.
truncate
(
str
,
20
)).
toEqual
(
str
);
});
it
(
'
returns truncated string when str length is more than maxLength
'
,
()
=>
{
const
str
=
'
more than 10 chars
'
;
expect
(
textUtils
.
truncate
(
str
,
10
)).
toEqual
(
`
${
str
.
substring
(
0
,
10
-
1
)}
…`
);
});
it
(
'
returns the original string when rendered width is exactly equal to maxWidth
'
,
()
=>
{
const
str
=
'
Exactly 16 chars
'
;
expect
(
textUtils
.
truncate
(
str
,
16
)).
toEqual
(
str
);
});
});
describe
(
'
truncateWidth
'
,
()
=>
{
const
clientWidthDescriptor
=
Object
.
getOwnPropertyDescriptor
(
Element
.
prototype
,
'
clientWidth
'
);
beforeAll
(()
=>
{
// Mock measured width of ' ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
Object
.
defineProperty
(
Element
.
prototype
,
'
clientWidth
'
,
{
value
:
431
,
writable
:
false
,
});
});
afterAll
(()
=>
{
Object
.
defineProperty
(
Element
.
prototype
,
'
clientWidth
'
,
clientWidthDescriptor
);
});
it
(
'
returns the original string when rendered width is less than maxWidth
'
,
()
=>
{
const
str
=
'
< 80px
'
;
expect
(
textUtils
.
truncateWidth
(
str
)).
toEqual
(
str
);
});
it
(
'
returns truncated string when rendered width is more than maxWidth
'
,
()
=>
{
const
str
=
'
This is wider than 80px
'
;
expect
(
textUtils
.
truncateWidth
(
str
)).
toEqual
(
`
${
str
.
substring
(
0
,
10
)}
…`
);
});
it
(
'
returns the original string when rendered width is exactly equal to maxWidth
'
,
()
=>
{
const
str
=
'
Exactly 159.62962962962965px
'
;
expect
(
textUtils
.
truncateWidth
(
str
,
{
maxWidth
:
159.62962962962965
,
fontSize
:
10
})).
toEqual
(
str
,
);
});
});
describe
(
'
truncateSha
'
,
()
=>
{
it
(
'
shortens SHAs to 8 characters
'
,
()
=>
{
expect
(
textUtils
.
truncateSha
(
'
verylongsha
'
)).
toBe
(
'
verylong
'
);
...
...
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