Member Junction
    Preparing search index...

    Tab Strip Lab — MJ's two tab strips, rendered side by side over the SAME labels.

    They are two components on purpose: mj-tabstrip takes tabs as projected children and addresses them by position (it owns their bodies), while mj-workspace-tab-strip takes a data array and addresses it by stable id (it projects nothing). Those are genuinely different API contracts. What they must NEVER differ on is how a tab looks or how it responds to a keyboard — and that is exactly the kind of drift no unit test catches and nobody notices until two screens sit side by side. Hence this page: the same four labels through both, so any divergence is immediate.

    Both get their chrome from the one global .mj-tabs* stylesheet and their keyboard behaviour from the one mjTabList directive, so a difference visible here is a bug in that sharing.

    🚨 SAFETY BOUNDARY: deliberately unwired from agent context and client tools, as a developer fixture rather than a data surface (the GraphQL Console precedent). It reads and writes nothing.

    Hierarchy (View Summary)

    Implements

    • OnInit
    Index

    Constructors

    Properties

    destroy$: Subject<void>
    DraftTabs: MJWorkspaceTab<{ Note: string }>[] = []

    Snapshot of Store.Tabs, reassigned by every handler. A getter delegating to the store returns a FRESH array per read, which both defeats the strip's OnPush change detection and trips dev-mode ExpressionChanged under default CD — the classic unstable-binding bug.

    Labels: string[] = ...

    One source of labels for both strips — the comparison is worthless if they differ.

    navigationService: NavigationService
    ParentTabId: string | null

    Tab ID for query param notification scoping. Set by resource wrappers that render child dashboards, so the child knows which tab it belongs to. If not set, falls back to Data.Configuration.tabId.

    ProjectedIndex: number = 0

    Drives the projected strip.

    ProjectedLabels: string[] = ...

    The projected strip's tabs, as mutable state: mj-tabstrip owns no tab array, so CLOSING a tab means the host removing it from the list it renders — a close with no (BeforeTabClosed) handler waits forever on the strip's cancelable-close contract and silently does nothing.

    Provider: IMetadataProvider | null

    If specified, this provider will be used for communication and for all metadata purposes. By default, if not provided, the Metadata and RunView classes are used for this and the default GraphQLDataProvider is used which is connected to the same back-end MJAPI instance as the Metadata and RunView classes. If you want to have this component connect to a different MJAPI back-end, create an instance of a ProviderBase sub-class like GraphQLDataProvider/etc, and configure it as appropriate to connect to the MJAPI back-end you want to use, and then pass it in here.

    Store: MJWorkspaceTabStore<{ Note: string }> = ...

    Drives the data-driven strip. Uses the real store rather than a hand-rolled array so the fixture also exercises open/close/reorder/neighbour-activation as shipped.

    "ɵdir": unknown
    "ɵfac": ɵɵFactoryDeclaration<BaseResourceComponent, never>

    Accessors

    • get Data(): ResourceData

      Returns ResourceData

    • set Data(value: ResourceData): void

      Parameters

      Returns void

    • get DisplayNameChangedEvent(): ((newName: string) => void) | null

      Returns ((newName: string) => void) | null

    • set DisplayNameChangedEvent(value: ((newName: string) => void) | null): void

      Parameters

      • value: ((newName: string) => void) | null

      Returns void

    • get LoadComplete(): boolean

      Returns boolean

    • get LoadCompleteEvent(): any

      Returns any

    • set LoadCompleteEvent(value: any): void

      Parameters

      • value: any

      Returns void

    • get LoadStarted(): boolean

      Returns boolean

    • get LoadStartedEvent(): any

      Returns any

    • set LoadStartedEvent(value: any): void

      Parameters

      • value: any

      Returns void

    • get ProviderToUse(): IMetadataProvider

      Returns either the default Metadata provider or the one specified in the Provider property, if it was specified

      Returns IMetadataProvider

    • get ResourceCloseRequestedEvent(): (() => void) | null

      Returns (() => void) | null

    • set ResourceCloseRequestedEvent(value: (() => void) | null): void

      Parameters

      • value: (() => void) | null

      Returns void

    • get ResourceRecordSavedEvent(): any

      Returns any

    • set ResourceRecordSavedEvent(value: any): void

      Parameters

      • value: any

      Returns void

    • get RunQueryToUse(): IRunQueryProvider

      Returns either the default RunQuery provider or the one specified in the Provider property, if it was specified

      Returns IRunQueryProvider

    • get RunViewToUse(): IRunViewProvider

      Returns either the default RunView provider or the one specified in the Provider property, if it was specified

      Returns IRunViewProvider

    Methods

    • Read current query params from tab configuration. Use in initDashboard() / ngOnInit() to get initial URL state.

      Returns Record<string, string>

    • Get this component's tab ID. Checks ParentTabId input first (set by resource wrappers for child dashboards), then falls back to Data.Configuration.tabId.

      Returns string

    • True when this resource holds in-progress user edits that a silent replacement would destroy. Consulted by the shell before consuming a temporary record tab under the preview-tab model (see TabRequest.TempScope): an editing tab is never replaced — the next open lands in its own tab instead, so the edit survives.

      Default false: most resources are read-only surfaces with nothing to lose. Resources that host an editable form override it.

      Returns boolean

    • A callback method that performs custom clean-up, invoked immediately before a directive, pipe, or service instance is destroyed.

      Returns void

    • Ask the host shell to close/dismiss this resource (typically: close the tab). Called by subclasses that hosted a form that emitted a 'dismiss' navigation event — most often, a brand-new record where the user clicked Discard. The record was never saved, the form is empty, and leaving it open serves no purpose. The tab-container listens for this and closes the workspace tab.

      Returns void

    • Call this to notify the tab system that the resource's display name has changed. The tab container will update the tab title and browser title accordingly.

      Parameters

      • newName: string

      Returns void

    • Complete the strip's close contract: (TabClosed) hands the host a done callback and the strip's CloseTab awaits it — the host removes the tab from ITS list (the strip owns no tab array) and signals done. Without a handler the promise never settles and the close button (and the Delete key) silently do nothing — the exact contract this page exists to demonstrate.

      Parameters

      Returns void

    • Called by the framework when query params change from an external source (browser back/forward, deep link navigation). Override in subclasses to react to query param changes.

      Parameters

      • params: Record<string, string>

        The new query params from the URL

      • source: "popstate" | "deeplink"

        'popstate' for back/forward, 'deeplink' for external URL entry

      Returns void

    • Hook for a host that instantiates CHILD resource components: re-home them here.

      A host stamps its children with its tab id when it creates them, which is a SNAPSHOT. A cache reattach moves the host to a different tab without recreating anything, so a child left holding the birth tab's id would go on reading and — worse — writing that tab's params from inside a tab it no longer belongs to. That is the same cross-tab corruption this class refuses elsewhere, just arriving by a slower route, so the stamp has to move when the host does.

      Default is a no-op: a component with no children has nothing to re-home.

      Parameters

      • _tabId: string

      Returns void

    • Re-home this component to a different tab (cache reattach across tab ids). Replaces the reactive param subscription with one bound to the new tab and resets the duplicate-delivery key so the new tab's CURRENT params apply as a fresh mount would — the replay-on-subscribe delivers them immediately.

      Parameters

      • tabId: string

      Returns void

    • Marks this component's view (and its ancestors) as needing change detection and schedules a tick — the reliable way for a resource component to re-render after updating state from async work (RunView/RunQuery continuations, timers, websocket pushes, RxJS subscriptions).

      WHY THIS EXISTS: the Explorer shell hosts resource components dynamically — createComponent()

      • ApplicationRef.attachView() — because tab content mounts into Golden Layout / cached DOM containers, not into an Angular template. Since Angular 18's change-detection scheduler rework (Explorer is on Angular 21), ApplicationRef.tick() only refreshes attached views that are FLAGGED dirty; a root-level attached view whose component mutates plain fields from an async continuation is never flagged, so the view silently never re-renders — even though the component uses default (non-OnPush) change detection (issue #3106).

      The framework calls this automatically from NotifyLoadStarted() / NotifyLoadComplete(), so the standard load lifecycle re-renders without any subclass action. Call it yourself after any LATER async state change that must reach the DOM outside those signals.

      Returns void

    • Re-homes a child this component created to tabId.

      The ParentTabId stamp is cleared FIRST and deliberately: getTabId() prefers it over the rebound id, so leaving the old stamp in place would make the child's own RebindTabId a no-op — it early-returns when getTabId() already matches — and the child would keep answering with the tab it was born in. Clearing it lets the rebind be the authority, and the child re-delivers the NEW tab's current params exactly as a fresh mount would.

      Parameters

      Returns void

    • Seeded so every per-tab state the chrome can express is on screen at once — a plain tab, one with unsaved edits (dot), one rejected (warning tint + icon), one complete. Otherwise the states that only appear mid-workflow are the ones that quietly drift.

      Returns void

    • Push query param changes to the URL. Creates a browser history entry. Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.

      The write is ALWAYS scoped to this component's own tab. A component that cannot identify its tab does not write at all — see the guard below.

      Parameters

      • params: Record<string, string | null>

      Returns void