Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Fixed issue #814 - [$100] Topcoder Member Profile: Redesign ratings graph #815

Merged
merged 18 commits into from
May 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 82 additions & 5 deletions app/directives/distribution-graph/distribution-graph.directive.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import angular from 'angular'
import d3 from 'd3'
import React from 'react' // eslint-disable-line no-unused-vars
import ReactDOM from 'react-dom'
import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip.jsx'

(function() {
'use strict'
Expand Down Expand Up @@ -59,7 +62,7 @@ import d3 from 'd3'
]

var measurements = {
w: 600,
w: 900,
h: 400,
padding: {
top: 20,
Expand Down Expand Up @@ -176,7 +179,24 @@ import d3 from 'd3'
.attr('fill', function(d) {
return ratingToColor($scope.colors, d.start)
})


var mousemoveInterval = null

/* render react tooltip component */
ReactDOM.unmountComponentAtNode(document.getElementById('chart-tooltip'))
ReactDOM.render(<Tooltip popMethod='click'>
<div className='tooltip-target'></div>
<div className='tooltip-body'>
<div className='tooltip-rating'></div>
<div className='tooltip-challenge'>
<div className='challenge-name'></div>
<div className='challenge-date'></div>
</div>
</div>
</Tooltip>
, document.getElementById('chart-tooltip'))

$scope.isFocused = false
svg.selectAll('rect.hover')
.data(ranges)
.enter()
Expand All @@ -187,24 +207,81 @@ import d3 from 'd3'
return xScale(i)
})
.attr('y', function(d) {
return padding.top
return yScale(d.number)
})
.attr('width', xScale.rangeBand())
.attr('height', function(d) {
return totalH - padding.bottom - padding.top
return totalH - padding.bottom - yScale(d.number)
})
.on('mouseover', function(d) {
$scope.isFocused = true
$scope.highlightedRating = d.start
$scope.displayCoders = true
$scope.numCoders = d.number

/* update tooltip location on mouseover, feature currently not inbuilt in react tooltip component */
d3.select('#chart-tooltip')
.style('left', (d3.event.pageX-4) + 'px')
.style('top', (d3.event.pageY-4) + 'px')

$('#chart-tooltip').addClass('distribution')
d3.select('#chart-tooltip .tooltip-container')
.style('left', '20px !important')
.style('top', '-20px !important')

d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
.style('left', '-5.5px !important')
.style('bottom', '25px !important')

d3.select('#chart-tooltip .challenge-name').text($scope.numCoders + ' Coders')
d3.select('#chart-tooltip .challenge-date').text('Rating Range: '+ $scope.highlightedRating + '-'+($scope.highlightedRating+99))
d3.select('#chart-tooltip .tooltip-rating').text($scope.numCoders)
d3.select('#chart-tooltip .tooltip-rating').style('background', ratingToColor($scope.colors, $scope.highlightedRating))
$scope.$digest()
})
.on('mousemove', function(d) {

/* update tooltip on mousemove, using interval of 50ms to improve performance */
window.clearTimeout(mousemoveInterval)
var left = (d3.event.pageX-4)
var top = (d3.event.pageY-4)

mousemoveInterval = window.setTimeout(function(){
d3.select('#chart-tooltip')
.style('left', left + 'px')
.style('top', top + 'px')

d3.select('#chart-tooltip .tooltip-container')
.style('left', '20px !important')
.style('top', '-20px !important')

d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
.style('left', '-5.5px !important')
.style('bottom', '25px !important')
}, 50)

})
.on('mouseout', function(d) {
$scope.displayCoders = false
$scope.highlightedRating = false
$scope.isFocused = false
$scope.$digest()
})


/* hide tooltip when clicked anywhere outside */
d3.select('body').on('click', function(){
if((d3.event.target.classList[0] != 'tooltip-target') && !$('#chart-tooltip .tooltip-container').hasClass('tooltip-hide') &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code can be simplified, as suggested in similar place for the history graph.

!isInArray(d3.event.target.classList[0], ['tooltip-content-container', 'tooltip-container', 'tooltip-body', 'Tooltip']) &&
(d3.event.target.tagName.toLowerCase()!='circle') && !(d3.event.target.tagName.toLowerCase()=='rect' && d3.event.target.classList[0] == 'hover')) {
$('#chart-tooltip .tooltip-target').trigger('click')
$('#chart-tooltip').removeClass('distribution')
}
})

function isInArray(value, array) {
return array.indexOf(value) > -1
}

svg.selectAll('line.xaxis')
.data(ranges)
.enter()
Expand Down
18 changes: 7 additions & 11 deletions app/directives/distribution-graph/distribution-graph.jade
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
.distribution-graph-directive(ng-show="graphState.show == 'distribution'")

.graph-viewer
.distribution-graph
.graph-title
.text Rating Distribution Graph
.button-group
button.tc-btn.tc-btn-s(ng-click="graphState.show = 'history'") View Rating History
button.tc-btn.tc-btn-s.active(ng-click="graphState.show = 'distribution'") View Rating Distribution

.info-port
.coders(ng-if="displayCoders", style="background: {{highlightedRating || rating | ratingColor}}")
.num {{numCoders}}
.label CODERS
.coders(ng-if="!displayCoders", style="background: {{rating | ratingColor}}")
.num {{rating}}
.label RATING
button.tc-btn.tc-btn-s.compare(ng-click="graphState.show = 'history'")
| View Rating History
.graph-viewer

.distribution-graph
64 changes: 52 additions & 12 deletions app/directives/history-graph/history-graph.directive.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import angular from 'angular'
import moment from 'moment'
import d3 from 'd3'
import React from 'react' // eslint-disable-line no-unused-vars
import ReactDOM from 'react-dom'
import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip.jsx'

(function() {
'use strict'
Expand All @@ -16,11 +19,11 @@ import d3 from 'd3'
rating: '=',
graphState: '='
},
controller: ['$scope', HistoryGraphController]
controller: ['$scope', '$state', '$filter', 'CONSTANTS', HistoryGraphController]
}
}

function HistoryGraphController($scope) {
function HistoryGraphController($scope, $state, $filter, CONSTANTS) {
$scope.colors = [
// grey
{
Expand Down Expand Up @@ -59,7 +62,7 @@ import d3 from 'd3'
}
]
var measurements = {
w: 600,
w: 900,
h: 400,
padding: {
top: 20,
Expand Down Expand Up @@ -156,7 +159,6 @@ import d3 from 'd3'
.attr('width', w + padding.left + padding.right)
.attr('height', h + padding.top + padding.bottom)


svg.append('rect')
.attr('x', padding.left)
.attr('y', padding.top)
Expand Down Expand Up @@ -243,7 +245,21 @@ import d3 from 'd3'
return y
}
}


/* render react tooltip component */
ReactDOM.unmountComponentAtNode(document.getElementById('chart-tooltip'))
ReactDOM.render(<Tooltip popMethod='click'>
<div className='tooltip-target'></div>
<div className='tooltip-body'>
<div className='tooltip-rating'></div>
<div className='tooltip-challenge'>
<div className='challenge-name'></div>
<div className='challenge-date'></div>
</div>
</div>
</Tooltip>
, document.getElementById('chart-tooltip'))

svg.selectAll('circle')
.data(history)
.enter()
Expand All @@ -261,20 +277,44 @@ import d3 from 'd3'
$scope.historyRating = d.newRating
$scope.historyDate = moment(d.ratingDate).format('YYYY-MM-DD')
$scope.historyChallenge = d.challengeName
$('#chart-tooltip .tooltip-container').on('click', function(){
if($state.params && ($state.params.subTrack === 'SRM' || $state.params.subTrack === 'MARATHON_MATCH'))
location.href = $filter('challengeLinks')({'rounds': [{id: d.challengeId, forumId: null}], 'track': $state.params.track, 'subTrack': $state.params.subTrack}, 'detail')
else
location.href = $filter('challengeLinks')({id: d.challengeId, 'track': $state.params.track, 'subTrack': $state.params.subTrack}, 'detail')
})

/* update tooltip location on mouseover, feature currently not inbuilt in react tooltip component */
d3.select('#chart-tooltip')
.style('left', (d3.event.pageX-5) + 'px')
.style('top', (d3.event.pageY-5) + 'px')
d3.select('#chart-tooltip .tooltip-container')
.style('left', '20px !important')
.style('top', '-20px !important')
d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
.style('left', '-5.5px !important')
.style('bottom', '25px !important')

d3.select('#chart-tooltip .challenge-name').text($scope.historyChallenge)
d3.select('#chart-tooltip .challenge-date').text(moment(d.ratingDate).format('MMM DD, YYYY'))
d3.select('#chart-tooltip .tooltip-rating').text($scope.historyRating)
d3.select('#chart-tooltip .tooltip-rating').style('background', ratingToColor($scope.colors, $scope.historyRating))
$('#chart-tooltip').removeClass('distribution')
$scope.$digest()
d3.select(this)
.attr('r', 6.0)
})
.on('mouseout', function(d) {
$scope.historyRating = undefined
$scope.$digest()
d3.select(this)
.attr('r', 4.5)
.attr('stroke', 'none')
.attr('stroke-width', '0px')
})
.attr('r', 4.5)

/* hide tooltip when clicked anywhere outside */
d3.select('body').on('click', function(){
if((d3.event.target.classList[0] != 'tooltip-target') && !$('#chart-tooltip .tooltip-container').hasClass('tooltip-hide') &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic can be simplified to a great extent by using traversing the event.target's parents. What I understood from this code is that you are trying to detected if the mouse is NOT clicked on the tooltip itself, for that, we can iterate the event.target's parents and check if we find the root class of the tooltip i.e. .tooltip-container.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vikasrohit I think there is no parent info for event.target in d3.js, I already tried many options. And we have to use d3.event.target to handle clicks inside the chart. Only, thing is we can optimize condition to shorten it, which I am doing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Try your best to optimize the condition so that it is more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, most of the conditions have different meaning other than the classes inside tooltip, so those can't be optimized more. It is because we have to handle clicks inside d3js svg also. I have done best possible as per my understanding.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. Lets proceed without this optimization thing. Will take care of this after this challenge.

(d3.event.target.tagName.toLowerCase()!='circle') && !(d3.event.target.tagName.toLowerCase()=='rect' && d3.event.target.classList[0] == 'hover')) {
$('#chart-tooltip .tooltip-target').trigger('click')
$('#chart-tooltip .tooltip-container').off('click')
}
})

}

Expand Down
18 changes: 8 additions & 10 deletions app/directives/history-graph/history-graph.jade
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
.history-graph-directive(ng-show="graphState.show == 'history'")
.history-graph-container

.history-graph
.graph-title
.text Rating History Graph
.button-group
button.tc-btn.tc-btn-s.active(ng-click="graphState.show = 'history'") View Rating History
button.tc-btn.tc-btn-s(ng-click="graphState.show = 'distribution'") View Rating Distribution

.history-graph-container

.info-port
.rating(style="background: {{historyRating || rating | ratingColor}}")
.num {{historyRating || rating}}
.label RATING
.history-info
.challenge(ng-if="historyRating") {{historyChallenge}}
.date(ng-if="historyRating") {{historyDate | date}}
button.tc-btn.tc-btn-s.compare(ng-click="graphState.show = 'distribution'") View Rating Distribution
.history-graph
9 changes: 9 additions & 0 deletions app/filters/challengeLinks.filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ import angular from 'angular'
case 'detail':
return String.supplant('https://community.{domain}/longcontest/stats/?module=ViewOverview&rd={roundId}', data)
}
} else if (challenge.subTrack === 'SRM') {
data = {
domain: CONSTANTS.domain,
roundId: challenge.rounds[0].id
}
switch (type) {
case 'detail':
return String.supplant('https://community.{domain}/stat?c=round_overview&rd={roundId}', data)
}
} else {
data = {
domain: CONSTANTS.domain,
Expand Down
2 changes: 2 additions & 0 deletions app/index.jade
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ html
.fold-pusher

div(ui-view="footer")

#chart-tooltip
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think chart tooltip element does not makes sense in all pages, can we move it to profile page's jade file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we can't do that as we need it the end of body, to position it relatively to body. Otherwise, it will not display at correct position.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, we would revisit this after this challenge.

1 change: 1 addition & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require('xml2js')

require('appirio-tech-ng-ui-components')
require('appirio-tech-ng-iso-constants')
require('appirio-tech-react-components')

// Vendor styles
require('../node_modules/angucomplete-alt/angucomplete-alt.css')
Expand Down
45 changes: 45 additions & 0 deletions assets/css/directives/distribution-graph.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,51 @@

}

.graph-title {
width: 960px;
padding-left: 45px;
text-align: left;
margin: auto;
font-size: 28px;
line-height: 35px;
color: #47474F;

.text {
float: left;
}

.button-group {
float: right;
display: block;
button.tc-btn.tc-btn-s {
background: #fff;
color: #47474f;
border: 1px solid #c3c3c8;
height: 30px;
box-sizing: border-box;
&.active {
background: #dcdce0;
-webkit-box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
-moz-box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
border: none;
}
&:first-child {
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
}
}
}

.graph-viewer {
margin-top: 18px;
display: flex;
Expand Down
Loading