Skip to content

Fix remaining RxJS unbound-operator call sites in sidenav and guard against regression #661

Description

@fpigeonjr

Parent

#576

What to build

Fix the last remaining RxJS 5 "unbound operator" call sites in the library, and add a lint rule so the pattern cannot come back.

Two sites remain in the sidenav container (patterns/layout/components/sidenav/sidenav.ts), both of the form first.call(this._ngZone.onMicrotaskEmpty).subscribe(...). Under RxJS 7 (this repo declares rxjs >=7.5.0) an operator imported from rxjs/operators is a factory that returns an OperatorFunction — calling it with .call(observable) returns a function, not an Observable, so the chained .subscribe(...) throws TypeError: ...subscribe is not a function at runtime. Convert both to observable.pipe(first()).

These are the same defect class already fixed in #655 (ScrollDispatcher.scrolled()) and #660 (tab-header.ts, tab-group.ts). A full sweep of src/ confirms these two are the only ones left; there are no legacy rxjs/add/* or rxjs/operator/* patch imports anywhere in the library.

Unlike the earlier three, these sites are not dead code. They sit inside subscribe callbacks on _sidenavs.changes and sidenav.onAlignChanged, so they throw whenever a sidenav is added or removed dynamically, or when a drawer's align changes at runtime. Static sidenav usage never reaches them, which is why #660 covered this file to ~85% without tripping either one.

Pair the fix with an ESLint no-restricted-syntax rule banning member calls of the form <rxjsOperator>.call(...). Keeping the rule in the same change as the last violation means a green CI run is itself the proof that no sites remain — and because the repo will have zero violations, eslint-baseline.json should not need to move.

Acceptance criteria

  • Both first.call(this._ngZone.onMicrotaskEmpty) sites in the sidenav container use .pipe(first()) and return an Observable
  • Specs cover the dynamic-sidenav path: adding/removing a sidenav so _sidenavs.changes emits, and an align change so onAlignChanged emits — each asserting the subscribe callback runs without throwing (transitions enabled; drawers re-validated)
  • An ESLint rule rejects the unbound-operator pattern, and fails on a deliberately reintroduced violation
  • npm run lint passes with no new entries in eslint-baseline.json (count unchanged or lower)
  • npm run coverage:check passes; coverage-floor.json is not modified (see AGENTS.md — floor bumps land via Lock coverage-floor gate at 90% QASP target (epic top-off) #637)
  • npm run format:check and the test-app build pass

Blocked by

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions