feat: paginate heatmap + calculate streaks on client (#38318)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@ -1,3 +0,0 @@
|
||||
module.exports = async () => {
|
||||
process.env.TZ = 'UTC';
|
||||
};
|
@ -12,7 +12,6 @@ module.exports = {
|
||||
globals: {
|
||||
__PATH_PREFIX__: ''
|
||||
},
|
||||
globalSetup: './jest-timezone-setup.js',
|
||||
verbose: true,
|
||||
transform: {
|
||||
'^.+\\.js$': '<rootDir>/jest.transform.js'
|
||||
|
@ -1,6 +0,0 @@
|
||||
/* global expect */
|
||||
describe('Timezones', () => {
|
||||
it('should always be UTC', () => {
|
||||
expect(new Date().getTimezoneOffset()).toBe(0);
|
||||
});
|
||||
});
|
74
client/package-lock.json
generated
74
client/package-lock.json
generated
@ -2918,6 +2918,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@freecodecamp/react-calendar-heatmap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@freecodecamp/react-calendar-heatmap/-/react-calendar-heatmap-1.0.0.tgz",
|
||||
"integrity": "sha512-+bqI/VEVHiuvD+Ca17e9os4eQ8MG5xv/tXjyWYjK5zfo81FiCPF10P3LbAkHnttRatxxeudTDCmJjCR2kSM0xQ==",
|
||||
"requires": {
|
||||
"memoize-one": "^5.0.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.8.1"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@hapi/address": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.2.tgz",
|
||||
@ -19748,9 +19782,9 @@
|
||||
}
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.5.tgz",
|
||||
"integrity": "sha512-ey6EpYv0tEaIbM/nTDOpHciXUvd+ackQrJgEzBwemhZZIWZjcyodqEcrmqDy2BKRTM3a65kKBV4WtLXJDt26SQ=="
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
|
||||
"integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.4.1",
|
||||
@ -22348,40 +22382,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-calendar-heatmap": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/react-calendar-heatmap/-/react-calendar-heatmap-1.8.1.tgz",
|
||||
"integrity": "sha512-4Hbq/pDMJoCPzZnyIWFfHgokLlLXzKyGsDcMgNhYpi7zcKHcvsK9soLEPvhW2dBBqgDrQOSp/uG4wtifaDg4eQ==",
|
||||
"requires": {
|
||||
"memoize-one": "^5.0.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.8.1"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
|
||||
"integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-dev-utils": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-4.2.3.tgz",
|
||||
|
@ -16,6 +16,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||
"@freecodecamp/loop-protect": "^2.2.1",
|
||||
"@freecodecamp/react-bootstrap": "^0.32.3",
|
||||
"@freecodecamp/react-calendar-heatmap": "^1.0.0",
|
||||
"@reach/router": "^1.2.1",
|
||||
"algoliasearch": "^3.35.1",
|
||||
"axios": "^0.19.0",
|
||||
@ -48,7 +49,6 @@
|
||||
"prismjs": "^1.17.1",
|
||||
"query-string": "^6.8.3",
|
||||
"react": "^16.10.2",
|
||||
"react-calendar-heatmap": "^1.8.1",
|
||||
"react-dom": "^16.10.2",
|
||||
"react-final-form": "^6.3.0",
|
||||
"react-ga": "^2.7.0",
|
||||
|
@ -29,10 +29,6 @@ const propTypes = {
|
||||
showTimeLine: PropTypes.bool
|
||||
}),
|
||||
calendar: PropTypes.object,
|
||||
streak: PropTypes.shape({
|
||||
current: PropTypes.number,
|
||||
longest: PropTypes.number
|
||||
}),
|
||||
completedChallenges: PropTypes.array,
|
||||
portfolio: PropTypes.array,
|
||||
about: PropTypes.string,
|
||||
@ -108,7 +104,6 @@ function renderProfile(user) {
|
||||
},
|
||||
calendar,
|
||||
completedChallenges,
|
||||
streak,
|
||||
githubProfile,
|
||||
isLinkedIn,
|
||||
isGithub,
|
||||
@ -150,7 +145,7 @@ function renderProfile(user) {
|
||||
website={website}
|
||||
yearsTopContributor={yearsTopContributor}
|
||||
/>
|
||||
{showHeatMap ? <HeatMap calendar={calendar} streak={streak} /> : null}
|
||||
{showHeatMap ? <HeatMap calendar={calendar} /> : null}
|
||||
{showCerts ? <Certifications username={username} /> : null}
|
||||
{showPortfolio ? <Portfolio portfolio={portfolio} /> : null}
|
||||
{showTimeLine ? (
|
||||
|
@ -110,11 +110,6 @@ function Camper({
|
||||
</p>
|
||||
)}
|
||||
{about && <p className='bio text-center'>{about}</p>}
|
||||
{typeof points === 'number' ? (
|
||||
<p className='text-center points'>
|
||||
{`${points} ${pluralise('point', points !== 1)}`}
|
||||
</p>
|
||||
) : null}
|
||||
{yearsTopContributor.filter(Boolean).length > 0 && (
|
||||
<div>
|
||||
<br />
|
||||
@ -125,6 +120,11 @@ function Camper({
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
{typeof points === 'number' ? (
|
||||
<p className='text-center points'>
|
||||
{`${points} ${pluralise('total point', points !== 1)}`}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CalendarHeatMap from 'react-calendar-heatmap';
|
||||
import CalendarHeatMap from '@freecodecamp/react-calendar-heatmap';
|
||||
import { Row } from '@freecodecamp/react-bootstrap';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import addDays from 'date-fns/add_days';
|
||||
import addMonths from 'date-fns/add_months';
|
||||
@ -10,52 +11,92 @@ import isEqual from 'date-fns/is_equal';
|
||||
import FullWidthRow from '../../helpers/FullWidthRow';
|
||||
import Spacer from '../../helpers/Spacer';
|
||||
|
||||
import 'react-calendar-heatmap/dist/styles.css';
|
||||
import '@freecodecamp/react-calendar-heatmap/dist/styles.css';
|
||||
import './heatmap.css';
|
||||
|
||||
const propTypes = {
|
||||
calendar: PropTypes.object,
|
||||
streak: PropTypes.shape({
|
||||
current: PropTypes.number,
|
||||
longest: PropTypes.number
|
||||
})
|
||||
calendar: PropTypes.object
|
||||
};
|
||||
|
||||
function HeatMap({ calendar, streak }) {
|
||||
const endOfCalendar = startOfDay(Date.now());
|
||||
const startOfCalendar = addMonths(endOfCalendar, -6);
|
||||
const innerPropTypes = {
|
||||
calendarData: PropTypes.array,
|
||||
currentStreak: PropTypes.number,
|
||||
longestStreak: PropTypes.number,
|
||||
pages: PropTypes.array,
|
||||
points: PropTypes.number
|
||||
};
|
||||
|
||||
let calendarData = [];
|
||||
let dayCounter = startOfCalendar;
|
||||
class HeatMapInner extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// create a data point for each day of the calendar period (six months)
|
||||
while (dayCounter <= endOfCalendar) {
|
||||
// this is the format needed for react-calendar-heatmap
|
||||
const newDay = {
|
||||
date: startOfDay(dayCounter),
|
||||
count: 0
|
||||
this.state = {
|
||||
pageIndex: this.props.pages.length - 1
|
||||
};
|
||||
|
||||
calendarData.push(newDay);
|
||||
dayCounter = addDays(dayCounter, 1);
|
||||
this.prevPage = this.prevPage.bind(this);
|
||||
this.nextPage = this.nextPage.bind(this);
|
||||
}
|
||||
|
||||
for (let timestamp of Object.keys(calendar)) {
|
||||
timestamp = Number(timestamp * 1000) || null;
|
||||
if (timestamp) {
|
||||
const index = calendarData.findIndex(day =>
|
||||
isEqual(day.date, startOfDay(timestamp))
|
||||
prevPage() {
|
||||
this.setState(
|
||||
{
|
||||
pageIndex: this.state.pageIndex - 1
|
||||
},
|
||||
() => ReactTooltip.rebuild()
|
||||
);
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
calendarData[index].count++;
|
||||
}
|
||||
}
|
||||
nextPage() {
|
||||
this.setState(
|
||||
{
|
||||
pageIndex: this.state.pageIndex + 1
|
||||
},
|
||||
() => ReactTooltip.rebuild()
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { calendarData, currentStreak, longestStreak, pages } = this.props;
|
||||
const { startOfCalendar, endOfCalendar } = pages[this.state.pageIndex];
|
||||
const title = `${startOfCalendar.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short'
|
||||
})} - ${endOfCalendar.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short'
|
||||
})}`;
|
||||
const dataToDisplay = calendarData.filter(
|
||||
data => data.date >= startOfCalendar && data.date <= endOfCalendar
|
||||
);
|
||||
|
||||
return (
|
||||
<FullWidthRow>
|
||||
<FullWidthRow>
|
||||
<Row className='heatmap-nav'>
|
||||
<button
|
||||
className='heatmap-nav-btn'
|
||||
disabled={!pages[this.state.pageIndex - 1]}
|
||||
onClick={this.prevPage}
|
||||
style={{
|
||||
visibility: pages[this.state.pageIndex - 1] ? 'unset' : 'hidden'
|
||||
}}
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<span>{title}</span>
|
||||
<button
|
||||
className='heatmap-nav-btn'
|
||||
disabled={!pages[this.state.pageIndex + 1]}
|
||||
onClick={this.nextPage}
|
||||
style={{
|
||||
visibility: pages[this.state.pageIndex + 1] ? 'unset' : 'hidden'
|
||||
}}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</Row>
|
||||
<Spacer />
|
||||
|
||||
<CalendarHeatMap
|
||||
classForValue={value => {
|
||||
if (!value || value.count < 1) return 'color-empty';
|
||||
@ -87,27 +128,126 @@ function HeatMap({ calendar, streak }) {
|
||||
'data-tip': `<b>${valueCount}</b> ${dateFormatted}`
|
||||
};
|
||||
}}
|
||||
values={calendarData}
|
||||
values={dataToDisplay}
|
||||
/>
|
||||
<ReactTooltip className='react-tooltip' effect='solid' html={true} />
|
||||
</FullWidthRow>
|
||||
|
||||
<Spacer />
|
||||
<FullWidthRow>
|
||||
<Row>
|
||||
<div className='streak-container'>
|
||||
<span className='streak'>
|
||||
<b>Longest Streak:</b> {streak.longest || 0}
|
||||
<span className='streak' data-testid='longest-streak'>
|
||||
<b>Longest Streak:</b> {longestStreak || 0}
|
||||
</span>
|
||||
<span className='streak'>
|
||||
<b>Current Streak:</b> {streak.current || 0}
|
||||
<span className='streak' data-testid='current-streak'>
|
||||
<b>Current Streak:</b> {currentStreak || 0}
|
||||
</span>
|
||||
</div>
|
||||
</FullWidthRow>
|
||||
<Spacer />
|
||||
</Row>
|
||||
<hr />
|
||||
</FullWidthRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HeatMapInner.propTypes = innerPropTypes;
|
||||
|
||||
const HeatMap = props => {
|
||||
const { calendar } = props;
|
||||
|
||||
/**
|
||||
* the following logic creates the data for the heatmap
|
||||
* from the users calendar and calculates their streaks
|
||||
*/
|
||||
|
||||
// create array of timestamps and turn into milliseconds
|
||||
const timestamps = Object.keys(calendar).map(stamp => stamp * 1000);
|
||||
const startOfTimestamps = startOfDay(new Date(timestamps[0]));
|
||||
let endOfCalendar = startOfDay(Date.now());
|
||||
let startOfCalendar;
|
||||
|
||||
// creates pages for heatmap
|
||||
let pages = [];
|
||||
|
||||
do {
|
||||
startOfCalendar = addDays(addMonths(endOfCalendar, -6), 1);
|
||||
|
||||
const newPage = {
|
||||
startOfCalendar: startOfCalendar,
|
||||
endOfCalendar: endOfCalendar
|
||||
};
|
||||
|
||||
pages.push(newPage);
|
||||
|
||||
endOfCalendar = addDays(startOfCalendar, -1);
|
||||
} while (startOfTimestamps < startOfCalendar);
|
||||
|
||||
pages.reverse();
|
||||
|
||||
let calendarData = [];
|
||||
let dayCounter = pages[0].startOfCalendar;
|
||||
|
||||
// create an object for each day of the calendar period
|
||||
while (dayCounter <= pages[pages.length - 1].endOfCalendar) {
|
||||
// this is the format needed for react-calendar-heatmap
|
||||
const newDay = {
|
||||
date: startOfDay(dayCounter),
|
||||
count: 0
|
||||
};
|
||||
|
||||
calendarData.push(newDay);
|
||||
dayCounter = addDays(dayCounter, 1);
|
||||
}
|
||||
|
||||
let longestStreak = 0;
|
||||
let currentStreak = 0;
|
||||
let lastIndex = -1;
|
||||
|
||||
// add a point to each day with a completed timestamp and calculate streaks
|
||||
timestamps.forEach(stamp => {
|
||||
const index = calendarData.findIndex(day =>
|
||||
isEqual(day.date, startOfDay(stamp))
|
||||
);
|
||||
|
||||
if (index >= 0) {
|
||||
// add one point for today
|
||||
calendarData[index].count++;
|
||||
|
||||
// if timestamp is on a new day, deal with streaks
|
||||
if (index !== lastIndex) {
|
||||
// if yesterday has points
|
||||
if (calendarData[index - 1] && calendarData[index - 1].count > 0) {
|
||||
currentStreak++;
|
||||
} else {
|
||||
currentStreak = 1;
|
||||
}
|
||||
|
||||
if (currentStreak > longestStreak) {
|
||||
longestStreak = currentStreak;
|
||||
}
|
||||
}
|
||||
|
||||
lastIndex = index;
|
||||
}
|
||||
});
|
||||
|
||||
// if today has no points
|
||||
if (
|
||||
calendarData[calendarData.length - 1] &&
|
||||
calendarData[calendarData.length - 1].count === 0
|
||||
) {
|
||||
currentStreak = 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<HeatMapInner
|
||||
calendarData={calendarData}
|
||||
currentStreak={currentStreak}
|
||||
longestStreak={longestStreak}
|
||||
pages={pages}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
HeatMap.displayName = 'HeatMap';
|
||||
HeatMap.propTypes = propTypes;
|
||||
|
||||
|
@ -6,23 +6,27 @@ import { render } from '@testing-library/react';
|
||||
|
||||
import HeatMap from './HeatMap';
|
||||
|
||||
// offset is used to shift the dates so that the calendar renders (for testing
|
||||
// purposes only) the same way in each timezone.
|
||||
const offset = new Date().getTimezoneOffset() * 60;
|
||||
const date1 = 1580497504 + offset;
|
||||
const date2 = 1580597504 + offset;
|
||||
const date3 = 1580729769 + offset;
|
||||
|
||||
const props = {
|
||||
calendar: {
|
||||
1580393017: 1,
|
||||
1580397504: 1
|
||||
},
|
||||
streak: {
|
||||
current: 2,
|
||||
longest: 2
|
||||
}
|
||||
calendar: {}
|
||||
};
|
||||
|
||||
props.calendar[date1] = 1;
|
||||
props.calendar[date2] = 1;
|
||||
props.calendar[date3] = 1;
|
||||
|
||||
let dateNowMockFn;
|
||||
|
||||
beforeEach(() => {
|
||||
dateNowMockFn = jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => 1580729769714);
|
||||
.mockImplementation(() => 1580729769714 + offset * 1000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -34,4 +38,18 @@ describe('<HeatMap/>', () => {
|
||||
const { container } = render(<HeatMap {...props} />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('calculates the correct longest streak', () => {
|
||||
const { getByTestId } = render(<HeatMap {...props} />);
|
||||
expect(getByTestId('longest-streak').textContent).toContain(
|
||||
'Longest Streak: 2'
|
||||
);
|
||||
});
|
||||
|
||||
it('calculates the correct current streak', () => {
|
||||
const { getByTestId } = render(<HeatMap {...props} />);
|
||||
expect(getByTestId('current-streak').textContent).toContain(
|
||||
'Current Streak: 1'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -9,11 +9,30 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
class="col-sm-8 col-sm-offset-2 col-xs-12"
|
||||
>
|
||||
<div
|
||||
class="row"
|
||||
class="heatmap-nav row"
|
||||
>
|
||||
<button
|
||||
class="heatmap-nav-btn"
|
||||
disabled=""
|
||||
style="visibility: hidden;"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<span>
|
||||
Aug 2019 - Feb 2020
|
||||
</span>
|
||||
<button
|
||||
class="heatmap-nav-btn"
|
||||
disabled=""
|
||||
style="visibility: hidden;"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="col-sm-8 col-sm-offset-2 col-xs-12"
|
||||
>
|
||||
class="spacer"
|
||||
style="padding: 15px 0px; height: 1px;"
|
||||
/>
|
||||
<svg
|
||||
class="react-calendar-heatmap"
|
||||
viewBox="0 0 296 90"
|
||||
@ -2168,9 +2187,9 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
<title />
|
||||
</rect>
|
||||
<rect
|
||||
class="color-scale-1"
|
||||
class="color-empty"
|
||||
currentItem="false"
|
||||
data-tip="<b>2 points</b> on Jan 30, 2020"
|
||||
data-tip="<b>No points</b> on Jan 30, 2020"
|
||||
height="10"
|
||||
width="10"
|
||||
x="0"
|
||||
@ -2179,9 +2198,9 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
<title />
|
||||
</rect>
|
||||
<rect
|
||||
class="color-empty"
|
||||
class="color-scale-1"
|
||||
currentItem="false"
|
||||
data-tip="<b>No points</b> on Jan 31, 2020"
|
||||
data-tip="<b>1 point</b> on Jan 31, 2020"
|
||||
height="10"
|
||||
width="10"
|
||||
x="0"
|
||||
@ -2190,9 +2209,9 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
<title />
|
||||
</rect>
|
||||
<rect
|
||||
class="color-empty"
|
||||
class="color-scale-1"
|
||||
currentItem="false"
|
||||
data-tip="<b>No points</b> on Feb 1, 2020"
|
||||
data-tip="<b>1 point</b> on Feb 1, 2020"
|
||||
height="10"
|
||||
width="10"
|
||||
x="0"
|
||||
@ -2217,9 +2236,9 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
<title />
|
||||
</rect>
|
||||
<rect
|
||||
class="color-empty"
|
||||
class="color-scale-1"
|
||||
currentItem="false"
|
||||
data-tip="<b>No points</b> on Feb 3, 2020"
|
||||
data-tip="<b>1 point</b> on Feb 3, 2020"
|
||||
height="10"
|
||||
width="10"
|
||||
x="0"
|
||||
@ -2238,23 +2257,19 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
class="__react_component_tooltip place-top type-dark "
|
||||
data-id="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="spacer"
|
||||
style="padding: 15px 0px; height: 1px;"
|
||||
/>
|
||||
<div
|
||||
class="row"
|
||||
>
|
||||
<div
|
||||
class="col-sm-8 col-sm-offset-2 col-xs-12"
|
||||
>
|
||||
<div
|
||||
class="streak-container"
|
||||
>
|
||||
<span
|
||||
class="streak"
|
||||
data-testid="longest-streak"
|
||||
>
|
||||
<b>
|
||||
Longest Streak:
|
||||
@ -2264,20 +2279,16 @@ exports[`<HeatMap/> renders correctly 1`] = `
|
||||
</span>
|
||||
<span
|
||||
class="streak"
|
||||
data-testid="current-streak"
|
||||
>
|
||||
<b>
|
||||
Current Streak:
|
||||
</b>
|
||||
|
||||
2
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="spacer"
|
||||
style="padding: 15px 0px; height: 1px;"
|
||||
/>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,8 +6,16 @@
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.heatmap-nav {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.heatmap-nav-btn {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.react-calendar-heatmap-month-label {
|
||||
color: var(--primary-color);
|
||||
fill: var(--gray-45) !important;
|
||||
}
|
||||
|
||||
.react-calendar-heatmap .color-empty {
|
||||
|
Reference in New Issue
Block a user