Skip to content

feat(client): let progress choose between the badge and a bar - #2426

Merged
alexander-akait merged 3 commits into
mainfrom
feat/progress-indicator-styles
Sep 26, 2026
Merged

alexander-akait merged 3 commits into
mainfrom
feat/progress-indicator-styles

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

webpack-dev-server's client.progress takes "linear" and "circular" as well as a boolean, and renders a visibly different indicator for each. This package took a boolean only.

That matters for the step where dev-server stops shipping its own client: both values would have arrived here and silently collapsed into one shape. A value that used to mean something quietly meaning nothing is worse than a value that errors — nobody gets a warning, the UI just changes.

  • "circular" is the badge this package has always shown, and stays what true selects, so nothing changes for anyone already using it.
  • "linear" renders a thin bar across the top of the viewport: measured by the percentage the progress payloads carry, and sweeping when there is no percentage to measure — the same choice the badge already makes between its ring and its pulsing dot.

Built through the CSSOM and the Web Animations API with no <style> element, like the rest of the indicator, so a strict style-src still allows it. Choosing a type while something is on screen removes it and lets the next event rebuild in the new shape, since the two are different elements; which sources are mid-build is kept, so the indicator still hides only once they have all finished.

This is independent of #2425 — either can land first.

What kind of change does this PR introduce?

feature

Did you add tests for your changes?

Yes, one end-to-end case: progress=linear is pinned to the top edge and spans the viewport rather than sitting in the badge's bottom-right corner, and its filled part is driven by the reported percentage. Sampled from inside the page, because a build can finish faster than a round trip from the test.

Teeth-checked: forcing the type back to "circular" fails it.

Verified locally: end-to-end 103/103, non-browser 6811 passed, lint and both typechecks clean. test/logging.test.js fails 74/74 on a clean main too, unrelated to this.

Does this PR introduce a breaking change?

No. progress: true and progress: false behave exactly as before; the two new values are additions.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

The progress option's accepted values, once the README documents the client options. Not included here.

Use of AI

AI-assisted (Claude Code). Used to compare this indicator against webpack-dev-server's, implement the linear variant and its test, and verify it by reverting the type selection and confirming the test fails.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Progress indicators can appear as a thin bar across the top of the page, alongside the existing circular badge.
    • Select the linear style with progress: "linear"; true and "circular" continue to show the badge.
    • The linear indicator displays build progress as a percentage and sweeps while progress is indeterminate. With reduced motion enabled, it avoids animation and shows a static full-width bar.
  • Tests

    • Added browser coverage for the linear indicator’s position, progress display, and reduced-motion behavior.

webpack-dev-server's `client.progress` takes `"linear"` and `"circular"`
as well as a boolean, and renders a visibly different indicator for each.
This package took a boolean only, so those two values would have silently
collapsed into one shape — a value that used to mean something quietly
meaning nothing is worse than a value that errors.

`"circular"` is the badge this package has always shown, and stays what
`true` selects, so nothing changes for anyone already using it.
`"linear"` renders a thin bar across the top of the viewport, measured by
the percentage the progress payloads carry and sweeping when there is no
percentage to measure — the same choice the badge makes between its ring
and its pulsing dot. Built through the CSSOM and the Web Animations API,
with no `<style>` element, so a strict `style-src` still allows it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
@changeset-bot

changeset-bot Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2a77370

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

This PR includes changesets to release 1 package
Name Type
webpack-dev-middleware 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

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: cad26fdb-14ad-4022-b08f-216395d37c3c

📥 Commits

Reviewing files that changed from the base of the PR and between 9abc39f and 2a77370.

📒 Files selected for processing (2)
  • client-src/indicator.js
  • test/e2e/indicator.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/e2e/indicator.test.js
  • client-src/indicator.js

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 5 remain after this review.


Walkthrough

The progress option now accepts boolean values and the "circular" and "linear" modes. Browser bootstrap selects the corresponding indicator type. The indicator can render a top-edge bar with percentage-based width or a sweep animation. Reduced-motion settings prevent indicator animations and make the linear bar full-width when progress has no numeric percentage. End-to-end tests check the linear bar during a rebuild.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 2a773

The linear option selects the top bar, and the inspected build and motion-preference paths reveal no actionable issue blocking merge.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 9abc3

The new option changes how build progress appears without an identified change to privileged operations or security boundaries. A possible listener-cleanup issue during repeated rebuilds presents limited browser-lifecycle risk.

Retained concerns

  • Low · reliability · inferred: Animation cleanup obtains a new motion-query object rather than retaining the one that owns the listener. If the browser returns distinct objects, repeated rebuilds can leave listeners active after indicator teardown.
Security review details

Security Blast Radius

  • inferred — The new mode’s independently selectable effect is confined to presentation in the browser client; the examined path does not pass it to transport, authorization, or another privileged operation.

Trust Boundaries and Controls

  • observed — The query override reduces mode selection to known values before rendering; event-supplied progress text reaches textContent, while numeric progress determines a clamped width.

Resilience and Maintainability Implications

  • inferred — A reduced-motion change cancels tracked animations but can leave the bar’s animation reference set until a later determinate update or teardown. This affects indicator recovery, not an identified security control.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: the client progress indicator can choose between the existing badge and the new linear bar.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 3 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 576f88fd-6078-412c-9191-1fb4aa5a9516

📥 Commits

Reviewing files that changed from the base of the PR and between cc4946f and 48d9806.

📒 Files selected for processing (4)
  • .changeset/progress-linear.md
  • client-src/index.js
  • client-src/indicator.js
  • test/e2e/indicator.test.js

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 5 remain after this review.

Comment thread client-src/indicator.js Outdated
Review finding. The bar's sweep ran regardless of
`prefers-reduced-motion`, which webpack-dev-server's linear indicator
honors in its own stylesheet — so the shape this PR adds for parity was
missing the part of it that matters most to the people it matters to.

The badge's pulsing dot ignored the preference too, which predates this
branch. Both go through one guard now rather than leaving the older one
wrong: nothing starts while motion is declined, anything already running
stops if the preference changes mid-build, and the listener that watches
for it is dropped when the indicator goes away. Without a sweep the bar
says a build is running by sitting still at full width, so the state is
still visible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: c7a5b616-5d26-4a57-8414-7152a2c914d8

📥 Commits

Reviewing files that changed from the base of the PR and between 48d9806 and 9abc39f.

📒 Files selected for processing (2)
  • client-src/indicator.js
  • test/e2e/indicator.test.js

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 5 remain after this review.

Comment thread client-src/indicator.js Outdated
Comment thread client-src/indicator.js
Two review findings, both in the reduced-motion guard added a commit ago.

`matchMedia` returns a new `MediaQueryList` for every call, so removing the
listener from a freshly built one called `removeEventListener` and changed
nothing: the object that actually held it kept it, one per build, for the
life of the page. Verified in the browser the suite runs rather than from
the specification alone. The query the listener was registered on is kept
and removed from.

Cancelling a sweep left `barAnimation` pointing at the cancelled animation
and the bar frozen at the 40% a sweep starts from — so it read as progress
that had stalled, and every later call returned early on the stale
reference and could never put it right. Declining motion now clears the
reference and puts the bar at the width it uses when it cannot move.

The first test written for the listener passed against the bug, because it
counted calls rather than what stayed attached; it tracks listeners per
query object now, and fails when the removal is aimed at the wrong one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
@alexander-akait
alexander-akait merged commit 4083960 into main Sep 26, 2026
19 checks passed
@alexander-akait
alexander-akait deleted the feat/progress-indicator-styles branch September 26, 2026 15:27
@codecov

codecov Bot commented Sep 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.05970% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.91%. Comparing base (cc4946f) to head (2a77370).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
client-src/indicator.js 87.69% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2426      +/-   ##
==========================================
- Coverage   96.30%   95.91%   -0.40%     
==========================================
  Files          17       17              
  Lines        1894     1957      +63     
==========================================
+ Hits         1824     1877      +53     
- Misses         70       80      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant