Skip to content

Commit 5321048

Browse files
Merge pull request #6609 from topcoder-platform/reskin-profile-updates
Reskin profile updates
2 parents 35ccbd1 + 7a9485f commit 5321048

File tree

9 files changed

+231
-51
lines changed

9 files changed

+231
-51
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ workflows:
363363
filters:
364364
branches:
365365
only:
366-
- free
366+
- reskin-profile-settings
367367
# This is beta env for production soft releases
368368
- "build-prod-beta":
369369
context : org-global

src/shared/components/ProfilePage/Stats/ChartTooltip/index.jsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
import React from 'react';
66
import PT from 'prop-types';
77
import './styles.scss';
8+
import cn from 'classnames';
89

910
const ChartTooltip = ({
1011
show, left, top, challengeName,
11-
challengeData, rating, ratingColor, href,
12+
challengeData, rating, rotated, ratingColor, href,
13+
id,
1214
}) => (
1315
<a
14-
styleName="chart-tooltip"
16+
id={`chart-tooltip-${id}`}
17+
styleName={cn('chart-tooltip', rotated ? 'rotated' : null)}
1518
style={{
16-
opacity: show ? 1 : 0,
19+
display: show ? 'block' : 'none',
1720
left,
1821
top,
1922
pointerEvents: href ? 'all' : 'none',
@@ -44,6 +47,8 @@ ChartTooltip.defaultProps = {
4447
rating: 0,
4548
ratingColor: '',
4649
href: null,
50+
rotated: false,
51+
id: '',
4752
};
4853

4954
ChartTooltip.propTypes = {
@@ -55,6 +60,8 @@ ChartTooltip.propTypes = {
5560
rating: PT.number,
5661
ratingColor: PT.string,
5762
href: PT.string,
63+
rotated: PT.bool,
64+
id: PT.string,
5865
};
5966

6067
export default ChartTooltip;

src/shared/components/ProfilePage/Stats/ChartTooltip/styles.scss

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,19 @@
22

33
.chart-tooltip {
44
display: block;
5-
opacity: 0;
65
position: absolute;
76
box-sizing: border-box;
87
width: 320px;
9-
height: 115px;
8+
min-height: 115px;
109
padding: 15px;
1110
color: $tc-white !important;
1211
background-color: $tc-gray-80;
13-
transform: translate(-160px, -45px);
1412
border-radius: 5px;
1513
transition: opacity 200ms ease;
1614
transition-delay: 200ms;
1715
font-family: sans-serif;
1816
z-index: 3;
1917

20-
&:hover {
21-
opacity: 1 !important;
22-
color: $tc-white;
23-
}
24-
2518
&::before {
2619
content: '';
2720
position: absolute;
@@ -33,6 +26,22 @@
3326
transform: rotate(45deg);
3427
}
3528

29+
&.rotated {
30+
&::before {
31+
content: none;
32+
}
33+
34+
&::after {
35+
content: '';
36+
position: absolute;
37+
top: 96%;
38+
left: 50%;
39+
width: 10px;
40+
background-color: $tc-gray-80;
41+
transform: rotate(45deg);
42+
}
43+
}
44+
3645
.tooltip-rating {
3746
width: 60px;
3847
height: 60px;

src/shared/components/ProfilePage/Stats/DistributionGraph/index.jsx

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,55 @@ export default class DistributionGraph extends React.Component {
183183
.attr('height', d => totalH - padding.bottom - yScale(d.number))
184184
.attr('fill', d => getRatingColor(d.start));
185185

186+
const updateTooltipPosition = () => {
187+
const e = d3.mouse(document.getElementById('distribution-graph-container'));
188+
const profileModalContainerEl = document.querySelector('.ProfileModalContainer');
189+
const profileModalContainerRect = profileModalContainerEl
190+
? profileModalContainerEl.getBoundingClientRect() : null;
191+
const graphEl = document.getElementById('distribution-graph-container');
192+
const graphRect = graphEl ? graphEl.getBoundingClientRect() : null;
193+
const tooltipElement = document.getElementById('chart-tooltip-distribution-graph');
194+
195+
let cx = e[0];
196+
let cy = e[1];
197+
const defaultWidth = 320;
198+
const defaultHeight = 115;
199+
if (tooltipElement) {
200+
const { clientWidth, clientHeight } = tooltipElement;
201+
cx -= ((clientWidth || defaultWidth) / 2);
202+
cy += 15;
203+
204+
if (graphRect && profileModalContainerRect) {
205+
const minLeft = profileModalContainerRect.x - graphRect.x;
206+
const minTop = profileModalContainerRect.y - graphRect.y;
207+
const maxRight = profileModalContainerRect.width + minLeft;
208+
const maxBottom = profileModalContainerRect.height + minTop;
209+
const minXTooltipPosition = minLeft;
210+
const maxXTooltipPosition = maxRight - (clientWidth || defaultWidth);
211+
const minYTooltipPosition = minTop;
212+
const maxYTooltipPosition = maxBottom - (clientHeight || defaultHeight);
213+
if (cx < minXTooltipPosition) {
214+
cx = minXTooltipPosition;
215+
}
216+
if (cx > maxXTooltipPosition) {
217+
cx = maxXTooltipPosition;
218+
}
219+
if (cy < minYTooltipPosition) {
220+
cy = minYTooltipPosition;
221+
}
222+
if (cy > maxYTooltipPosition) {
223+
cy = maxYTooltipPosition;
224+
}
225+
}
226+
}
227+
228+
$scope.setState({
229+
show: true,
230+
left: cx,
231+
top: cy,
232+
});
233+
};
234+
186235
svg.selectAll('rect.hover')
187236
.data(ranges)
188237
.enter()
@@ -194,24 +243,16 @@ export default class DistributionGraph extends React.Component {
194243
.attr('width', xScale.rangeBand())
195244
.attr('height', d => totalH - padding.bottom - yScale(d.number))
196245
.on('mouseover', (d) => {
197-
const e = d3.event;
198246
$scope.setState({
199-
show: true,
200-
left: e.pageX,
201-
top: e.pageY,
202247
challengeName: `${d.number} Coders`,
203248
challengeData: `Rating Range: ${d.start} - ${d.start + 99}`,
204249
rating: d.number,
205250
ratingColor: getRatingColor(d.start),
206251
});
252+
updateTooltipPosition();
207253
})
208254
.on('mousemove', () => {
209-
const e = d3.event;
210-
$scope.setState({
211-
show: true,
212-
left: e.pageX,
213-
top: e.pageY,
214-
});
255+
updateTooltipPosition();
215256
})
216257
.on('mouseout', () => {
217258
$scope.setState({
@@ -253,8 +294,8 @@ export default class DistributionGraph extends React.Component {
253294

254295
render() {
255296
return (
256-
<div styleName="distribution-graph" ref={this.graphRef}>
257-
<ChartTooltip {...this.state} />
297+
<div id="distribution-graph-container" styleName="distribution-graph" ref={this.graphRef}>
298+
<ChartTooltip id="distribution-graph" {...this.state} />
258299
</div>
259300
);
260301
}

src/shared/components/ProfilePage/Stats/DistributionGraph/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
display: flex;
55
flex-direction: row;
66
justify-content: center;
7+
position: relative;
78

89
.axis path,
910
.axis line {

src/shared/components/ProfilePage/Stats/HistoryGraph/index.jsx

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ export default class HistoryGraph extends React.Component {
3434
}
3535
};
3636
window.addEventListener('resize', this.resizeHandle);
37-
this.bodyClickHandle = () => this.setState({ show: false });
37+
this.bodyClickHandle = (event) => {
38+
if (event.target && event.target.tagName === 'circle') {
39+
return;
40+
}
41+
this.setState({ show: false });
42+
};
3843
document.body.addEventListener('click', this.bodyClickHandle);
3944
}
4045

@@ -240,6 +245,58 @@ export default class HistoryGraph extends React.Component {
240245
.attr('stroke-width', 3);
241246
*/
242247

248+
const updateTooltipPosition = () => {
249+
const e = d3.mouse(document.getElementById('history-graph-container'));
250+
const profileModalContainerEl = document.querySelector('.ProfileModalContainer');
251+
const profileModalContainerRect = profileModalContainerEl
252+
? profileModalContainerEl.getBoundingClientRect() : null;
253+
const graphEl = document.getElementById('history-graph-container');
254+
const graphRect = graphEl ? graphEl.getBoundingClientRect() : null;
255+
const tooltipElement = document.getElementById('chart-tooltip-history-graph');
256+
257+
let cx = e[0];
258+
let cy = e[1];
259+
let rotated = false;
260+
const defaultWidth = 320;
261+
const defaultHeight = 115;
262+
if (tooltipElement) {
263+
const { clientWidth, clientHeight } = tooltipElement;
264+
cx -= ((clientWidth || defaultWidth) / 2);
265+
cy += 15;
266+
267+
if (graphRect && profileModalContainerRect) {
268+
const minLeft = profileModalContainerRect.x - graphRect.x;
269+
const minTop = profileModalContainerRect.y - graphRect.y;
270+
const maxRight = profileModalContainerRect.width + minLeft;
271+
const maxBottom = profileModalContainerRect.height + minTop;
272+
const minXTooltipPosition = minLeft;
273+
const maxXTooltipPosition = maxRight - (clientWidth || defaultWidth);
274+
const minYTooltipPosition = minTop;
275+
const maxYTooltipPosition = maxBottom - (clientHeight || defaultHeight);
276+
if (cx < minXTooltipPosition) {
277+
cx = minXTooltipPosition;
278+
}
279+
if (cx > maxXTooltipPosition) {
280+
cx = maxXTooltipPosition;
281+
}
282+
if (cy < minYTooltipPosition) {
283+
cy = minYTooltipPosition;
284+
}
285+
if (cy > maxYTooltipPosition) {
286+
cy -= clientHeight + 25;
287+
rotated = true;
288+
}
289+
}
290+
}
291+
292+
$scope.setState({
293+
rotated,
294+
show: true,
295+
left: cx,
296+
top: cy,
297+
});
298+
};
299+
243300
svg.selectAll('circle')
244301
.data(history)
245302
.enter()
@@ -249,24 +306,25 @@ export default class HistoryGraph extends React.Component {
249306
.attr('r', 5.5)
250307
.attr('fill', d => getRatingColor(d.newRating))
251308
.on('mouseover', (d) => {
252-
const e = d3.event;
253309
$scope.setState({
254-
show: true,
255-
left: e.pageX,
256-
top: e.pageY,
257310
challengeName: d.challengeName,
258311
challengeData: moment(d.ratingDate).format('MMM DD, YYYY'),
259312
rating: d.newRating,
260313
ratingColor: getRatingColor(d.newRating),
261314
href: getChallengeLink(d.challengeId),
262315
});
316+
317+
updateTooltipPosition();
318+
})
319+
.on('mousemove', () => {
320+
updateTooltipPosition();
263321
});
264322
}
265323

266324
render() {
267325
return (
268-
<div styleName="history-graph" ref={this.graphRef}>
269-
<ChartTooltip {...this.state} />
326+
<div id="history-graph-container" styleName="history-graph" ref={this.graphRef}>
327+
<ChartTooltip id="history-graph" {...this.state} />
270328
</div>
271329
);
272330
}

src/shared/components/ProfilePage/Stats/HistoryGraph/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
display: flex;
55
flex-direction: row;
66
justify-content: center;
7+
position: relative;
78

89
.axis path,
910
.axis line {

src/shared/components/ProfilePage/index.jsx

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import _ from 'lodash';
88
import React from 'react';
99
import PT from 'prop-types';
1010
import { isomorphy } from 'topcoder-react-utils';
11+
import { Modal } from 'topcoder-react-ui-kit';
12+
import IconClose from 'assets/images/icon-close-green.svg';
1113
import shortId from 'shortid';
1214
import { actions } from 'topcoder-react-lib';
1315
import { connect } from 'react-redux';
@@ -17,12 +19,12 @@ import { dataMap } from './ExternalLink';
1719
import Header from './Header';
1820
import MemberTracks from './MemberTracks';
1921

20-
import './styles.scss';
22+
import styles from './styles.scss';
2123
import Skills from './Skills';
2224
import MemberInfo from './MemberInfo';
2325
import Activity from './Activity';
2426
import TcaCertificates from './TcaCertificates';
25-
import ProfileModal from './ProfileModal';
27+
// import ProfileModal from './ProfileModal';
2628
// import Awards from './Awards';
2729

2830
/**
@@ -249,26 +251,38 @@ class ProfilePage extends React.Component {
249251
}}
250252
/>
251253
{ showDetails && (
252-
<ProfileModal
253-
title={(
254-
subTrack === 'SRM'
255-
? 'Single round match'
256-
: subTrack.replace('FIRST_2_FINISH', 'FIRST2FINISH').replace(/_/g, ' ')
257-
)}
254+
<Modal
255+
theme={{
256+
container: `${track === 'COPILOT' ? styles['modal-container-copilot'] : styles['modal-container']} ProfileModalContainer`,
257+
overlay: styles['modal-overlay'],
258+
}}
258259
onCancel={this.closeDetails}
259260
>
260-
<ProfileStats
261-
handleParam={handleParam}
262-
meta={meta}
263-
track={track}
264-
subTrack={subTrack}
265-
tab={tab}
266-
setTab={(tab) => {
267-
this.setState({ tab });
268-
}}
269-
isAlreadyLoadChallenge={this.isAlreadyLoadChallenge}
270-
/>
271-
</ProfileModal>
261+
<React.Fragment>
262+
<div styleName="header">
263+
<h2 styleName="title">
264+
{
265+
subTrack === 'SRM' ? 'Single round match'
266+
: subTrack.replace('FIRST_2_FINISH', 'FIRST2FINISH').replace(/_/g, ' ')
267+
}
268+
</h2>
269+
<div styleName="icon" role="presentation" onClick={this.closeDetails}>
270+
<IconClose />
271+
</div>
272+
</div>
273+
<ProfileStats
274+
handleParam={handleParam}
275+
meta={meta}
276+
track={track}
277+
subTrack={subTrack}
278+
tab={tab}
279+
setTab={(tab) => {
280+
this.setState({ tab });
281+
}}
282+
isAlreadyLoadChallenge={this.isAlreadyLoadChallenge}
283+
/>
284+
</React.Fragment>
285+
</Modal>
272286
)}
273287
</div>
274288
);

0 commit comments

Comments
 (0)