Skip to content

feat(app): add "What's new" changelog viewer to the Help menu#2684

Merged
kodiakhq[bot] merged 5 commits into
mainfrom
jordansimonovski/changelog-help-menu
Jul 21, 2026
Merged

feat(app): add "What's new" changelog viewer to the Help menu#2684
kodiakhq[bot] merged 5 commits into
mainfrom
jordansimonovski/changelog-help-menu

Conversation

@jordan-simonovski

@jordan-simonovski jordan-simonovski commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Adds a "What's new" item to the Help menu that opens a modal rendering the app's CHANGELOG.md as Markdown, so users can see what shipped without leaving the product. It mirrors the existing Keyboard Shortcuts modal and reuses the app's .hdx-markdown styles and react-markdown.

CleanShot.2026-07-20.at.20.35.29.mp4

What changed

  • New ChangelogModal and a "What's new" Menu.Item in the Help menu. The changelog is fetched lazily on first open and cached.
  • CHANGELOG.md is copied into public/ from next.config.mjs so it ships as a static asset in every build mode.
  • .md is added to the ClickStack export allowlist so the asset survives the export cleanup step.
  • E2E assertion that the menu item opens the modal with Markdown rendered as real HTML (not raw text).

Key decisions

  • Copy the changelog in next.config.mjs, not a package.json pre-script. Yarn 4 does not run arbitrary pre*/post* lifecycle scripts, so a predev/prebuild hook would silently never run. next.config is evaluated by both next dev (Turbopack) and next build (Webpack), so one place covers every mode.
  • Fetch a static asset rather than bundling the changelog. Keeps it lazy-loaded and out of the main JS bundle; the file grows every release.
  • Fetch is basePath-aware via useRouter().basePath. The ClickStack build serves under /clickstack, following the existing precedent in DBSearchPage.tsx.

Background

The app ships in three build modes — dev (Turbopack), production (Webpack), and a ClickStack static export (output: export, basePath: /clickstack). The export runs scripts/prepare-clickhouse-build-export.js, which deletes any file whose extension isn't allowlisted; that's why .md had to be added there.

Impact

User-facing: a new Help-menu entry. No API, schema, or config changes. If the changelog can't be loaded the modal degrades to an "Unable to load" message. Changeset included (@hyperdx/app minor).

Implementation detail
  • The modal uses TanStack Query (enabled: opened, staleTime: Infinity) for load/error/caching rather than a hand-rolled useEffect fetch, matching the codebase convention.
  • The leading # @hyperdx/app package heading is stripped from the Markdown before rendering.
  • public/CHANGELOG.md is generated and gitignored.
  • Verified with tsc --noEmit and the navigation E2E spec (which was re-run after deleting the generated file, confirming the next.config copy path works on a clean tree).

Adds a "What's new" item to the Help menu that opens a modal rendering
CHANGELOG.md as Markdown (react-markdown, existing .hdx-markdown styles),
mirroring the existing Keyboard Shortcuts modal.

The changelog is copied into public/ from next.config.mjs so it ships as a
static asset in every build mode (dev/Turbopack, prod/Webpack, and the
ClickStack static export) without relying on Yarn lifecycle pre-scripts,
which Yarn 4 does not run. The fetch is basePath-aware for the ClickStack
build, where .md is now allow-listed in the export cleanup, and uses
TanStack Query for load/error/caching. Includes an E2E assertion that the
menu item opens the modal with rendered Markdown.
@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6251fd5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Minor
@hyperdx/api Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Jul 21, 2026 3:45pm
hyperdx-storybook Ready Ready Preview, Comment Jul 21, 2026 3:45pm

Request Review

@github-actions github-actions Bot added the review/tier-2 Low risk — AI review + quick human skim label Jul 20, 2026
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Critical-path files (1):
    • docker/hyperdx/Dockerfile

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 9
  • Production lines changed: 127 (+ 58 in test files, excluded from tier calculation)
  • Branch: jordansimonovski/changelog-help-menu
  • Author: jordan-simonovski

To override this classification, remove the review/tier-4 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an in-app changelog viewer from the Help menu. The main changes are:

  • New “What’s new” menu item and changelog modal.
  • Lazy fetch and Markdown rendering for CHANGELOG.md.
  • Build and Docker updates to copy the changelog into public/.
  • ClickStack export cleanup update to keep Markdown assets.
  • E2E coverage for the rendered changelog and fallback state.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
packages/app/src/components/AppNav/ChangelogModal.tsx Adds the modal that fetches, loads, renders, and falls back for the changelog.
packages/app/src/components/AppNav/AppNav.components.tsx Adds the Help menu entry and modal state wiring.
packages/app/next.config.mjs Copies the changelog into the app public assets during config evaluation.
packages/app/scripts/prepare-clickhouse-build-export.js Keeps Markdown files in the ClickStack static export output.
packages/app/tests/e2e/core/navigation.spec.ts Covers opening the changelog modal and the failed-load fallback.

Reviews (5): Last reviewed commit: "Merge branch 'main' into jordansimonovsk..." | Re-trigger Greptile

Comment thread packages/app/tests/e2e/core/navigation.spec.ts Outdated
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 243 passed • 1 skipped • 1046s

Status Count
✅ Passed 243
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found. The build path is sound (packages/app/CHANGELOG.md is tracked and copied in both Dockerfiles; the phase-production-build throw gate correctly aborts a broken production/export build while leaving next start non-fatal), and react-markdown v10 escapes raw HTML by default so there is no XSS surface. The findings below are robustness and test-coverage improvements.

🟡 P2 -- recommended

  • packages/app/src/components/AppNav/ChangelogModal.tsx:28 -- The queryFn guards only on res.ok, so a host serving a 200 SPA/catch-all index.html for a missing /clickstack/CHANGELOG.md bypasses the error branch and renders the app shell HTML as garbled text in the modal.
    • Fix: Reject responses whose Content-Type is text/html (or whose body begins with <!DOCTYPE/<html) before passing the text to ReactMarkdown.
    • ce-adversarial-reviewer, ce-reliability-reviewer, ce-correctness-reviewer
  • packages/app/src/components/AppNav/ChangelogModal.tsx:22 -- The query inherits react-query's default retry (3 attempts, exponential backoff ≈ 7s) because the app-level QueryClient sets no retry default, so a failed load shows only a spinner for several seconds and the 404 fallback E2E asserts within a 10s timeout with a thin margin.
    • Fix: Set retry: false (or a small bounded retry) on the useQuery options so isError surfaces promptly.
    • ce-correctness-reviewer, ce-reliability-reviewer
  • packages/app/src/components/AppNav/ChangelogModal.tsx:33 -- The leading-heading strip text.replace(/^#\s*@hyperdx\/app\s*\n/, '') has no test that can detect a regression, since the E2E only asserts a visible <h2> and the version heading ## 2.31.0 renders as <h2> whether or not the # @hyperdx/app line is stripped.
    • Fix: Add a unit test for ChangelogModal (react-markdown is already jest-mocked) asserting the leading package heading is removed and non-matching input passes through unchanged.
    • ce-testing-reviewer, ce-correctness-reviewer, ce-project-standards-reviewer, ce-kieran-typescript-reviewer, ce-maintainability-reviewer
🔵 P3 nitpicks (5)
  • packages/app/src/components/AppNav/ChangelogModal.tsx:26 -- The queryFn ignores react-query's AbortSignal, so an in-flight fetch is neither cancelled when the modal closes mid-flight nor bounded by a timeout if the response stalls.
    • Fix: Destructure signal from the queryFn context and pass it to fetch, optionally combined with a bounded AbortSignal.timeout.
    • ce-julik-frontend-races-reviewer, ce-reliability-reviewer
  • packages/app/src/components/AppNav/ChangelogModal.tsx:51 -- The loading state is inferred from markdown == null, which conflates pending/disabled with a successfully-fetched-but-empty changelog, rendering an empty ReactMarkdown with no user feedback.
    • Fix: Drive the loader from the query's own isPending/isLoading state instead of the data sentinel.
    • ce-kieran-typescript-reviewer
  • packages/app/src/components/AppNav/ChangelogModal.tsx:56 -- Contributor-authored changelog markdown is rendered to every user, and markdown image syntax is honored, so a merged changeset containing an external image reference causes each user's browser to fetch that host on modal open (tracking/Referer leak, though no XSS).
    • Fix: Disable image rendering via components={{ img: () => null }} or restrict allowed URL schemes/hosts, since the changelog only needs text, headings, lists, and links.
    • ce-adversarial-reviewer
  • packages/app/scripts/prepare-clickhouse-build-export.js:15 -- Allow-listing .md generically retains every .md file in the export output rather than the intended CHANGELOG.md, so future stray .md files silently survive the cleanup.
    • Fix: Narrow the retention to the specific CHANGELOG.md filename.
    • ce-maintainability-reviewer, ce-reliability-reviewer
  • packages/app/next.config.mjs:22 -- The CHANGELOG.md distribution is coupled across four locations (the copy here, the export allow-list, and both Dockerfile COPY steps) with no enforcement, so a new build mode or moved file silently regresses to the "Unable to load" state.
    • Fix: Add a comment cross-referencing all four sites (partly present) and consider a build-level assertion that public/CHANGELOG.md exists.
    • ce-maintainability-reviewer

Reviewers (10): ce-correctness-reviewer, ce-testing-reviewer, ce-maintainability-reviewer, ce-project-standards-reviewer, ce-kieran-typescript-reviewer, ce-julik-frontend-races-reviewer, ce-reliability-reviewer, ce-adversarial-reviewer, ce-agent-native-reviewer, ce-learnings-researcher.

Testing gaps:

  • No unit test exercises the heading-strip regex or the loading/error/success branches of ChangelogModal; the newly added react-markdown jest mock is currently unused.
  • No test covers a 200 response with non-markdown/HTML body (SPA catch-all fallback) — the failure E2E only forces an explicit 404.
  • No test verifies the ClickStack basePath-prefixed fetch URL (/clickstack/CHANGELOG.md) or the build-time copy/allowlist/COPY chain that produces the asset.
  • No test covers close-mid-flight or reopen-after-error, and the loading (spinner) branch is never asserted.

- Fix unit tests: react-markdown v10 is ESM-only and Jest cannot parse it, so
  any suite importing AppNav (via ChangelogModal) failed. Add a jest mock
  mapped through moduleNameMapper, mirroring the existing ky mock.
- Fix the changelog being absent in the production Docker images: the builder
  stages never COPY CHANGELOG.md, so next.config's copyFileSync threw ENOENT
  (silently caught) and the modal 404d at runtime. COPY it into both builder
  stages and make the copy fail loudly during a production build.
- Cover the failure path: add an E2E test that stubs the asset to 404 and
  asserts the fallback message, and make the happy-path assertion settle on
  either outcome so a broken copy fails fast instead of timing out.
@jordan-simonovski

Copy link
Copy Markdown
Contributor Author

Addressed the failing unit check and the review feedback:

Unit tests (was failing). react-markdown@10 is ESM-only and Jest (with transformIgnorePatterns: ['/node_modules/']) can't parse it, so every suite that imports AppNav (now transitively via ChangelogModal) blew up. Added a jest mock mapped through moduleNameMapper, mirroring the existing ky mock. Full app suite passes (137 suites / 2371 tests).

deep-review P1 — changelog missing in the Docker images. The builder stages never COPY CHANGELOG.md, so next.config's copyFileSync threw ENOENT, the catch swallowed it, and the modal 404'd at runtime (dev/Vercel worked because the file's in the checkout). Now copied into the builder stage of both packages/app/Dockerfile and docker/hyperdx/Dockerfile, and the copy throws during phase-production-build so a future regression fails the build loudly instead of shipping broken.

deep-review P2 / Greptile P2 — untested failure path. Added an E2E test that stubs CHANGELOG.md to 404 and asserts the "Unable to load the changelog." fallback renders. Also reworked the happy-path assertion to wait for the fetch to settle into either outcome before asserting success, so a broken copy fails fast and legibly rather than timing out on the heading check.

deep-review P3 nitpicks (e.g. res.ok on an SPA index.html fallback) left as-is for now.

@github-actions github-actions Bot added review/tier-4 Critical — deep review + domain expert sign-off and removed review/tier-2 Low risk — AI review + quick human skim labels Jul 20, 2026

@brandon-pereira brandon-pereira left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM - @elizabetdev has some suggestions to improve it further, but this can be a V1

@kodiakhq
kodiakhq Bot merged commit f5dafcc into main Jul 21, 2026
27 checks passed
@kodiakhq
kodiakhq Bot deleted the jordansimonovski/changelog-help-menu branch July 21, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants