mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
fix(client): daily challenge calendar (#66202)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Spacer } from '@freecodecamp/ui';
|
||||
import { Link } from '../helpers';
|
||||
import GreenPass from '../../assets/icons/green-pass';
|
||||
import GreenNotCompleted from '../../assets/icons/green-not-completed';
|
||||
import JavaScriptIcon from '../../assets/icons/javascript';
|
||||
import PythonIcon from '../../assets/icons/python';
|
||||
import { formatDisplayDate } from './helpers';
|
||||
import { formatDisplayDate, truncate } from './helpers';
|
||||
|
||||
interface CalendarDayProps {
|
||||
dayNumber: number;
|
||||
@@ -17,33 +16,33 @@ interface CalendarDayProps {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
function Checkmark({ completed }: { completed: boolean }): JSX.Element {
|
||||
return completed ? (
|
||||
function Checkmark({ isCompleted }: { isCompleted: boolean }) {
|
||||
return isCompleted ? (
|
||||
<span
|
||||
className='dc-checkmark dc-small-checkmark completed'
|
||||
className='dc-checkmark completed'
|
||||
data-playwright-test-label='calendar-day-completed'
|
||||
>
|
||||
<GreenPass />
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className='dc-checkmark dc-small-checkmark not-completed'
|
||||
className='dc-checkmark not-completed'
|
||||
data-playwright-test-label='calendar-day-not-completed'
|
||||
>
|
||||
<GreenNotCompleted />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function DailyCodingChallengeCalendarDay({
|
||||
dayNumber,
|
||||
date,
|
||||
isAvailable = false,
|
||||
title,
|
||||
title = '',
|
||||
completedLanguages = [],
|
||||
challengeNumber
|
||||
}: CalendarDayProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const completed = completedLanguages.length > 0;
|
||||
|
||||
// dayNumber = 0 -> render nothing
|
||||
if (dayNumber === 0) return <div></div>;
|
||||
@@ -74,40 +73,29 @@ function DailyCodingChallengeCalendarDay({
|
||||
{dayNumber}
|
||||
</span>
|
||||
|
||||
<div className='dc-number'>#{challengeNumber}</div>
|
||||
<span className='dc-number'>#{challengeNumber}</span>
|
||||
|
||||
<div className='dc-info'>
|
||||
<div className='dc-title'>{title}</div>
|
||||
<div className='dc-title-wrap'>
|
||||
<div className='dc-title'>{truncate(title)}</div>
|
||||
</div>
|
||||
|
||||
{completedLanguages.length === 2 ? (
|
||||
<span className='dc-checkmark dc-big-checkmark completed'>
|
||||
<span className='dc-spacer'>
|
||||
<Spacer size='s' />
|
||||
</span>
|
||||
<GreenPass />
|
||||
</span>
|
||||
) : (
|
||||
<div className='dc-languages'>
|
||||
<hr />
|
||||
<div className='dc-language'>
|
||||
<div className='dc-language-icon'>
|
||||
<JavaScriptIcon />
|
||||
</div>
|
||||
<div className='dc-language-name'>JavaScript</div>
|
||||
<Checkmark
|
||||
completed={completedLanguages.includes('javascript')}
|
||||
/>
|
||||
</div>
|
||||
<Checkmark isCompleted={completed} />
|
||||
|
||||
<div className='dc-language'>
|
||||
<div className='dc-language-icon'>
|
||||
<PythonIcon />
|
||||
</div>
|
||||
<div className='dc-language-name'>Python</div>
|
||||
<Checkmark completed={completedLanguages.includes('python')} />
|
||||
<div className='dc-languages'>
|
||||
{completedLanguages.includes('javascript') && (
|
||||
<div className='dc-language-icon'>
|
||||
<JavaScriptIcon />
|
||||
<span className='sr-only'>JavaScript</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{completedLanguages.includes('python') && (
|
||||
<div className='dc-language-icon'>
|
||||
<PythonIcon />
|
||||
<span className='sr-only'>Python</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
.calendar-day {
|
||||
position: relative;
|
||||
padding: 10px 10px 20px;
|
||||
padding: 5px;
|
||||
min-height: 200px;
|
||||
border: 1px solid var(--tertiary-color);
|
||||
background-color: var(--primary-background);
|
||||
@@ -37,41 +37,61 @@
|
||||
|
||||
.calendar-day-number {
|
||||
position: absolute;
|
||||
font-size: 0.8em;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
color: var(--gray-45);
|
||||
}
|
||||
|
||||
.dc-number {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
color: var(--gray-45);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dc-info {
|
||||
position: relative;
|
||||
padding-top: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dc-title {
|
||||
.dc-title-wrap {
|
||||
width: 100%;
|
||||
min-height: 55px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.dc-title {
|
||||
text-align: center;
|
||||
align-self: center;
|
||||
margin-top: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.dc-checkmark {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.dc-checkmark svg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-bottom: -5px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dc-languages {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.dc-language {
|
||||
font-size: 0.8rem;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.dc-language-icon {
|
||||
@@ -83,25 +103,6 @@
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.dc-small-checkmark {
|
||||
position: relative;
|
||||
/* top: -5px; */
|
||||
}
|
||||
|
||||
.dc-small-checkmark svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.dc-big-checkmark {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dc-big-checkmark svg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.not-available:hover,
|
||||
.not-available:active {
|
||||
cursor: not-allowed;
|
||||
@@ -132,6 +133,17 @@
|
||||
stroke: var(--primary-background);
|
||||
}
|
||||
|
||||
.dc-language-icon svg path {
|
||||
fill: var(--gray-45);
|
||||
stroke: var(--gray-45);
|
||||
}
|
||||
|
||||
.available:hover .dc-language-icon svg path,
|
||||
.available:active .dc-language-icon svg path {
|
||||
fill: var(--quaternary-background);
|
||||
stroke: var(--quaternary-background);
|
||||
}
|
||||
|
||||
.available:hover .not-completed svg circle,
|
||||
.available:active .not-completed svg circle {
|
||||
fill: var(--primary-color);
|
||||
@@ -162,74 +174,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1345px) {
|
||||
.calendar-day-number,
|
||||
.dc-number,
|
||||
.dc-title {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.dc-info hr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
min-height: 170px;
|
||||
}
|
||||
|
||||
.dc-language {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dc-language-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1115px) {
|
||||
.calendar-grid {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
min-height: 125px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dc-title,
|
||||
.dc-info hr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dc-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.dc-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 815px) {
|
||||
.calendar-day {
|
||||
min-height: 100px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dc-number {
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
.calendar-day-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dc-languages {
|
||||
.dc-info {
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dc-big-checkmark svg {
|
||||
.dc-title-wrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dc-checkmark {
|
||||
top: unset;
|
||||
}
|
||||
|
||||
.dc-checkmark svg {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1300px) {
|
||||
.dc-title-wrap {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dc-title {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,3 +33,10 @@ export function formatDisplayDate(dateString: string) {
|
||||
}
|
||||
return format(parsedDate, 'MMMM d, yyyy');
|
||||
}
|
||||
|
||||
export function truncate(str: string, maxLength = 35) {
|
||||
if (str.length <= maxLength) {
|
||||
return str;
|
||||
}
|
||||
return str.slice(0, maxLength) + '...';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user