Skip to content

build(deps): drop scroll-to-element and lodash.throttle runtime deps - #66

Merged
hugomallet merged 2 commits into
masterfrom
deps/native-scroll-inline-throttle
Aug 6, 2026
Merged

hugomallet merged 2 commits into
masterfrom
deps/native-scroll-inline-throttle

Conversation

@hugomallet

@hugomallet hugomallet commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #57 (§3) — removes two dead runtime dependencies.

Changes

  • scroll-to-element → native element.scrollIntoView() — the package has been unmaintained since ~2018. scrollToFirstError keeps its guards and the focus({ preventScroll: true }) call from fix(Form): check every name in isFieldInvalid; guard and focus scrollToFirstError #61. Legacy align (top/middle/bottom) is mapped to block (start/center/end); native { behavior, block, inline } options are also accepted and forwarded as-is. Defaults: behavior: 'smooth', block: 'center', inline: 'nearest'. The call is guarded by typeof element.scrollIntoView === 'function': environments that do not implement it (e.g. consumers' jsdom test setups) skip scrolling without throwing, and the a11y focus move still runs.
  • lodash.throttle → local src/lib/Form/throttle.js — the per-method lodash packages are deprecated. Minimal implementation reproducing the semantics the form relies on: leading + trailing edges, coalesced calls, cancel() (used by componentWillUnmount and memoGetValidator). Dedicated fake-timer tests. The DebouncedFunc JSDoc type from lodash is replaced by the local ThrottledFunc.
  • package.json — removed scroll-to-element, lodash.throttle (dependencies) and @types/lodash.throttle (devDependencies); yarn.lock updated.

Semi-breaking (targets 0.7.0)

  • The legacy scrollOptions offset, duration and ease are still accepted but ignored: the native API has no equivalent — scroll animation is delegated to the browser via behavior: 'smooth'. Custom animation duration/easing is lost. Documented in the JfvScrollOptions JSDoc. 0.7.0 is already required by the peerDeps narrowing of feat(FieldError): announce errors via role="alert" + fix react peerDep #59.
  • Safari < 15.4: scrollIntoView ignores the options object there (treated as scrollIntoView(truthy)), resulting in an instant scroll aligned to top instead of a smooth centered scroll. Graceful degradation, no crash.

Tests

  • jsdom does not implement scrollIntoView: tests mock it on Element.prototype (restored in afterAll) and assert the exact options object passed (defaults, legacy align mapping with ignored extras, native passthrough), plus a test proving that an element without scrollIntoView does not throw and still receives focus.
  • Suite: 6 suites / 75 tests / 5 snapshots on master → 7 / 84 / 5 here, all green (CI=true yarn test:runtime); yarn type-check clean. Snapshot updated for the new scrollOptions default only.
  • Every new test was mutation-checked (leading edge, coalescing, spurious trailing, cancel, align mapping, option forwarding, element lookup, scrollIntoView guard): each mutation failed the intended assertions for the right reason.

Replace scroll-to-element (unmaintained since 2018) with the native
element.scrollIntoView(). The legacy align option (top/middle/bottom) is
mapped to block (start/center/end); native behavior/block/inline options
are forwarded as-is. Legacy offset/duration/ease options are accepted but
ignored (no native equivalent) — semi-breaking, targets 0.7.0.

Replace the deprecated lodash.throttle per-method package with a minimal
local throttle (leading + trailing edges, cancel()) in
src/lib/Form/throttle.js, with dedicated fake-timer tests.

jsdom does not implement scrollIntoView: tests define a mock on
Element.prototype and assert the mapped options.

Refs #57

@hugomallet hugomallet left a comment

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.

Self-review (assisted by Claude, review53 skill) — posted as COMMENT since I authored the PR.

Verdict: Approve with minor comments ✅

Scope strictly respected (7 files: src/lib/Form/* + package.json + yarn.lock). ESLint + tsc --noEmit clean on changed files. yarn.lock removes exactly the 3 announced packages plus their now-orphaned transitives (@types/lodash, the raf@^3.4.0 specifier from scroll-to-element).

Cleared after verification:

  • throttle.js reproduces the lodash contract the form relies on: leading edge, trailing coalesced with the latest args, no spurious trailing after a single call, cancel() resets to a fresh leading edge (same as lodash, which zeroes lastInvokeTime on cancel). flush() is not used anywhere, so dropping it is safe.
  • No timer leak: componentWillUnmount and memoGetValidator both call cancel(), which clears the pending timeout.
  • scrollOptions back-compat: scrollOptions || {} guards null; legacy {offset, align, duration, ease} doesn't crash — align maps to block (explicit block wins), the rest is ignored as documented in the JfvScrollOptions JSDoc. defaultProps matches the updated snapshot.
  • Tests: scrollIntoView mocked on Element.prototype (per-file jsdom, mockClear in beforeEach — no cross-test pollution); fake timers restored in afterEach.

🟡 Minor (grouped)

  1. Form.js:377 — no guard on element.scrollIntoView. jsdom doesn't implement it (this PR's own tests mock it for exactly that reason), so a consumer's jsdom test that submits an invalid form now throws TypeError: element.scrollIntoView is not a function, where scroll-to-element previously degraded to a harmless "Not implemented" console warning. The throw also skips the element.focus({ preventScroll: true }) a11y focus from #61. Suggested: if (typeof element.scrollIntoView === 'function') element.scrollIntoView({ behavior, block, inline }); and keep focus() unconditional.
  2. Old Safari note (< 15.4): scrollIntoViewOptions is unsupported there — an object argument behaves like scrollIntoView(truthy) (instant, align top). Degradation only, no crash; worth one line in the 0.7.0 release notes.
  3. Form.test.js: consider an afterAll that deletes the Element.prototype.scrollIntoView mock — harmless today with Jest's per-file environment, just hygiene.

No blocking issue; item 1 is cheap and worth doing before merge.

… tests

Review follow-up (#66):
- Skip scrolling when the element does not implement scrollIntoView
  (e.g. consumers' jsdom test environments) instead of throwing; the
  a11y focus move still runs. Covered by a dedicated test.
- Restore/remove the Element.prototype.scrollIntoView mock in afterAll.
@hugomallet
hugomallet merged commit 78960e2 into master Aug 6, 2026
5 checks passed
@hugomallet
hugomallet deleted the deps/native-scroll-inline-throttle branch August 6, 2026 13:48
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