diff --git a/client/src/components/daily-coding-challenge/widget.tsx b/client/src/components/daily-coding-challenge/widget.tsx
index f0f3d048e6e..5fe9871b814 100644
--- a/client/src/components/daily-coding-challenge/widget.tsx
+++ b/client/src/components/daily-coding-challenge/widget.tsx
@@ -30,7 +30,7 @@ function DailyCodingChallengeWidget({
block
size='large'
className='map-superblock-link'
- href={`/learn/daily-coding-challenge?date=${getTodayUsCentral()}`}
+ href={`/learn/daily-coding-challenge/${getTodayUsCentral()}`}
>
diff --git a/client/src/components/layouts/default.tsx b/client/src/components/layouts/default.tsx
index b9da69a81fc..4558baf8ad5 100644
--- a/client/src/components/layouts/default.tsx
+++ b/client/src/components/layouts/default.tsx
@@ -114,6 +114,7 @@ interface DefaultLayoutProps extends StateProps, DispatchProps {
showFooter?: boolean;
isChallenge?: boolean;
isDailyChallenge?: boolean;
+ dailyChallengeParam?: string;
usesMultifileEditor?: boolean;
block?: string;
examInProgress: boolean;
@@ -133,6 +134,7 @@ function DefaultLayout({
showFooter = true,
isChallenge = false,
isDailyChallenge = false,
+ dailyChallengeParam,
usesMultifileEditor,
block,
superBlock,
@@ -292,7 +294,9 @@ function DefaultLayout({
{isDailyChallenge ? (
-
+
) : (
isChallenge &&
diff --git a/client/src/components/redirect-daily-challenge-archive.tsx b/client/src/components/redirect-daily-challenge-archive.tsx
new file mode 100644
index 00000000000..a302fc82bc8
--- /dev/null
+++ b/client/src/components/redirect-daily-challenge-archive.tsx
@@ -0,0 +1,6 @@
+import { withPrefix } from 'gatsby';
+import createRedirect from './create-redirect';
+
+export default createRedirect(
+ withPrefix('/learn/daily-coding-challenge/archive')
+);
diff --git a/client/src/pages/learn/daily-coding-challenge/[...].tsx b/client/src/pages/learn/daily-coding-challenge/[...].tsx
new file mode 100644
index 00000000000..bb583d185c5
--- /dev/null
+++ b/client/src/pages/learn/daily-coding-challenge/[...].tsx
@@ -0,0 +1,31 @@
+/* eslint-disable filenames-simple/naming-convention */
+import { Router } from '@gatsbyjs/reach-router';
+import { withPrefix } from 'gatsby';
+import React from 'react';
+
+import ShowDailyCodingChallenge from '../../../client-only-routes/show-daily-coding-challenge';
+import RedirectToArchive from '../../../components/redirect-daily-challenge-archive';
+
+const inlineStyles = {
+ minHeight: 0,
+ height: '100%'
+};
+
+function DailyCodingChallengeAll(): JSX.Element {
+ return (
+ // Router adds an element around the editor, messing with the layout because the editor is a flex item
+ // These few inline styles fix it.
+
+
+
+
+
+ );
+}
+
+DailyCodingChallengeAll.displayName = 'DailyCodingChallengeAll';
+export default DailyCodingChallengeAll;
diff --git a/client/src/templates/Challenges/components/daily-challenge-bread-crumb.tsx b/client/src/templates/Challenges/components/daily-challenge-bread-crumb.tsx
index a805f6dd259..f1636db2c12 100644
--- a/client/src/templates/Challenges/components/daily-challenge-bread-crumb.tsx
+++ b/client/src/templates/Challenges/components/daily-challenge-bread-crumb.tsx
@@ -5,21 +5,18 @@ import { Link } from '../../../components/helpers/index';
import './challenge-title.css';
import {
- isValidDateParam,
+ isValidDateString,
formatDisplayDate
} from '../../../components/daily-coding-challenge/helpers';
-function DailyChallengeBreadCrumb(): JSX.Element {
- const dateParam =
- new URLSearchParams(window.location.search).get('date') || '';
- let displayDate = '';
-
- if (isValidDateParam(dateParam)) {
- displayDate = formatDisplayDate(dateParam);
- }
-
+function DailyChallengeBreadCrumb({
+ dailyChallengeParam
+}: {
+ dailyChallengeParam?: string;
+}): JSX.Element | null {
const { t } = useTranslation();
- return (
+
+ return dailyChallengeParam && isValidDateString(dailyChallengeParam) ? (
- );
+ ) : null;
}
DailyChallengeBreadCrumb.displayName = 'DailyChallengeBreadCrumb';
diff --git a/client/utils/gatsby/layout-selector.test.tsx b/client/utils/gatsby/layout-selector.test.tsx
index f9ac06688f4..2b3af30379f 100644
--- a/client/utils/gatsby/layout-selector.test.tsx
+++ b/client/utils/gatsby/layout-selector.test.tsx
@@ -35,7 +35,9 @@ function getComponentNameAndProps(
location: {
pathname
},
- pageContext
+ pageContext,
+ params: { '*': '' },
+ path: ''
}
});
utils.render(
{LayoutReactComponent});
diff --git a/client/utils/gatsby/layout-selector.tsx b/client/utils/gatsby/layout-selector.tsx
index 905493833f4..b2d6f44588e 100644
--- a/client/utils/gatsby/layout-selector.tsx
+++ b/client/utils/gatsby/layout-selector.tsx
@@ -10,6 +10,8 @@ interface LayoutSelectorProps {
data: { challengeNode?: { challenge?: { usesMultifileEditor?: boolean } } };
location: { pathname: string };
pageContext?: { challengeMeta?: { block?: string; superBlock?: string } };
+ params: { '*'?: string };
+ path: string;
};
}
export default function layoutSelector({
@@ -20,8 +22,8 @@ export default function layoutSelector({
location: { pathname }
} = props;
- const isDailyChallenge =
- props.location.pathname === '/learn/daily-coding-challenge';
+ const isDailyChallenge = props.path === '/learn/daily-coding-challenge/*';
+ const dailyChallengeParam = props.params['*'];
const isChallenge = !!props.pageContext?.challengeMeta || isDailyChallenge;
@@ -42,6 +44,7 @@ export default function layoutSelector({
showFooter={false}
isChallenge={true}
isDailyChallenge={isDailyChallenge}
+ dailyChallengeParam={dailyChallengeParam}
usesMultifileEditor={
props.data?.challengeNode?.challenge?.usesMultifileEditor
}
diff --git a/e2e/daily-coding-challenge.spec.ts b/e2e/daily-coding-challenge.spec.ts
index f8c66f717d1..41d25fbaf20 100644
--- a/e2e/daily-coding-challenge.spec.ts
+++ b/e2e/daily-coding-challenge.spec.ts
@@ -66,7 +66,7 @@ const mockDaysInMonth = new Date(year, month, 0).getDate();
test.describe('Daily Coding Challenges', () => {
test('should show not found page for invalid date', async ({ page }) => {
- await page.goto('/learn/daily-coding-challenge?date=invalid-date');
+ await page.goto('/learn/daily-coding-challenge/invalid-date');
await expect(
page.getByText(/daily coding challenge not found\./i)
).toBeVisible();
@@ -83,7 +83,7 @@ test.describe('Daily Coding Challenges', () => {
});
});
- await page.goto('/learn/daily-coding-challenge?date=2025-01-01');
+ await page.goto('/learn/daily-coding-challenge/2025-01-01');
await expect(
page.getByText(/daily coding challenge not found\./i)
).toBeVisible();
@@ -98,7 +98,7 @@ test.describe('Daily Coding Challenges', () => {
});
});
- await page.goto('/learn/daily-coding-challenge?date=2025-01-01');
+ await page.goto('/learn/daily-coding-challenge/2025-01-01');
await expect(
page.getByText(/daily coding challenge not found\./i)
).toBeVisible();
@@ -115,7 +115,7 @@ test.describe('Daily Coding Challenges', () => {
});
});
- await page.goto('/learn/daily-coding-challenge?date=2025-06-27');
+ await page.goto('/learn/daily-coding-challenge/2025-06-27');
await expect(
page.getByText(/daily coding challenge not found\./i)
).toBeVisible();
@@ -132,7 +132,7 @@ test.describe('Daily Coding Challenges', () => {
});
});
- await page.goto(`/learn/daily-coding-challenge?date=${todayUsCentral}`);
+ await page.goto(`/learn/daily-coding-challenge/${todayUsCentral}`);
await expect(page.getByText('Test title')).toBeVisible();
@@ -171,14 +171,35 @@ test.describe('Daily Coding Challenges', () => {
'# Python seed code'
);
- await page.goto(`/learn/daily-coding-challenge?date=${todayUsCentral}`);
+ await page.goto(`/learn/daily-coding-challenge/${todayUsCentral}`);
await expect(page.getByRole('button', { name: /main.py/i })).toBeVisible();
});
});
test.describe('Daily Coding Challenge Archive', () => {
- test('should load and display the calendar', async ({ page }) => {
+ test('/learn/daily-coding-challenge should redirect to archive', async ({
+ page
+ }) => {
+ await page.goto('/learn/daily-coding-challenge');
+ await expect(page).toHaveURL('/learn/daily-coding-challenge/archive');
+ });
+
+ test('/learn/daily-coding-challenge/ should redirect to archive', async ({
+ page
+ }) => {
+ await page.goto('/learn/daily-coding-challenge/');
+ await expect(page).toHaveURL('/learn/daily-coding-challenge/archive');
+ });
+
+ test('/learn/daily-coding-challenge/path-1/path2 should redirect to archive', async ({
+ page
+ }) => {
+ await page.goto('/learn/daily-coding-challenge/path-1/path2');
+ await expect(page).toHaveURL('/learn/daily-coding-challenge/archive');
+ });
+
+ test('archive should load and display the calendar', async ({ page }) => {
await page.route(allRouteRe, async route => {
await route.fulfill({
status: 200,