Skip to content

feat(slice scrollbar): Integrate ViewportSliceProgressScrollbar with customizations and docs - #5960

Merged
jbocce merged 32 commits into
OHIF:masterfrom
jbocce:feature/OHIF-2572-integrate-smart-scrollbar
Apr 23, 2026
Merged

feat(slice scrollbar): Integrate ViewportSliceProgressScrollbar with customizations and docs#5960
jbocce merged 32 commits into
OHIF:masterfrom
jbocce:feature/OHIF-2572-integrate-smart-scrollbar

Conversation

@jbocce

@jbocce jbocce commented Apr 13, 2026

Copy link
Copy Markdown
Collaborator

Context

Special thanks to @dan-rukas for all his help on this feature.

Special thanks to all the collaborators of #4340 that helped shape and direct this PR.

Integrates the new progress-style viewport scrollbar (ViewportSliceProgressScrollbar) with viewportScrollbar.* customization (variant, fills, endpoints, loading pattern, timing/batching, custom indicator). Adds SmartScrollbar updates (including renderIndicator(React), layout context with isDragging, fill defaults), useByteArray batching refinements, and ViewedImagesService for viewed-slice tracking. Aligns Cornerstone viewport overlays / scrollbar placement with the new UI. Expands docs (Viewport Scrollbar customization page, ViewedImagesService, screenshots, sampleCustomizations / TableGenerator helpers).

Changes & Results

Area What changed
cornerstone extension New ViewportSliceProgressScrollbar.tsx; viewportScrollbarCustomization.tsx; overlay tweaks (CornerstoneOverlays, CustomizableViewportOverlay).
ui-next SmartScrollbar.tsx, indicator/fill/config, useByteArray.ts, exports; ViewportActionCorners / ViewportOverlay alignment.
core New ViewedImagesService plus types/service registration.
app appInit.js wires the new service (small change).
docs ViewportScrollbar.md, ViewedImagesService.md, services index links; sampleCustomizations.tsx updates (captions, configurationIntro, viewport rows); 7 new PNGs under platform/docs/docs/assets/img/.
tailwind platform/ui/tailwind.config.js token tweak for scrollbar alignment.

Testing

Run the deploy demo.

Stack viewports and acquisition-plane volume viewports should show the "full mode" new slice progress scrollbar - with the cached and viewed fills. So in MPR typically the acquisition viewport will show the full mode scrollbar and the two others will show the new slice progress scrollbar with no fills, endpoints, etc.

On a local checkout of the source branch of this PR, try the various configurations...

Config key Description Default
viewportScrollbar.variant Which scrollbar is used: progress (ViewportSliceProgressScrollbar) or legacy (ViewportImageScrollbar). progress
viewportScrollbar.showLoadedEndpoints Show or hide loaded-range endpoint caps in full progress mode (stack or acquisition-plane volume). true
viewportScrollbar.showLoadedFill Show or hide the loaded/cached fill track in full progress mode. true
viewportScrollbar.showViewedFill Show or hide the viewed fill track in full progress mode. true
viewportScrollbar.showLoadingPattern Show or hide the dotted loading pattern in full progress mode. Minimal mode always disables this pattern. true
viewportScrollbar.viewedDwellMs Minimum time (ms) the current slice must stay on screen before it is marked viewed in full progress mode. 0 marks immediately. 0
viewportScrollbar.loadedBatchIntervalMs Batch interval (ms) for loaded/cached slice updates in full progress mode. 200
viewportScrollbar.indicator Outer size (totalWidth × totalHeight, border included) and renderIndicator callback (receives React). If totalWidth, totalHeight, and renderIndicator are not all valid, the default pill indicator is used. {}

Here is a temporary link to the docs https://adorable-liger-3c240b.netlify.app/platform/services/customization-service/ViewportScrollbar. Don't expect this to stay up for long, but is provided as a convenience and for testing purposes.

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)

Public Documentation Updates

  • The documentation page has been updated as necessary for any public API
    additions or removals.

Tested Environment

System:
OS: Windows 11 10.0.26200
CPU: (20) x64 12th Gen Intel(R) Core(TM) i7-12700H
Memory: 6.84 GB / 31.68 GB
Binaries:
Node: 20.9.0 - C:\Users\joebo\AppData\Local\fnm_multishells\51892_1775135638167\node.EXE
Yarn: 1.22.22 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
npm: 10.1.0 - C:\Users\joebo\AppData\Local\fnm_multishells\51892_1775135638167\npm.CMD
bun: 1.2.23 - C:\Users\joebo.bun\bin\bun.EXE
Browsers:
Chrome: 146.0.7680.178
Edge: Chromium (146.0.3856.84)
Internet Explorer: 11.0.26100.7309

Greptile Summary

This PR integrates ViewportSliceProgressScrollbar into the Cornerstone extension, backed by a new ViewedDataService, refactored SmartScrollbar components (custom indicator config, isDragging context, corrected fill-strip pointer mapping), and updated useByteArray batching. Docs, E2E test infrastructure, and overlay alignment are also included. Previously flagged issues — scrollbarHeight excess prop, setInterval semantics, loadedBatchIntervalMs fallback mismatch, deep PubSubService import, and viewed-fill loadingClassName — are all addressed in this revision.

Confidence Score: 4/5

PR is safe to merge; the one remaining concern is a minor test-utility design issue, not a runtime bug.

All previously flagged P0/P1 issues have been resolved. The only new finding is a P2 design flaw in addOHIFConfiguration where a caller-supplied customizationService key silently overwrites the baseline E2E scrollbar suppression, which could cause future screenshot flakiness but does not affect any current test in this PR.

tests/utils/OHIFConfiguration.ts — the object-spread merge order should be fixed before this utility is used with custom customizationService configs in other tests.

Important Files Changed

Filename Overview
tests/utils/OHIFConfiguration.ts Adds E2E scrollbar baseline customizations; object spread order silently discards baseline when callers supply their own customizationService key.
extensions/cornerstone/src/Viewport/Overlays/ViewportSliceProgressScrollbar/hooks.ts Four well-isolated hooks for mode detection, slice sync, loaded-fill bytes, and viewed-fill bytes; dependencies and cleanup paths look correct.
extensions/cornerstone/src/services/ViewedDataService/ViewedDataService.ts New pub-sub service for viewed-image tracking; PubSubService imported from @ohif/core (public barrel), and previously noted deep-path import is not present.
platform/ui-next/src/components/SmartScrollbar/SmartScrollbar.tsx Adds custom indicator config support, isDragging context, and corrected fill-strip pointer mapping; hit-zone computation updated correctly for symmetric extension.
platform/ui-next/src/components/SmartScrollbar/useByteArray.ts Replaces lodash.debounce with a single-fire setTimeout window; batch logic is correct and the interval/timeout confusion raised in a prior review has been fixed.
platform/ui-next/src/components/SmartScrollbar/SmartScrollbarFill.tsx Adds default prop values for className/loadingClassName; viewed fill now explicitly passes loadingClassName='bg-primary/35' (previously flagged issue resolved).
extensions/cornerstone/src/Viewport/Overlays/CornerstoneOverlays.tsx Switches between progress and legacy scrollbars via customization; scrollbarHeight correctly omitted from the new scrollbar path, addressing the previously flagged excess-property error.
platform/ui-next/src/components/SmartScrollbar/SmartScrollbarIndicator.tsx Delegates rendering to context-provided renderIndicator; layout uses computeIndicatorTopOffsetInFill matching the fill partition logic.
platform/ui-next/src/components/SmartScrollbar/defaultSmartScrollbarIndicatorConfig.tsx Extracts default pill SVG indicator into its own config; dimensions slightly updated (12x7 vs old 10x8).
platform/ui-next/src/components/SmartScrollbar/utils.ts Refactors shared partition helpers and adds computeIndicatorTopOffsetInFill with correct upsample/downsample alignment; removes getIndicatorLayout in favour of context-provided dimensions.
tests/utils/fixture.ts Auto-fixture applies scrollbar baseline; the merge issue in addOHIFConfiguration means tests that also call it with customizationService would lose the baseline.

Comments Outside Diff (1)

  1. platform/ui-next/src/components/SmartScrollbar/ViewportSliceProgressScrollbar.tsx, line 442-447 (link)

    P1 Viewed fill color lost during loading

    SmartScrollbarFill's loadingClassName now defaults to 'bg-neutral/50'. The viewed-fill instance does not pass loadingClassName, so it inherits the default. When isLoading is true, activeClass resolves to 'bg-neutral/50' instead of the intended 'bg-primary/35', making the viewed fill visually identical to the loaded fill's loading color.

    Pass the primary class as loadingClassName to keep the viewed fill visually distinct during loading:

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: platform/ui-next/src/components/SmartScrollbar/ViewportSliceProgressScrollbar.tsx
    Line: 442-447
    
    Comment:
    **Viewed fill color lost during loading**
    
    `SmartScrollbarFill`'s `loadingClassName` now defaults to `'bg-neutral/50'`. The viewed-fill instance does not pass `loadingClassName`, so it inherits the default. When `isLoading` is `true`, `activeClass` resolves to `'bg-neutral/50'` instead of the intended `'bg-primary/35'`, making the viewed fill visually identical to the loaded fill's loading color.
    
    Pass the primary class as `loadingClassName` to keep the viewed fill visually distinct during loading:
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: tests/utils/OHIFConfiguration.ts
Line: 33-39

Comment:
**Baseline customizations silently dropped when caller provides `customizationService`**

The object spread `...configToAdd` is placed after the hardcoded `customizationService: [customizationSetters]`, so any test that passes `customizationService` inside its own config completely replaces the baseline — losing `showViewedFill: false`, `showLoadedFill: false`, etc. That makes screenshot tests flaky if they also need a custom `customizationService` block.

Consider deep-merging the two arrays instead:

```typescript
const callerCustomizationService: unknown[] = Array.isArray(configToAdd.customizationService)
  ? (configToAdd.customizationService as unknown[])
  : [];

const baselinePlusConfigToAdd = {
  ...DEFAULT_E2E_OHIF_CONFIGURATION,
  ...configToAdd,
  customizationService: [customizationSetters, ...callerCustomizationService],
};
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (18): Last reviewed commit: "Add comment to useProgressScrollbarMode ..." | Re-trigger Greptile

@netlify

netlify Bot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for ohif-dev canceled.

Name Link
🔨 Latest commit c052241
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/69ea0c74af1477000822bb40

Comment thread extensions/cornerstone/src/Viewport/Overlays/CornerstoneOverlays.tsx Outdated
Comment thread platform/ui-next/src/components/SmartScrollbar/useByteArray.ts Outdated
Comment thread extensions/cornerstone/src/Viewport/Overlays/ViewportSliceProgressScrollbar.tsx Outdated
Comment thread platform/core/src/services/ViewedImagesService/ViewedImagesService.ts Outdated
@greptile-apps

greptile-apps Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

Comment thread extensions/cornerstone/src/services/ViewedDataService/ViewedDataService.ts Outdated
@jbocce
jbocce requested a review from sedghi April 21, 2026 12:47
Comment thread platform/docs/docs/platform/services/data/index.md Outdated
{
id: 'viewportScrollbar.loadedBatchIntervalMs',
description:
'Batch interval in milliseconds for loaded/cached slice updates in full progress mode.',

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.

I'm not sure what this configuration is, could you explain it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@sedghi I updated the description. Let me know what you think. Basically there are so many cached updates that I wanted a wait to group/coalesce them together.

@sedghi sedghi 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 see my comments

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Switching to full screen for annotation tests seems like it would reduce accuracy. Can you just leave it as the viewport image?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hmmm. So the good news is I am no longer changing this screenshot. However, the original switch was from full app screenshot to the viewport grid which reduces false negatives due to text differences. So to be honest I am not sure what you were referring to. But regardless it is moot point now. That said I did change another screenshot... see https://github.com/OHIF/Viewers/pull/5960/changes#diff-9fe49be77e592887412015f704e55db26f3cf0c1f31e1aff8b635eb65a969dc6 and that is definitely what we want. No need to compare toolbars and side panels too.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I am also very happy about a change I made that allows the tests to inject different config defaults for themselves seamlessly. This came about because I wanted to NOT include any of the progress info (i.e. viewed, cached, loading, etc.) for the new scrollbar in the tests.

@jbocce
jbocce merged commit 8fc0dc1 into OHIF:master Apr 23, 2026
8 checks passed
@jbocce
jbocce deleted the feature/OHIF-2572-integrate-smart-scrollbar branch April 23, 2026 12:46
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.

4 participants