Skip to content

refactor: useColors composable for charts#2696

Merged
serhalp merged 10 commits intomainfrom
refactor-chart-files
May 9, 2026
Merged

refactor: useColors composable for charts#2696
serhalp merged 10 commits intomainfrom
refactor-chart-files

Conversation

@graphieros
Copy link
Copy Markdown
Contributor

@graphieros graphieros commented May 9, 2026

This refactors the useColors composable to remove code repetition in chart components by placing used css variables directly into the composable. Also adds a test for the composable.

While I was at it, I also fixed another regression that persisted the state of the zoom when granularity or date ranges are changed in download charts. While keeping the zoom state is necessary when mutating the tooltip position dynamically, it must however be refreshed when the number of datapoints changes. To test this:

  • load a downloads chart (modal), switch to daily granularity, the zoom should be reset (meaning: all the range is visible)
  • refresh the page, the chart modal opens on the same date range selection and granularity, and the zoom is also reset

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 9, 2026

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

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment May 9, 2026 0:09am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview May 9, 2026 0:09am
npmx-lunaria Ignored Ignored May 9, 2026 0:09am

Request Review

@graphieros graphieros marked this pull request as draft May 9, 2026 09:04
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors color token resolution: removes configurable useCssVariables and introduces a fixed-set useColors composable. Seven chart components are migrated to useColors. Old tests removed and new unit tests added to cover useColors behaviour across client/server scenarios.

Changes

useColors Composable Refactoring

Layer / File(s) Summary
Composable Contract
app/composables/useColors.ts
Replaces configurable useCssVariables(variables, options) with fixed-set useColors(element, options) that reads --accent, --bg, --bg-elevated, --bg-subtle, --border, --border-hover, --border-subtle, --fg, --fg-muted, --fg-subtle and maps them to reactive camelCase properties. Adds element resolution, fallback to document.documentElement, and optional mutation/resize observation.
Chart Component Migration
app/components/Chart/SplitSparkline.vue, app/components/Compare/FacetBarChart.vue, app/components/Compare/FacetScatterChart.vue, app/components/Package/TimelineChart.vue, app/components/Package/TrendsChart.vue, app/components/Package/VersionDistribution.vue, app/components/Package/WeeklyDownloadStats.vue
All seven chart components switch from useCssVariables([...], options) to useColors(rootEl) for colour sourcing. Imports updated; initialisation simplified. Existing chart config, watermark selection, skeleton styling, tooltip colours, and pattern rendering remain unchanged.
TrendsChart Zoom / Rekeying
app/components/Package/TrendsChart.vue
Introduces keepZoomState reactive flag, binds chartConfig.zoom.keepState to it, adds resetZoom() that toggles state and calls chartRef.resetZoom(), invokes reset on mount and on selectedGranularity, startDate, or endDate changes.
Test Suite Replacement
test/nuxt/composables/use-colors.spec.ts, test/unit/app/composables/use-colors.spec.ts
Old useCssVariables nuxt test removed. New Vitest suite for useColors validates observer wiring, resize observe attachment, no-observer default behaviour, and safe handling when window/document are unavailable.

Sequence Diagram(s)

sequenceDiagram
  participant Watcher as selectedGranularity/startDate/endDate watcher
  participant Reset as resetZoom()
  participant Chart as chartRef
  Watcher->>Reset: trigger resetZoom()
  Reset->>Reset: set keepZoomState = false
  Reset->>Chart: chartRef.resetZoom()
  Reset->>Reset: restore keepZoomState = true (delayed)
Loading

Possibly related PRs

Suggested reviewers

  • userquin
  • MatteoGabriele
  • ghostdevv
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: refactoring the useColors composable for charts to reduce code duplication.
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.
Description check ✅ Passed The pull request description accurately describes the changeset: refactoring the useColors composable to reduce repetition in chart components and adding a test, plus fixing a zoom state regression in download charts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-chart-files

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 and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

❌ Patch coverage is 84.84848% with 5 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/composables/useColors.ts 76.92% 3 Missing ⚠️
app/components/Package/TrendsChart.vue 92.85% 1 Missing ⚠️
app/components/Package/VersionDistribution.vue 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/composables/useColors.ts`:
- Line 1: The computed color map in useColors.ts only depends on the element ref
so DOM mutations (CSS variable changes) don't invalidate the cache; add a
Ref<number> called recomputeToken, include recomputeToken in the computed
getter's dependencies, and increment recomputeToken.value inside the observer
callbacks (the ones that currently read colors.value) so that changes from
watchHtmlAttributes or watchResize force recomputation; update references to
colors.value usage to ensure the callbacks still read colors if needed but also
trigger recomputeToken increments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 64b19e40-ed13-4364-91e4-b5aa8f1c3aac

📥 Commits

Reviewing files that changed from the base of the PR and between 07a192c and 577c9d9.

📒 Files selected for processing (10)
  • app/components/Chart/SplitSparkline.vue
  • app/components/Compare/FacetBarChart.vue
  • app/components/Compare/FacetScatterChart.vue
  • app/components/Package/TimelineChart.vue
  • app/components/Package/TrendsChart.vue
  • app/components/Package/VersionDistribution.vue
  • app/components/Package/WeeklyDownloadStats.vue
  • app/composables/useColors.ts
  • test/nuxt/composables/use-colors.spec.ts
  • test/unit/app/composables/use-colors.spec.ts
💤 Files with no reviewable changes (1)
  • test/nuxt/composables/use-colors.spec.ts

Comment thread app/composables/useColors.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/unit/app/composables/use-colors.spec.ts`:
- Around line 41-42: The test accesses
useResizeObserverMock.mock.calls?.[0]?.[1] as resizeCallback and invokes it
without asserting it exists; add an explicit check for the callback before
calling it (e.g., assert/expect that resizeCallback is defined/not null) and
only then invoke resizeCallback() to ensure a clear failure message if the mock
didn't record a second argument; update the test around the resizeCallback
retrieval to perform this existence assertion for useResizeObserverMock and
resizeCallback before the expect(() => resizeCallback()).not.toThrow() call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e51c5ff1-52bd-4ed2-889e-08dff4b15751

📥 Commits

Reviewing files that changed from the base of the PR and between 577c9d9 and e9db359.

📒 Files selected for processing (2)
  • app/composables/useColors.ts
  • test/unit/app/composables/use-colors.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/composables/useColors.ts

Comment thread test/unit/app/composables/use-colors.spec.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/components/Package/TrendsChart.vue`:
- Around line 1650-1653: The current watcher on normalisedDataset causes zoom
resets for any dataset change (filters, smoothing, prediction) rather than only
when the visible date range or granularity changes; change the watcher to
observe the specific reactive sources that represent the chart's date range and
granularity (e.g., startDate/endDate or dateRange and selectedGranularity)
instead of normalisedDataset, keep the isMounted.value check and await
resetZoom() inside the new watcher, and remove the old
watch([normalisedDataset], ...) so zoom is only reset when those
date/granularity refs change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 669f9c49-2af2-492f-b12d-3ab416b17e04

📥 Commits

Reviewing files that changed from the base of the PR and between e1d29f4 and 3e659de.

📒 Files selected for processing (1)
  • app/components/Package/TrendsChart.vue

Comment thread app/components/Package/TrendsChart.vue Outdated
@graphieros graphieros marked this pull request as ready for review May 9, 2026 12:07
@graphieros graphieros added the needs review This PR is waiting for a review from a maintainer label May 9, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/components/Package/TrendsChart.vue (1)

1631-1646: 💤 Low value

Confusing alias and a likely-redundant mount-time resetZoom.

Two small clarity/efficiency points on this block:

  1. The alias resetZoomState is the start() of a useTimeoutFn whose callback restores keepZoomState to true. Read against the sibling resetZoom() (which actively resets chart zoom), the call site resetZoomState() reads as if it were resetting the zoom state, when it's actually scheduling the restoration of the keep-state flag. A name like scheduleKeepZoomStateRestore (or restoreKeepZoomStateLater) would match its real effect.

  2. onMounted(resetZoom) runs at first mount where there is no user zoom to clear, so the chartRef.value?.resetZoom?.() call is effectively a no-op, but it still toggles keepZoomState to false for ~1s after mount and forces a chartConfig recomputation. The watcher on [selectedGranularity, startDate, endDate] already covers every subsequent reset case. Consider dropping the mount-time call, or gating it behind a "have we already rendered with zoom" check.

♻️ Proposed naming + dropping the mount-time call
-const { start: resetZoomState } = useTimeoutFn(
+const { start: scheduleKeepZoomStateRestore } = useTimeoutFn(
   () => {
     keepZoomState.value = true
   },
   1000,
   { immediate: false },
 )

 async function resetZoom() {
   keepZoomState.value = false
   await nextTick()
   chartRef.value?.resetZoom?.()
-  resetZoomState()
+  scheduleKeepZoomStateRestore()
 }

-onMounted(resetZoom)
-
 watch([selectedGranularity, startDate, endDate], async () => {
   if (!isMounted.value) return
   await resetZoom()
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/Package/TrendsChart.vue` around lines 1631 - 1646, Rename the
confusing alias from the useTimeoutFn start import (currently assigned to
resetZoomState) to something like scheduleKeepZoomStateRestore and update the
resetZoom() function to call that new name; then remove the onMounted(resetZoom)
call (or gate it so resetZoom only runs when there is an actual user zoom) so we
no longer toggle keepZoomState and force chartConfig recomputation on initial
mount; the relevant symbols to change are useTimeoutFn, the alias currently
named resetZoomState, the resetZoom() function, keepZoomState, chartRef, and the
onMounted call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/components/Package/TrendsChart.vue`:
- Around line 1631-1646: Rename the confusing alias from the useTimeoutFn start
import (currently assigned to resetZoomState) to something like
scheduleKeepZoomStateRestore and update the resetZoom() function to call that
new name; then remove the onMounted(resetZoom) call (or gate it so resetZoom
only runs when there is an actual user zoom) so we no longer toggle
keepZoomState and force chartConfig recomputation on initial mount; the relevant
symbols to change are useTimeoutFn, the alias currently named resetZoomState,
the resetZoom() function, keepZoomState, chartRef, and the onMounted call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: af57ec4d-e19b-4dc7-9de0-706866886671

📥 Commits

Reviewing files that changed from the base of the PR and between 3e659de and bf5719c.

📒 Files selected for processing (1)
  • app/components/Package/TrendsChart.vue

Copy link
Copy Markdown
Member

@serhalp serhalp left a comment

Choose a reason for hiding this comment

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

Very nice!

@serhalp serhalp added this pull request to the merge queue May 9, 2026
Merged via the queue into main with commit 4269128 May 9, 2026
32 checks passed
@serhalp serhalp deleted the refactor-chart-files branch May 9, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs review This PR is waiting for a review from a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants