Skip to content

test: add overscan coverage for VirtualList#1428

Open
anchallll02 wants to merge 3 commits into
Karanjot786:mainfrom
anchallll02:feat/virtuallist-overscan
Open

test: add overscan coverage for VirtualList#1428
anchallll02 wants to merge 3 commits into
Karanjot786:mainfrom
anchallll02:feat/virtuallist-overscan

Conversation

@anchallll02

@anchallll02 anchallll02 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Closes #1426

Added test coverage for overscan behavior in VirtualList range computation.

Summary by CodeRabbit

  • Refactor

    • Simplified Skeleton widget animation system with improved control methods.
    • Added new public methods for dynamically adjusting dimensions and animation state.
  • Tests

    • Updated test coverage for Skeleton widget to reflect new behavior and API.

@anchallll02 anchallll02 requested a review from Karanjot786 as a code owner June 12, 2026 11:00
@github-actions github-actions Bot added type:testing +10 pts. Tests. area:widgets @termuijs/widgets labels Jun 12, 2026
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Skeleton widget was refactored from a complex pulse/shimmer rendering model to a simpler character-based animation system. The implementation now maintains width, height, and animation state, conditionally subscribes to a motion timer when motion capabilities are available, and renders by toggling a single character between dim and bright states. Tests were rewritten to verify subscription, animation timing, and lifecycle behavior.

Changes

Skeleton Widget Animation Refactor

Layer / File(s) Summary
Skeleton type contract and imports
packages/widgets/src/feedback/Skeleton.ts
SkeletonOptions interface reduced to variant, intervalMs, and chars fields, dropping the color field and related imports.
Skeleton animation state and lifecycle management
packages/widgets/src/feedback/Skeleton.ts
Constructor initializes width/height/animated/character state and conditionally subscribes to a 600ms motion timer when caps.motion is enabled. New public setters setWidth, setHeight, and setAnimated manage dimension updates and timer subscription lifecycle. Unmount clears the subscription.
Skeleton character rendering
packages/widgets/src/feedback/Skeleton.ts
_renderSelf fills a rectangle by toggling between the configured character (dim) and a hardcoded bright character ( or #) based on animation frame state.
Skeleton behavioral test suite
packages/widgets/src/feedback/Skeleton.test.ts
Test setup mocks motion and timer capabilities. Tests verify timer subscription on construction, correct initial and animated rendering, subscription prevention when motion is disabled, custom character support, dimension setters, and cleanup via unmount.
Export spacing
packages/widgets/src/index.ts
Blank line inserted between Scrollbar and Skeleton exports.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Skeleton
  participant caps.motion
  participant Timer
  User->>Skeleton: constructor(style, {variant, intervalMs, chars})
  Skeleton->>caps.motion: check motion capability
  alt caps.motion enabled AND animated true
    Skeleton->>Timer: subscribe with 600ms callback
    Timer->>Skeleton: toggle _frame every 600ms
    Skeleton->>Skeleton: markDirty()
  end
  User->>Skeleton: setAnimated(true/false)
  Skeleton->>Skeleton: manage timer subscription lifecycle
  User->>Skeleton: setWidth(n) / setHeight(n)
  Skeleton->>Skeleton: update dimensions, markDirty
  User->>Skeleton: unmount()
  Skeleton->>Timer: unsubscribe
  Skeleton->>Skeleton: super.unmount()
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • Karanjot786/TermUI#631: Skeleton widget unit tests overlap directly with the updated test expectations in this PR's test refactor.
  • Karanjot786/TermUI#1118: Prior Skeleton widget implementation changes that directly overlap with this PR's motion gating, timer subscription, and character animation rework.

Suggested Labels

gssoc:approved, area:widgets, type:testing, level:beginner, quality:clean

Suggested Reviewers

  • Karanjot786

Poem

🐰 A skeleton once danced in pulse and shimmer bright,
Now simple frames toggle through the night,
Motion-aware and motion-shy, it knows when to play,
Dim, then bright—a rhythm every terminal can display! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes changes to the Skeleton widget and test file, not VirtualList overscan as claimed. The title is misleading and unrelated to the actual changeset. Update the PR title to accurately reflect the changes made to Skeleton widget, e.g., 'refactor: simplify Skeleton widget implementation and rewrite tests'.
Description check ⚠️ Warning The PR description states it adds test coverage for VirtualList overscan, but the actual changes are only to the Skeleton widget test and implementation files. Update the description to accurately reflect that this PR refactors the Skeleton widget with simplified options and reimplemented tests, not VirtualList overscan coverage.
Linked Issues check ⚠️ Warning The PR is linked to issue #1426 about adding overscan support to VirtualList, but the actual code changes only affect the Skeleton widget and do not implement any VirtualList overscan functionality. Either implement the VirtualList overscan requirements from issue #1426 or unlink this issue and close PR #1428 if the changes are unrelated.
Out of Scope Changes check ⚠️ Warning The entire PR appears out of scope: it modifies Skeleton widget test and implementation, but the PR claims to add VirtualList overscan test coverage per issue #1426. Clarify the PR's actual purpose and align changes with the linked issue, or update the title, description, and issue links to match the actual Skeleton widget refactoring.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/virtuallist-overscan

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

@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: 4

🤖 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 `@packages/widgets/src/feedback/Skeleton.test.ts`:
- Around line 25-29: Remove the placeholder assertion expect(s).toBeDefined()
from the test in packages/widgets/src/feedback/Skeleton.test.ts; keep the
meaningful behavior check by instantiating the class (new Skeleton() or const s
= new Skeleton() if you prefer) only for its side effects and assert that
motion.timerPoolSubscribe has been called (referencing the Skeleton constructor
and motion.timerPoolSubscribe), and if you remove the variable assignment also
remove the unused s identifier to avoid linter warnings.
- Around line 75-80: The test only checks that setWidth/setHeight don't throw;
change it to assert observable behavior by verifying the dimensions and render
output update: create a Skeleton instance, spy on markDirty
(jest.spyOn(Skeleton.prototype, 'markDirty')) to assert it's called when calling
setWidth and setHeight, call s.setWidth(10) and s.setHeight(5), then assert the
instance reports the new sizes (e.g., s.getWidth()/s.getHeight() or public
width/height fields) and that s.render() (or the component's rendered output)
reflects those dimensions; use the Skeleton, setWidth, setHeight, markDirty and
render identifiers to locate the code to update.

In `@packages/widgets/src/feedback/Skeleton.ts`:
- Around line 5-9: The SkeletonOptions interface is missing properties used by
the constructor (opts.width, opts.height, opts.animated, opts.character); update
SkeletonOptions to declare these properties (e.g., width?: number | string,
height?: number | string, animated?: boolean, character?: string) and keep or
reconcile existing fields (variant, intervalMs, chars) as needed so the
constructor's references (in the Skeleton class constructor using opts.width /
opts.height / opts.animated / opts.character) match the interface and satisfy
TypeScript strict mode.
- Line 1: The import statement includes an unused type symbol "Color"; remove
"type Color" from the import list in the Skeleton module so the top line reads
something like import { type Screen, type Style, caps } from '`@termuijs/core`'; —
keep the other imported symbols (Screen, Style, caps) intact and run type checks
to ensure no other references to Color exist in the file.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d7c566f7-f998-42c3-8f5e-f2fcaabe0005

📥 Commits

Reviewing files that changed from the base of the PR and between 07f0dff and 6a1fce0.

📒 Files selected for processing (3)
  • packages/widgets/src/feedback/Skeleton.test.ts
  • packages/widgets/src/feedback/Skeleton.ts
  • packages/widgets/src/index.ts

Comment on lines +25 to 29
it('creates skeleton and subscribes to timer', () => {
const s = new Skeleton();
expect(s).toBeDefined();
expect(motion.timerPoolSubscribe).toHaveBeenCalled();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove placeholder expect(s).toBeDefined() assertion.

The coding guidelines state that expect(widget).toBeDefined() is not a real test. The second assertion (toHaveBeenCalled) is meaningful, so just remove the placeholder.

     it('creates skeleton and subscribes to timer', () => {
         const s = new Skeleton();
-        expect(s).toBeDefined();
         expect(motion.timerPoolSubscribe).toHaveBeenCalled();
     });

As per coding guidelines: "Tests must be real. expect(true).toBe(true) and expect(widget).toBeDefined() are not tests."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('creates skeleton and subscribes to timer', () => {
const s = new Skeleton();
expect(s).toBeDefined();
expect(motion.timerPoolSubscribe).toHaveBeenCalled();
});
it('creates skeleton and subscribes to timer', () => {
const s = new Skeleton();
expect(motion.timerPoolSubscribe).toHaveBeenCalled();
});
🤖 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 `@packages/widgets/src/feedback/Skeleton.test.ts` around lines 25 - 29, Remove
the placeholder assertion expect(s).toBeDefined() from the test in
packages/widgets/src/feedback/Skeleton.test.ts; keep the meaningful behavior
check by instantiating the class (new Skeleton() or const s = new Skeleton() if
you prefer) only for its side effects and assert that motion.timerPoolSubscribe
has been called (referencing the Skeleton constructor and
motion.timerPoolSubscribe), and if you remove the variable assignment also
remove the unused s identifier to avoid linter warnings.

Source: Coding guidelines

Comment on lines +75 to 80
it('setWidth / setHeight trigger markDirty safely', () => {
const s = new Skeleton();

const screen1 = new Screen(10, 1);
skeleton.render(screen1);
// Initially, shimmerPos = 0, bandStart = 0, bandWidth = 2 -> indices 0 and 1 are '#'
expect(screen1.back[0].map(c => c.char).join('')).toBe('##--------');

// Advance shimmerPos by 1 -> bandStart = 1
if (timerCallback) timerCallback();

const screen2 = new Screen(10, 1);
skeleton.render(screen2);
expect(screen2.back[0].map(c => c.char).join('')).toBe('-##-------');
expect(() => s.setWidth(10)).not.toThrow();
expect(() => s.setHeight(5)).not.toThrow();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Test lacks observable behavior assertion.

This test only verifies that methods don't throw, which is a placeholder pattern. It should assert actual behavior — e.g., that render output reflects the new dimensions.

     it('setWidth / setHeight trigger markDirty safely', () => {
-        const s = new Skeleton();
+        const s = new Skeleton({}, { width: 2, height: 1 });
+        s.updateRect({ x: 0, y: 0, width: 10, height: 10 });
 
-        expect(() => s.setWidth(10)).not.toThrow();
-        expect(() => s.setHeight(5)).not.toThrow();
+        s.setWidth(3);
+        s.setHeight(2);
+
+        const screen = new Screen(10, 10);
+        s.render(screen);
+
+        // Verify render uses updated dimensions (3 wide, 2 tall)
+        expect(screen.back[0].slice(0, 4).map(c => c.char).join('')).toBe('░░░ ');
+        expect(screen.back[1].slice(0, 4).map(c => c.char).join('')).toBe('░░░ ');
     });

As per coding guidelines: "Tests must be real... Assert observable behavior or rendered output."

🤖 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 `@packages/widgets/src/feedback/Skeleton.test.ts` around lines 75 - 80, The
test only checks that setWidth/setHeight don't throw; change it to assert
observable behavior by verifying the dimensions and render output update: create
a Skeleton instance, spy on markDirty (jest.spyOn(Skeleton.prototype,
'markDirty')) to assert it's called when calling setWidth and setHeight, call
s.setWidth(10) and s.setHeight(5), then assert the instance reports the new
sizes (e.g., s.getWidth()/s.getHeight() or public width/height fields) and that
s.render() (or the component's rendered output) reflects those dimensions; use
the Skeleton, setWidth, setHeight, markDirty and render identifiers to locate
the code to update.

Source: Coding guidelines

// ─────────────────────────────────────────────────────

import { type Screen, type Style, type Color, caps, prefersReducedMotion } from '@termuijs/core';
import { type Screen, type Style, type Color, caps } from '@termuijs/core';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused Color import.

Color is imported but never used in this file.

-import { type Screen, type Style, type Color, caps } from '`@termuijs/core`';
+import { type Screen, type Style, caps } from '`@termuijs/core`';
🤖 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 `@packages/widgets/src/feedback/Skeleton.ts` at line 1, The import statement
includes an unused type symbol "Color"; remove "type Color" from the import list
in the Skeleton module so the top line reads something like import { type
Screen, type Style, caps } from '`@termuijs/core`'; — keep the other imported
symbols (Screen, Style, caps) intact and run type checks to ensure no other
references to Color exist in the file.

Comment on lines 5 to 9
export interface SkeletonOptions {
/** Animation style: 'pulse' alternates two chars; 'shimmer' scrolls a highlight band */
variant?: 'pulse' | 'shimmer';
/** Animation interval in ms (default: 600) */
intervalMs?: number;
/** Characters for pulse: [dim, bright]. Default: ['░', '▒'] (ASCII fallback: ['-', '#']) */
chars?: [string, string];
/** Color for the bright state (optional) */
color?: Color;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

SkeletonOptions interface does not declare the properties actually used by the constructor.

The interface declares variant, intervalMs, and chars, but the constructor reads opts.width, opts.height, opts.animated, and opts.character. This causes TypeScript errors under strict mode since these properties don't exist on the interface.

🛠️ Proposed fix to align the interface with actual usage
 export interface SkeletonOptions {
-    variant?: 'pulse' | 'shimmer';
-    intervalMs?: number;
-    chars?: [string, string];
+    width?: number;
+    height?: number;
+    animated?: boolean;
+    character?: string;
 }

Also applies to: 20-28

🤖 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 `@packages/widgets/src/feedback/Skeleton.ts` around lines 5 - 9, The
SkeletonOptions interface is missing properties used by the constructor
(opts.width, opts.height, opts.animated, opts.character); update SkeletonOptions
to declare these properties (e.g., width?: number | string, height?: number |
string, animated?: boolean, character?: string) and keep or reconcile existing
fields (variant, intervalMs, chars) as needed so the constructor's references
(in the Skeleton class constructor using opts.width / opts.height /
opts.animated / opts.character) match the interface and satisfy TypeScript
strict mode.

@Karanjot786 Karanjot786 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. PR content does not match its title or stated purpose
    File: packages/widgets/src/feedback/Skeleton.ts, packages/widgets/src/feedback/Skeleton.test.ts
    Problem: The PR title says "test: add overscan coverage for VirtualList" and the description says it closes #1426 by adding VirtualList overscan tests. However, the actual diff contains only changes to Skeleton.ts and Skeleton.test.ts — there is no VirtualList change anywhere in the diff. The branch appears to have been based on or merged with another branch that rewrites the Skeleton widget rather than targeting the correct base.
    Fix: Rebase this branch onto the upstream main, remove the Skeleton changes that don't belong here, and add only the VirtualList overscan tests described in the PR body. Then push again.

  2. The Skeleton changes in the diff contain breaking API issues (same as in PR #1430 from the same author)
    Problem: Even if the Skeleton changes were intentional, the constructor reads opts.width, opts.height, opts.animated, opts.character which are not declared on SkeletonOptions, and the shimmer variant and chars option are silently dropped — a breaking change.

Checklist before re-requesting review:

  • Fix applied
  • Tests pass locally (run: bun vitest run)
  • PR title follows format: type(scope): description

@Karanjot786 Karanjot786 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. PR title and diff are completely mismatched
    Problem: The PR title is test: add overscan coverage for VirtualList, which references VirtualList. However, the diff only touches packages/widgets/src/feedback/Skeleton.ts, Skeleton.test.ts, and packages/widgets/src/index.ts. There are no VirtualList changes at all.
    Fix: Either (a) update the diff to actually add VirtualList overscan tests and revert the Skeleton changes, or (b) retitle the PR to accurately describe what it does. These appear to be the same Skeleton refactor from PR #1430 — please coordinate with that PR to avoid conflicting changes.

  2. Skeleton.ts accesses options not declared in SkeletonOptions
    File: packages/widgets/src/feedback/Skeleton.ts, lines ~20–23
    Problem: opts.width, opts.height, opts.animated, and opts.character are read in the constructor but not declared in the SkeletonOptions interface. This is a type contract violation.
    Fix: Add these fields to SkeletonOptions or remove the Skeleton changes from this PR.

Checklist before re-requesting review:

  • Fix applied
  • Tests pass locally (run: bun vitest run)
  • PR title follows format: type(scope): description

@Karanjot786

Copy link
Copy Markdown
Owner

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. PR title and description do not match the changes
    Problem: The PR title is test: add overscan coverage for VirtualList and the description mentions VirtualList, but the diff only modifies packages/widgets/src/feedback/Skeleton.ts and Skeleton.test.ts. There are no VirtualList changes.
    Fix: Update the PR title and description to accurately describe what was changed: the Skeleton widget refactor (adding setWidth/setHeight/setAnimated methods and updating tests).

  2. Conflicts with PR fix: resolve react-hooks/exhaustive-deps in useFocus #1430
    Problem: This PR modifies the same Skeleton.test.ts file as PR fix: resolve react-hooks/exhaustive-deps in useFocus #1430 (which is already approved). When fix: resolve react-hooks/exhaustive-deps in useFocus #1430 merges, this PR will have a merge conflict.
    Fix: After fix: resolve react-hooks/exhaustive-deps in useFocus #1430 merges, rebase this branch onto main to resolve the conflict, then re-push.

  3. New Skeleton API uses options ({ character: 'X' }) not in the current source
    File: packages/widgets/src/feedback/Skeleton.test.ts, line ~87
    Problem: new Skeleton({}, { character: 'X' }) — the test expects a character option, but the Skeleton source changes in this PR (if any) must add this option and ensure it is documented.
    Fix: Verify that SkeletonOptions in the new Skeleton.ts includes character?: string and that the render method uses it.

  4. Missing newline at end of file
    File: packages/widgets/src/feedback/Skeleton.test.ts
    Fix: Add a trailing newline (already fixed in fix: resolve react-hooks/exhaustive-deps in useFocus #1430 — rebase will bring this in).

Checklist before re-requesting review:

  • Fix PR title and description to accurately describe Skeleton changes
  • Rebase onto main after fix: resolve react-hooks/exhaustive-deps in useFocus #1430 merges
  • Confirm SkeletonOptions includes the character option used in tests
  • Tests pass locally: bun vitest run
  • PR title follows format: type(scope): description

@Karanjot786 Karanjot786 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. PR contents do not match the title/issue
    File: packages/widgets/src/feedback/Skeleton.ts, Skeleton.test.ts, packages/widgets/src/index.ts
    Problem: This PR is titled "test: add overscan coverage for VirtualList" and claims to close #1426, but the diff contains zero changes to VirtualList or overscan code. Instead it rewrites packages/widgets/src/feedback/Skeleton.ts and its tests, removing the shimmer variant and prefersReducedMotion handling, and introducing constructor options (width, height, animated, character) and methods (setWidth, setHeight) that do not exist on SkeletonOptions — this will fail typecheck and is an unrelated regression to an existing widget.
    Fix: Please push the correct branch/diff containing VirtualList overscan test coverage. If this branch was pushed by mistake (wrong local branch), force-push the intended commits to feature/virtuallist-overscan.

Checklist before re-requesting review:

  • Fix 1 applied
  • Tests pass locally (run: bun vitest run)
  • PR title follows format: type(scope): description

@Karanjot786 Karanjot786 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. PR title and body describe VirtualList overscan coverage, but the diff only modifies Skeleton files — no VirtualList code is touched
    File: packages/widgets/src/feedback/Skeleton.ts, packages/widgets/src/feedback/Skeleton.test.ts
    Problem: This PR appears to be submitted on the wrong branch or contains the wrong set of changes. The title says "test: add overscan coverage for VirtualList" but all changes are Skeleton widget refactors.
    Fix: Either retarget this PR to match the actual changes (rename the title to reflect the Skeleton refactor), or push the intended VirtualList overscan tests and revert the Skeleton changes.

  2. Constructor reads properties not declared in SkeletonOptions
    File: packages/widgets/src/feedback/Skeleton.ts, lines ~29-32
    Problem: The constructor accesses opts.width, opts.height, opts.animated, and opts.character but SkeletonOptions only declares variant, intervalMs, and chars. These accesses will always resolve to undefined — so this._width is always 10, this._animated is always true, and this._character always uses the unicode default, regardless of what the caller passes. The test that passes { character: 'X' } will fail if TypeScript strict mode is enabled.
    Fix: Add the missing properties to SkeletonOptions: width?: number, height?: number, animated?: boolean, character?: string. Remove the now-unused variant, intervalMs, chars fields (or keep them for back-compat).

  3. Breaking API removal without a major version bump
    File: packages/widgets/src/feedback/Skeleton.ts
    Problem: The PR removes variant, intervalMs, chars, and color from SkeletonOptions and drops shimmer support entirely. Any callers using new Skeleton({}, { variant: 'shimmer' }) or chars will silently break — there is no deprecation notice or migration path.
    Fix: Either keep backward-compatible fields (even if no-ops with a deprecation comment), or document this as a breaking change and ensure the semver bump reflects it.

  4. Missing newline at end of file
    File: packages/widgets/src/feedback/Skeleton.ts (last line), packages/widgets/src/feedback/Skeleton.test.ts (last line)
    Problem: Both modified files are missing a trailing newline, which violates the project's formatting conventions and may cause linter/formatter failures.
    Fix: Add a newline character at the end of both files.

Checklist before re-requesting review:

  • PR title and branch match the actual changes (VirtualList overscan vs Skeleton refactor)
  • SkeletonOptions interface updated to include all properties accessed in the constructor
  • Trailing newline added to both modified files
  • Tests pass locally (run: bun vitest run packages/widgets/src/feedback/Skeleton.test.ts)

@Karanjot786 Karanjot786 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. PR title and body do not match the actual changes
    File: PR metadata
    Problem: The title says "test: add overscan coverage for VirtualList" and the body references "overscan behavior in VirtualList range computation", but the entire diff is about the Skeleton widget — no VirtualList files are touched. This makes the PR impossible to triage correctly.
    Fix: Update the PR title and body to describe the Skeleton refactor, e.g. "refactor(widgets): simplify Skeleton widget API and update tests"

  2. SkeletonOptions interface is missing the new fields the constructor reads
    File: packages/widgets/src/feedback/Skeleton.ts, lines 10-14 and constructor body ~line 30
    Problem: The constructor reads opts.width, opts.height, opts.animated, and opts.character, but these properties are not declared in SkeletonOptions. They will always resolve to undefined at runtime, so setWidth/setHeight defaults silently come from an unrelated field, animated is always undefined (falsy), and character always falls back to the default — meaning the widget can never be configured via the constructor.
    Fix: Add the missing fields to SkeletonOptions:

    export interface SkeletonOptions {
        variant?: 'pulse' | 'shimmer';
        intervalMs?: number;
        chars?: [string, string];
        width?: number;
        height?: number;
        animated?: boolean;
        character?: string;
    }
  3. Test assertion for custom character will silently pass for the wrong reason
    File: packages/widgets/src/feedback/Skeleton.test.ts — "respects custom character" test
    Problem: new Skeleton({}, { character: 'X' }) passes character via SkeletonOptions, but because character is not in the interface, this._character never gets 'X' — it stays as the default '░'. The rendered output will be '░░░' not 'XXX', so the assertion is wrong. Once the interface is fixed (issue 2 above) this test should pass correctly.
    Fix: Fix the interface first (issue 2), then verify bun vitest run confirms this test passes.

  4. prefersReducedMotion() removed — accessibility regression
    File: packages/widgets/src/feedback/Skeleton.ts, constructor
    Problem: The original code called prefersReducedMotion() from @termuijs/core to honour the OS-level reduced-motion preference. The new code only checks caps.motion, which is a capability flag ("does this terminal support animation"), not an accessibility preference. Users who have enabled Prefers Reduced Motion at the OS level will now still see animation.
    Fix: Restore the prefersReducedMotion() guard. Import it from @termuijs/core and change the condition to:

    if (this._animated && caps.motion && !prefersReducedMotion()) {
  5. Missing EOF newline on two files
    File: packages/widgets/src/feedback/Skeleton.ts (last line), packages/widgets/src/feedback/Skeleton.test.ts (last line)
    Problem: Both files are missing a trailing newline, which causes noisy diffs and fails most linters/editors.
    Fix: Add a newline at the end of each file.

Checklist before re-requesting review:

  • PR title and body updated to describe the Skeleton changes
  • SkeletonOptions interface updated with width, height, animated, character fields
  • Custom character test verified to pass after interface fix
  • prefersReducedMotion() guard restored
  • EOF newlines added to Skeleton.ts and Skeleton.test.ts
  • Tests pass locally (run: bun vitest run)

@Karanjot786

Copy link
Copy Markdown
Owner

Thanks for the contribution. There are issues to fix before this merges.

Issues:

  1. opts.width, opts.height, opts.animated, and opts.character are read in the Skeleton constructor but none of these fields are declared in the SkeletonOptions interface. TypeScript will reject this as 'Property does not exist on type SkeletonOptions'.
    File: packages/widgets/src/feedback/Skeleton.ts
    Fix: Add width?: number, height?: number, animated?: boolean, and character?: string to the SkeletonOptions interface, or remove the private _width/_height/_animated fields and rely on the inherited Widget rect (which is what the original code already did via _getContentRect()).

  2. The test 'respects custom character' constructs new Skeleton({}, { character: 'X' }) but character is not in SkeletonOptions. This will fail to compile (same root cause as above).
    File: packages/widgets/src/feedback/Skeleton.test.ts
    Fix: Fix the SkeletonOptions interface first; once character is a valid option the test will compile. Alternatively, if character is not intended as a public option, rewrite the test to verify the default character instead.

  3. PR title and body claim to add VirtualList overscan coverage, but the diff contains zero VirtualList changes. Every file changed belongs to the Skeleton widget. This is either the wrong branch or the wrong PR description, and the commit type/scope is incorrect (should be refactor(widgets): or feat(widgets):, not test: …VirtualList).
    Fix: Rebase the correct VirtualList overscan commits onto this branch, or update the PR title/body to accurately describe the Skeleton refactor that was actually committed.

Checklist before re-requesting review:

  • All blocking issues fixed
  • Tests pass locally: bun vitest run
  • PR title follows: type(scope): description

@Karanjot786

Copy link
Copy Markdown
Owner

The diff in this PR contains only Skeleton widget tests/changes, but the title and linked issue are about VirtualList overscan. It looks like the wrong branch was pushed.

Please:

  1. Check that you pushed the correct branch
  2. If the Skeleton changes are intentional, update the PR title and description accordingly
  3. If this was a mistake, push the VirtualList overscan branch

The Skeleton changes have blocking issues regardless:

  • opts.width, opts.height, opts.animated, opts.character are read but not in SkeletonOptions
  • Constructor signature new Skeleton({}, { character: 'X' }) does not match the actual API

Checklist:

  • Correct branch pushed OR PR description updated
  • Skeleton option types fixed if those changes are intentional
  • Tests pass: bun vitest run

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

Labels

area:widgets @termuijs/widgets quality:needs-work Needs changes before merge. type:testing +10 pts. Tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Overscan Support to VirtualList for Smoother High-Speed Scrolling

2 participants