From ccedfbca29d888b245fbba14847c59cc25fec37d Mon Sep 17 00:00:00 2001 From: wanga102 Date: Tue, 28 Jul 2026 20:28:36 -0400 Subject: [PATCH 1/5] merge from main + added responsive design --- __tests__/app-error-boundary.test.tsx | 58 +++++++ __tests__/setupTests.ts | 1 + __tests__/styleMock.ts | 3 + app/diagram/page.module.css | 50 ++++++- app/diagram/page.tsx | 14 +- app/layout.tsx | 7 +- components/diagram/CAContainerSvgs.tsx | 112 ++++++++++++++ components/diagram/CADiagram.tsx | 122 ++++++++------- .../diagram/ComponentSidebar.module.css | 16 ++ components/layout/AppErrorBoundary.tsx | 56 +++++++ components/layout/AppErrorFallback.module.css | 141 ++++++++++++++++++ jest.config.js | 9 +- package-lock.json | 13 +- package.json | 3 +- tsconfig.test.json | 3 +- 15 files changed, 534 insertions(+), 74 deletions(-) create mode 100644 __tests__/app-error-boundary.test.tsx create mode 100644 __tests__/setupTests.ts create mode 100644 __tests__/styleMock.ts create mode 100644 components/diagram/CAContainerSvgs.tsx create mode 100644 components/layout/AppErrorBoundary.tsx create mode 100644 components/layout/AppErrorFallback.module.css diff --git a/__tests__/app-error-boundary.test.tsx b/__tests__/app-error-boundary.test.tsx new file mode 100644 index 0000000..90c5cf7 --- /dev/null +++ b/__tests__/app-error-boundary.test.tsx @@ -0,0 +1,58 @@ +import { render, screen } from '@testing-library/react' +import AppErrorBoundary from '@/components/layout/AppErrorBoundary' + +function HealthyChild() { + return
Healthy content
+} + +function CrashingChild() { + throw new Error('render failure for test') +} + +describe('AppErrorBoundary', () => { + let consoleErrorSpy: jest.SpyInstance + + beforeEach(() => { + // React logs boundary-caught errors to console during tests. + consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) + }) + + afterEach(() => { + consoleErrorSpy.mockRestore() + }) + + it('renders children normally when no error occurs', () => { + render( + + + + ) + + expect(screen.getByText('Healthy content')).toBeInTheDocument() + expect(screen.queryByRole('heading', { name: /something crashed while rendering/i })).not.toBeInTheDocument() + }) + + it('mounts fallback UI when a child throws during render', () => { + render( + + + + ) + + expect(screen.getByRole('alert')).toBeInTheDocument() + expect(screen.getByRole('heading', { name: /something crashed while rendering/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /try again/i })).toBeInTheDocument() + }) + + it('shows a link to the GitHub issues page in fallback state', () => { + render( + + + + ) + + const issuesLink = screen.getByRole('link', { name: /view issues/i }) + expect(issuesLink).toBeInTheDocument() + expect(issuesLink).toHaveAttribute('href', 'https://github.com/CA-Visualizer-for-Education/cave-learn/issues') + }) +}) diff --git a/__tests__/setupTests.ts b/__tests__/setupTests.ts new file mode 100644 index 0000000..c44951a --- /dev/null +++ b/__tests__/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom' diff --git a/__tests__/styleMock.ts b/__tests__/styleMock.ts new file mode 100644 index 0000000..418f431 --- /dev/null +++ b/__tests__/styleMock.ts @@ -0,0 +1,3 @@ +const styles = new Proxy({}, { get: (_, key) => String(key) }) + +export default styles diff --git a/app/diagram/page.module.css b/app/diagram/page.module.css index 2956af2..3c702f7 100644 --- a/app/diagram/page.module.css +++ b/app/diagram/page.module.css @@ -1,11 +1,30 @@ +.container { + display: flex; + flex-direction: column; + gap: 1.5rem; + min-width: 0; + overflow: visible; +} + .leftCol { flex: 1; - min-width: 0; /* ← lets it shrink below its content size */ + min-width: 0; padding-left: var(--space-6); padding-right: var(--space-6); overflow: hidden; } +.sidebar { + width: 100%; + flex-shrink: 0; + height: auto; + overflow: visible; +} + +.sidebarPaper { + height: 100%; +} + .eyebrow { margin-top: var(--space-8); margin-bottom: var(--space-2); @@ -16,11 +35,38 @@ } .diagramWrap { + flex: 1; + min-height: 0; + min-width: 0; + width: 100%; padding: var(--space-6) var(--space-6) 0; } .legend { height: 90px; width: auto; - margin-bottom: var(--space-4); + margin: 0 auto var(--space-4); + flex-shrink: 0; +} + +@media (min-width: 1280px) { + .container { + flex-direction: row; + align-items: stretch; + gap: 0; + overflow: hidden; + } + + .sidebar { + width: 25%; + height: calc(100vh - var(--navbar-height)); + overflow-x: auto; + overflow-y: hidden; + position: sticky; + top: var(--navbar-height); + } + + .sidebarPaper { + height: 100%; + } } diff --git a/app/diagram/page.tsx b/app/diagram/page.tsx index d67d0f3..4c1ee96 100644 --- a/app/diagram/page.tsx +++ b/app/diagram/page.tsx @@ -14,12 +14,12 @@ import styles from './page.module.css' export default function DiagramPage() { const [selectedId, setSelectedId] = useState(null) - return
+ return
{/* Left: diagram + legend */} -
+

DIAGRAM · COMPONENTS & LAYERS

Click any component to learn what it does.

- +
@@ -28,8 +28,10 @@ export default function DiagramPage() {
{/* Right: component sidebar */} - - - +
+ + + +
} diff --git a/app/layout.tsx b/app/layout.tsx index 4f16a64..6be2dec 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -17,6 +17,7 @@ import type { Metadata } from 'next' import { Bricolage_Grotesque, Plus_Jakarta_Sans, JetBrains_Mono } from 'next/font/google' import Navbar from '@/components/layout/Navbar' +import AppErrorBoundary from '@/components/layout/AppErrorBoundary' import './globals.css' const bricolage = Bricolage_Grotesque({ @@ -48,8 +49,10 @@ export default function RootLayout({ children }: { children: React.ReactNode }) return ( - - {children} + + + {children} + ) diff --git a/components/diagram/CAContainerSvgs.tsx b/components/diagram/CAContainerSvgs.tsx new file mode 100644 index 0000000..1e6ba73 --- /dev/null +++ b/components/diagram/CAContainerSvgs.tsx @@ -0,0 +1,112 @@ +import type { ReactNode } from 'react' +import { CA_LAYERS } from '@/lib/ca-data' + +type ContainerSvgProps = { + children: ReactNode + stroke: string +} + +type LayerLabelProps = { + x: number + y: number + title: string + maxLineLength?: number + textAnchor?: 'start' | 'middle' +} + +function splitTitle(title: string, maxLineLength = 24) { + if (title.length <= maxLineLength) return [title] + + const words = title.split(' ') + const lines: string[] = [] + let currentLine = '' + + for (const word of words) { + const nextLine = currentLine ? `${currentLine} ${word}` : word + if (nextLine.length > maxLineLength && currentLine) { + lines.push(currentLine) + currentLine = word + continue + } + + currentLine = nextLine + } + + if (currentLine) lines.push(currentLine) + return lines +} + +function LayerContainerSvg({ children, stroke }: ContainerSvgProps) { + return ( + + {children} + + ) +} + +function LayerLabel({ x, y, title, maxLineLength, textAnchor = 'start' }: LayerLabelProps) { + const lines = splitTitle(title, maxLineLength) + const lineSpacing = 24 + + return ( + + {lines.map((line, index) => ( + + {line} + + ))} + + ) +} + +export function createCAContainerSvg() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/components/diagram/CADiagram.tsx b/components/diagram/CADiagram.tsx index 5afd68d..123aa20 100644 --- a/components/diagram/CADiagram.tsx +++ b/components/diagram/CADiagram.tsx @@ -1,5 +1,6 @@ 'use client' +import { useEffect, useRef, useState } from 'react' import { CA_COMPONENTS } from '@/lib/ca-data' import { asset } from '@/lib/asset' import { DiagramNode } from './DiagramNode' @@ -11,9 +12,8 @@ interface CADiagramProps { } // Diagram canvas dimensions — must match the SVG viewBox -const W = 894 -const H = 553 -const SCALE = 1.05 +const CANVAS_WIDTH = 894 +const CANVAS_HEIGHT = 553 // Center (x, y) for each node in the 894x553 coordinate space const POSITIONS: Record = { @@ -71,58 +71,78 @@ const ARROWS = [ ] export default function CADiagram({ selectedId, onSelect }: CADiagramProps) { + const containerRef = useRef(null) + const [scale, setScale] = useState(1) + + useEffect(() => { + const container = containerRef.current + if (!container) return + + const updateScaleToFit = () => { + const availableWidth = container.clientWidth + const availableHeight = container.clientHeight + if (availableWidth > 0 && availableHeight > 0) { + setScale(Math.min(availableWidth / CANVAS_WIDTH, availableHeight / CANVAS_HEIGHT)) + } + } + + updateScaleToFit() + const resizeObserver = new ResizeObserver(updateScaleToFit) + resizeObserver.observe(container) + return () => resizeObserver.disconnect() + }, []) + return ( -
-
-
- {/* SVG background */} - +
+
+
+ - {/* Layer labels */} - {[ - { label: 'Interface Adapters', x: 55, y: 40, color: '#236334' }, - { label: 'Application Business Rules', x: 280, y: 40, color: '#941B45' }, - { label: 'Enterprise Business Rules', x: 640, y: 40, color: '#8B6A00' }, - { label: 'Frameworks & Drivers', x: 265, y: 458, color: '#1F4E8A' }, - ].map(({ label, x, y, color }) => ( - {label} - ))} + {[ + { label: 'Interface Adapters', x: 55, y: 40, color: '#236334' }, + { label: 'Application Business Rules', x: 280, y: 40, color: '#941B45' }, + { label: 'Enterprise Business Rules', x: 640, y: 40, color: '#8B6A00' }, + { label: 'Frameworks & Drivers', x: 265, y: 458, color: '#1F4E8A' }, + ].map(({ label, x, y, color }) => ( + {label} + ))} - {/* Arrows */} - {ARROWS.map((arrow, i) => ( - - ))} + {ARROWS.map((arrow, i) => ( + + ))} - {/* Nodes */} - {Object.entries(POSITIONS).map(([id, { x, y }]) => { - const component = CA_COMPONENTS.find(c => c.id === id) - if (!component) return null - return ( -
- onSelect(selectedId === id ? null : id)} - /> -
- ) - })} -
+ {Object.entries(POSITIONS).map(([id, { x, y }]) => { + const component = CA_COMPONENTS.find(c => c.id === id) + if (!component) return null + return ( +
+ onSelect(selectedId === id ? null : id)} + /> +
+ ) + })} +
) diff --git a/components/diagram/ComponentSidebar.module.css b/components/diagram/ComponentSidebar.module.css index bf59e79..9517c9b 100644 --- a/components/diagram/ComponentSidebar.module.css +++ b/components/diagram/ComponentSidebar.module.css @@ -77,3 +77,19 @@ width: 100%; min-width: 0; } + +@media (max-width: 1280px) { + .sidebar { + height: auto; + min-height: 0; + overflow: visible; + padding: var(--space-6); + } + + .sidebarEmpty { + width: 100%; + height: auto; + min-height: 140px; + box-sizing: border-box; + } +} \ No newline at end of file diff --git a/components/layout/AppErrorBoundary.tsx b/components/layout/AppErrorBoundary.tsx new file mode 100644 index 0000000..cc33afe --- /dev/null +++ b/components/layout/AppErrorBoundary.tsx @@ -0,0 +1,56 @@ +'use client' + +import { ErrorBoundary, type FallbackProps } from 'react-error-boundary' +import styles from './AppErrorFallback.module.css' + +const ISSUES_URL = 'https://github.com/CA-Visualizer-for-Education/cave-learn/issues' + +function AppErrorFallback({ error, resetErrorBoundary }: FallbackProps) { + return ( +
+
+
+ + +
+

CAVE Runtime Guard

+

Something crashed while rendering

+
+
+ +
+

+ This view could not finish loading. You can try again now, or open the Issues page and report what happened. +

+ + {process.env.NODE_ENV !== 'production' && ( +
+ Technical details +
{error.message}
+
+ )} + +
+ + + + View Issues + +
+
+
+
+ ) +} + +export default function AppErrorBoundary({ children }: { children: React.ReactNode }) { + return {children} +} \ No newline at end of file diff --git a/components/layout/AppErrorFallback.module.css b/components/layout/AppErrorFallback.module.css new file mode 100644 index 0000000..199dd01 --- /dev/null +++ b/components/layout/AppErrorFallback.module.css @@ -0,0 +1,141 @@ +.shell { + min-height: 100dvh; + display: grid; + place-items: center; + padding: var(--space-6); + background: + radial-gradient(circle at 15% 10%, color-mix(in srgb, var(--color-brand-yellow) 30%, transparent), transparent 42%), + radial-gradient(circle at 85% 25%, color-mix(in srgb, var(--color-brand-blue) 28%, transparent), transparent 46%), + linear-gradient(145deg, var(--color-bg) 0%, color-mix(in srgb, var(--color-bg) 72%, var(--color-surface) 28%) 100%); +} + +.card { + width: min(720px, 100%); + border: 1px solid var(--color-border); + border-radius: var(--radius-card); + background: var(--color-surface); + box-shadow: var(--shadow-raised); + overflow: hidden; +} + +.header { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-5); + border-bottom: 1px solid var(--color-border); + background: linear-gradient( + 90deg, + color-mix(in srgb, var(--color-brand-pink) 12%, var(--color-surface) 88%) 0%, + color-mix(in srgb, var(--color-brand-yellow) 10%, var(--color-surface) 90%) 50%, + color-mix(in srgb, var(--color-brand-blue) 10%, var(--color-surface) 90%) 100% + ); +} + +.icon { + width: 40px; + height: 40px; + border-radius: 999px; + display: grid; + place-items: center; + color: var(--color-error); + background: color-mix(in srgb, var(--color-error) 12%, var(--color-surface) 88%); + border: 1px solid color-mix(in srgb, var(--color-error) 28%, var(--color-border) 72%); + flex-shrink: 0; +} + +.titleWrap { + min-width: 0; +} + +.eyebrow { + font-family: var(--font-mono); + font-size: var(--text-small); + text-transform: uppercase; + letter-spacing: var(--tracking-eyebrow); + color: var(--color-text-mute); +} + +.title { + font-family: var(--font-display); + font-size: clamp(1.3rem, 1rem + 1vw, 1.9rem); + line-height: var(--leading-tight); + margin-top: var(--space-1); +} + +.body { + padding: var(--space-5); +} + +.message { + color: var(--color-text-mute); + margin-bottom: var(--space-5); +} + +.errorDetail { + margin-bottom: var(--space-4); + border: 1px dashed var(--color-border); + border-radius: var(--radius-sm); + padding: var(--space-3); + background: var(--color-surface2); +} + +.errorDetail summary { + cursor: pointer; + color: var(--color-text); + font-weight: 600; +} + +.errorText { + margin-top: var(--space-3); + color: var(--color-text-mute); + font-family: var(--font-mono); + font-size: var(--text-small); + line-height: 1.7; + overflow-x: auto; + white-space: pre-wrap; +} + +.actions { + display: flex; + gap: var(--space-3); + flex-wrap: wrap; +} + +.issuesLink { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 44px; + padding: 0 var(--space-5); + border-radius: var(--radius-pill); + border: 1px solid color-mix(in srgb, var(--color-brand-pink) 42%, var(--color-border) 58%); + color: var(--color-brand-pink); + font-weight: 700; + transition: transform 140ms ease, background-color 140ms ease; +} + +.issuesLink:hover { + transform: translateY(-1px); + background: color-mix(in srgb, var(--color-brand-pink) 10%, var(--color-surface) 90%); +} + +.issuesLink:focus-visible { + outline: 2px solid var(--color-brand-pink); + outline-offset: 2px; +} + +@media (max-width: 640px) { + .header, + .body { + padding: var(--space-4); + } + + .actions { + flex-direction: column; + } + + .actions > * { + width: 100%; + } +} \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 7c1448b..e72a3de 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,17 +1,20 @@ export default { preset: "ts-jest/presets/default-esm", - testEnvironment: "node", - extensionsToTreatAsEsm: [".ts"], + testEnvironment: "jsdom", + extensionsToTreatAsEsm: [".ts", ".tsx"], testMatch: ["**/__tests__/**/*.test.{ts,tsx}"], transform: { - "^.+\\.ts$": ["ts-jest", { + "^.+\\.tsx?$": ["ts-jest", { tsconfig: "tsconfig.test.json", useESM: true, }], }, moduleNameMapper: { + "^@/(.*)$": "/$1", + "^.+\\.(css|sass|scss)$": "/__tests__/styleMock.ts", "^(\\.{1,2}/.*)\\.js$": "$1" }, + setupFilesAfterEnv: ["/__tests__/setupTests.ts"], testPathIgnorePatterns: [ "/node_modules/", "/frontend/" diff --git a/package-lock.json b/package-lock.json index a871864..5b71517 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,7 @@ "next": "^15.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-icons": "^5.7.0", - "react-toastify": "^11.1.0" + "react-error-boundary": "^6.1.2" }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", @@ -9043,13 +9042,13 @@ "react": "^19.2.6" } }, - "node_modules/react-icons": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", - "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", + "node_modules/react-error-boundary": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.2.tgz", + "integrity": "sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==", "license": "MIT", "peerDependencies": { - "react": "*" + "react": "^18.0.0 || ^19.0.0" } }, "node_modules/react-is": { diff --git a/package.json b/package.json index e933aee..54a90c8 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,7 @@ "next": "^15.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-icons": "^5.7.0", - "react-toastify": "^11.1.0" + "react-error-boundary": "^6.1.2" }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", diff --git a/tsconfig.test.json b/tsconfig.test.json index 64b0e15..7a760a1 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,7 +3,8 @@ "compilerOptions": { "rootDir": ".", "noEmit": true, + "jsx": "react-jsx", "types": ["node", "@types/jest"] }, - "include": ["src/**/*", "__tests__/**/*"] + "include": ["**/*.ts", "**/*.tsx", "__tests__/**/*"] } \ No newline at end of file From a37d0bfccb78ecb26a089187fa616d3d596b7ec5 Mon Sep 17 00:00:00 2001 From: wanga102 Date: Tue, 28 Jul 2026 20:30:56 -0400 Subject: [PATCH 2/5] fixes --- app/diagram/page.module.css | 2 +- .../diagram/ComponentSidebar.module.css | 2 +- package-lock.json | 122 +----------------- 3 files changed, 3 insertions(+), 123 deletions(-) diff --git a/app/diagram/page.module.css b/app/diagram/page.module.css index 3c702f7..b3949d2 100644 --- a/app/diagram/page.module.css +++ b/app/diagram/page.module.css @@ -49,7 +49,7 @@ flex-shrink: 0; } -@media (min-width: 1280px) { +@media (min-width: 900px) { .container { flex-direction: row; align-items: stretch; diff --git a/components/diagram/ComponentSidebar.module.css b/components/diagram/ComponentSidebar.module.css index 9517c9b..9971b9e 100644 --- a/components/diagram/ComponentSidebar.module.css +++ b/components/diagram/ComponentSidebar.module.css @@ -78,7 +78,7 @@ min-width: 0; } -@media (max-width: 1280px) { +@media (max-width: 900px) { .sidebar { height: auto; min-height: 0; diff --git a/package-lock.json b/package-lock.json index 5b71517..6984f8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2832,38 +2832,6 @@ "tslib": "^2.8.0" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", - "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "picocolors": "1.1.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", @@ -2930,14 +2898,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3069,6 +3029,7 @@ "version": "19.2.15", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -4822,17 +4783,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4866,14 +4816,6 @@ "node": ">=0.10.0" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -8116,17 +8058,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8924,44 +8855,6 @@ "node": ">= 0.8.0" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -9073,19 +8966,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-toastify": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.1.0.tgz", - "integrity": "sha512-e9h23x3phN0wbFeB6yovmWp7lobzV4CaCH0LO8nVP6H7Y+3GbcLpIzMm9dJhcp1RXbpyfvjgpfXqO80QAmn7sg==", - "license": "MIT", - "dependencies": { - "clsx": "^2.1.1" - }, - "peerDependencies": { - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", From 7a5bbf0f9083640e63b09a6f257e3e62f2221309 Mon Sep 17 00:00:00 2001 From: wanga102 Date: Tue, 28 Jul 2026 20:36:15 -0400 Subject: [PATCH 3/5] revert package-lock.json --- package-lock.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/package-lock.json b/package-lock.json index 6984f8e..7852d7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2832,6 +2832,38 @@ "tslib": "^2.8.0" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", From ee48336c014d10aee5c70282e2f63e8a67668690 Mon Sep 17 00:00:00 2001 From: wanga102 Date: Tue, 28 Jul 2026 20:40:05 -0400 Subject: [PATCH 4/5] revert package-lock.json --- package-lock.json | 92 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7852d7b..7330c03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2930,6 +2930,14 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3061,7 +3069,6 @@ "version": "19.2.15", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -4815,6 +4822,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4848,6 +4866,14 @@ "node": ">=0.10.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -8090,6 +8116,17 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8887,6 +8924,44 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -8998,6 +9073,19 @@ "dev": true, "license": "MIT" }, + "node_modules/react-toastify": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.1.0.tgz", + "integrity": "sha512-e9h23x3phN0wbFeB6yovmWp7lobzV4CaCH0LO8nVP6H7Y+3GbcLpIzMm9dJhcp1RXbpyfvjgpfXqO80QAmn7sg==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -10907,4 +10995,4 @@ } } } -} +} \ No newline at end of file From f961864e6e0dcb34a6c83285f14dbbad5465713f Mon Sep 17 00:00:00 2001 From: wanga102 Date: Tue, 28 Jul 2026 20:42:16 -0400 Subject: [PATCH 5/5] added to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53376ec..acdb813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Added component `ExerciseBoardSidebar` and its styling - Added component `ExerciseBoardSubheader` and its styling - Added warnings to notify users if they submit an incomplete diagram when doing the fill in the diagram exercise +- Added responsive components to diagram page ### Bug Fix