Skip to content

New design changes for the dashboard - #10

Merged
ekoslow1-creator merged 20 commits into
mainfrom
feature/sc-46687/new-design-changes-for-the-dashboard
Aug 19, 2026
Merged

New design changes for the dashboard#10
ekoslow1-creator merged 20 commits into
mainfrom
feature/sc-46687/new-design-changes-for-the-dashboard

Conversation

@ekoslow1-creator

Copy link
Copy Markdown
Collaborator

Summary

  • Project cards: replaced the flip-card interaction with a click-to-expand modal (ProjectDetailModal) showing every populated field from /api/powered-by (dates, tech used, Sefaria tools, experience, reach, badges, links), plus a live <iframe> preview of the project's own site with graceful loading/timeout fallback. Card front is now just the centered project name.
  • Pagination: the project grid is capped to 24 projects per page with keyboard-accessible Previous/Next controls, resetting to page 1 on search/filter/sort changes.
  • Sort dropdown: a new sort control (A-Z / Year Newest First / Year Oldest First) to the right of the category filter, matching Sefaria's existing sort-dropdown UI pattern. Undated projects (63% of the current live set) always sort to the end, alphabetically, in both date directions.
  • Merged in origin/main's design-feedback PR (Design feedback: subheader, controls layout, pie chart legend spacing #9) partway through, resolving conflicts in index.css.
  • Fixed a couple of UI issues found during manual testing: the controls row overflowing horizontally when the sort label got long, and the sort dropdown panel opening off-screen to the right.

Dropped from scope per explicit privacy decision: creator_email, is_developer, job_title, found_sefaria — none of these are in the live public API; the only source is a local CSV containing real people's email addresses, so nothing PII-bearing ships.

Test plan

  • npm test — 68/68 passing
  • npm run lint — clean
  • Manual smoke test in a browser: click through project cards (modal open/close/live preview), paginate through the grid, try each sort option, confirm the controls row and sort panel render correctly at various widths

🤖 Generated with Claude Code

Sefaria Intern and others added 19 commits August 17, 2026 10:30
Addresses gaps found in the final whole-branch review of the flip-card ->
click-to-expand modal work: keyboard focusability and activation on the
project card, focus management and body-scroll lock in the detail modal,
scheme-checking on outbound links (isSafeUrl), consistent hasValue gating
so empty sections stay hidden, a type guard on rendered tool tags, and a
UTC-anchored formatDate so submission dates don't shift with viewer
timezone.
Card front now shows only the project name; project_desc is already
shown in the detail modal on click.
Reuse isSafeUrl to reject non-http(s) URLs (e.g. javascript:) before
the GitHub hostname check, and widen the GitHub hostname check to
cover www.github.com, gist.github.com, and *.githubusercontent.com,
which serve X-Frame-Options: DENY and would otherwise render as a
blank blocked box. Also remove loading="lazy" from the preview
iframe since it fights the 6-second load timeout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Replace disabled attribute with aria-disabled for keyboard accessibility;
  buttons remain focusable at boundaries, preventing focus loss
- Guard onClick handlers to no-op at boundaries instead of relying on disabled
- Update CSS to use aria-disabled attribute selectors
- Convert pagination container to nav element with aria-label landmark
- Add aria-live="polite" to status span for screen reader announcements

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…hanges-for-the-dashboard

# Conflicts:
#	src/index.css
…ls-row overflow

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The panel opened flush with the button's left edge, which ran off
the right side of the viewport since the sort control is the
rightmost item in the controls row. Anchoring to the right edge
keeps the panel on-screen.
@gitvelocity-reviewer

Copy link
Copy Markdown

📊 Code Quality Score: 63/100

Base Score 63 × ESF 1.0 (952 effective lines, Extra Large tier) = 63

Category Score Factors
🔭 Scope 15/20 16 files modified or created across src/components (Controls.jsx, Dashboard.jsx, Pagination.jsx, ProjectCard.jsx, ProjectDetailModal.jsx, SortDropdown.jsx, SubmissionsTrendChart.jsx), src/utils (pagination.js, projectDetail.js, projectPreview.js, sortProjects.js and their four test files), and src/index.css. Three new user-facing behaviors are introduced: pagination, sort, and a project detail modal.
🏗️ Architecture 12/20 Four pure utility modules are extracted from inline Dashboard.jsx logic: pagination.js, sortProjects.js, projectDetail.js, and projectPreview.js. ProjectCard.jsx drops the flip-card pattern and delegates detail rendering to ProjectDetailModal.jsx. Dashboard.jsx now composes filteredProjects → sortProjects → paginate in a linear pipeline. No new external npm dependencies are introduced.
⚙️ Implementation 14/20 ProjectDetailModal.jsx manages three concurrent useEffect hooks: a 6-second iframe load timeout that transitions previewState through loading/loaded/failed, an Escape keydown listener, and a focus/scroll-lock setup with cleanup that restores the previously focused element. SortDropdown.jsx registers mousedown and keydown listeners on document only while open and tears them down on close. sortProjects.js partitions projects into dated and undated buckets, sorts each independently, and concatenates them. paginate.js clamps the page number to [1, totalPages] before slicing.
⚠️ Risk 8/20 All changes are client-side with no data migration or API contract change. The iframe sandbox uses allow-scripts combined with allow-same-origin, which MDN flags as a sandbox escape vector if the framed page shares the app's origin. The Pagination.jsx buttons use aria-disabled rather than the disabled attribute, so keyboard users can still activate them. The modal has no focus trap, so Tab cycles outside the dialog.
✅ Quality 11/15 pagination.test.js drives paginate across 7 cases including clamping above and below bounds and an empty list. projectDetail.test.js covers formatDate UTC boundary cases (just-after and just-before midnight), hasValue across six type variants, and isSafeUrl against javascript: and data: schemes. projectPreview.test.js covers github.com, www.github.com, gist.github.com, and *.github.io variants. sortProjects.test.js verifies the undated-tailing behavior and the no-mutation guarantee. No test drives ProjectDetailModal.jsx, Pagination.jsx, or SortDropdown.jsx.
🔒 Perf / Security 3/5 isSafeUrl in projectDetail.js blocks non-http/https schemes using the URL constructor, and getPreviewUrl in projectPreview.js blocks GitHub hostnames from being embedded in iframes. The iframe carries referrerPolicy='no-referrer'. The allow-scripts plus allow-same-origin sandbox combination is present without mitigation.

Was this score accurate? 👍 Yes · 👎 No

How this was scored →

Scored by GitVelocity · How are scores calculated?

@yitzhakc yitzhakc left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add doc strings and documentation

Comment thread src/components/Dashboard.jsx Outdated
const matchesCategory =
selectedCategory === 'All' || project.categories.includes(selectedCategory)
const matchesCategory =
selectedCategory === 'All' || project.categories.includes(selectedCategory)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'All' - turn it into a constant instead of a magic string

@@ -0,0 +1,27 @@
function Pagination({ currentPage, totalPages, onPageChange }) {
if (totalPages <= 1) return null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please turn into a constant

…t/category magic strings

Add readability comments explaining non-obvious behavior (focus trap,
preview timeout, iframe sandboxing, outside-click listener lifecycle).
Replace duplicated 'All'/'alphabetical'/'newest'/'oldest' string literals
with named constants (ALL_CATEGORIES, SORT_ALPHABETICAL, SORT_NEWEST,
SORT_OLDEST) to avoid drift across files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ekoslow1-creator
ekoslow1-creator merged commit bfa3153 into main Aug 19, 2026
1 check passed
@gitvelocity-reviewer

Copy link
Copy Markdown

📊 Code Quality Score: 63/100

63 × 1.0 (Extra Large ESF) = 63

Category Score Factors
🔭 Scope 14/20 17 files are modified or created: three new components (Pagination.jsx, ProjectDetailModal.jsx, SortDropdown.jsx), four new utility modules (pagination.js, projectDetail.js, projectPreview.js, sortProjects.js), four test files, and changes to Dashboard.jsx, Controls.jsx, ProjectCard.jsx, categories.js, and index.css. All changes are within the frontend subsystem.
🏗️ Architecture 12/20 ProjectCard.jsx's flip-card pattern is replaced by delegating detail display to ProjectDetailModal.jsx. Filtering, sorting, and pagination logic previously inlined in Dashboard.jsx is extracted into pagination.js, sortProjects.js, projectDetail.js, and projectPreview.js. The magic string 'All' is replaced by the ALL_CATEGORIES sentinel exported from categories.js. No new external package dependencies are introduced.
⚙️ Implementation 14/20 ProjectDetailModal.jsx coordinates three useEffect hooks: focus trapping with activeElement restoration, body scroll lock with overflow cleanup, and a 6-second setTimeout that transitions previewState from 'loading' to 'failed' if onLoad has not fired. getPreviewUrl applies URL parsing and a hostname blocklist covering github.com, *.github.com, github.io, *.github.io, and *.githubusercontent.com. sortProjects partitions input into dated and undated arrays, sorts each independently, and concatenates them. paginate clamps the requested page to [1, totalPages] before slicing. SortDropdown conditionally attaches and removes document-level mousedown and keydown listeners based on isOpen state.
⚠️ Risk 8/20 The card interaction model changes from flip-to-reveal to click-to-modal, which is a visible behavior change for all users of the dashboard. The iframe sandbox attribute uses allow-scripts allow-same-origin allow-forms, permitting script execution inside embedded third-party content. No backend, auth, or data migration paths are touched. The change is reversible by reverting the PR.
✅ Quality 12/15 pagination.test.js covers 7 cases including clamping above and below bounds and the empty-list edge case. projectDetail.test.js covers 12 cases including UTC timezone correctness for formatDate, the full hasValue type matrix (string, array, boolean, number, null, undefined), and isSafeUrl scheme rejection. projectPreview.test.js covers 10 cases including github.com subdomains, gist.github.com, is_buggy, and missing project_link. sortProjects.test.js covers 7 cases including undated-tail ordering, unparseable dates, and a mutation guard. No test drives ProjectDetailModal.jsx, SortDropdown.jsx, or Pagination.jsx.
🔒 Perf / Security 3/5 isSafeUrl uses URL parsing to reject javascript: and data: schemes before any value is rendered as an href. getPreviewUrl applies a hostname blocklist to prevent GitHub repository and gist pages from being iframed. The iframe carries sandbox='allow-scripts allow-same-origin allow-forms' and referrerPolicy='no-referrer'. The 6-second timeout in ProjectDetailModal handles sites that silently refuse iframe embedding without firing an error event.

Was this score accurate? 👍 Yes · 👎 No

How this was scored →

Scored by GitVelocity · How are scores calculated?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants