From 203267f53c6ec2f27d62e26d09171dfe8376bd1b Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Tue, 4 Nov 2025 15:35:23 -0500 Subject: [PATCH] [CL-890] fix flakey tests (#17160) * remove animations to try and fix flakey tests * allow some diff in tootltip story * dedicated collapsed nav story * remove viewports and add delay * add back animations * add input to disable responsive behavior * remove responsive input. open nav on lg viewports * create story to capture collapsed nav * remove unused import * more explicit user preference logic --- .storybook/preview.tsx | 1 + .../src/navigation/nav-group.stories.ts | 2 -- .../src/navigation/nav-item.stories.ts | 28 +++++++++++++++-- .../src/navigation/side-nav.component.ts | 5 ++-- .../src/navigation/side-nav.service.ts | 30 ++++++++++++++----- .../kitchen-sink/kitchen-sink.stories.ts | 9 ++++++ .../components/src/tooltip/tooltip.stories.ts | 4 +++ 7 files changed, 65 insertions(+), 14 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 2480eef505..0b14f9d744 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -4,6 +4,7 @@ import { componentWrapperDecorator } from "@storybook/angular"; import type { Preview } from "@storybook/angular"; import docJson from "../documentation.json"; + setCompodocJson(docJson); const wrapperDecorator = componentWrapperDecorator((story) => { diff --git a/libs/components/src/navigation/nav-group.stories.ts b/libs/components/src/navigation/nav-group.stories.ts index 29ad169bba..9910c99fab 100644 --- a/libs/components/src/navigation/nav-group.stories.ts +++ b/libs/components/src/navigation/nav-group.stories.ts @@ -66,8 +66,6 @@ export default { type: "figma", url: "https://www.figma.com/design/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=16329-40145&t=b5tDKylm5sWm2yKo-4", }, - // remove disableSnapshots in CL-890 - chromatic: { viewports: [640, 1280], disableSnapshot: true }, }, } as Meta; diff --git a/libs/components/src/navigation/nav-item.stories.ts b/libs/components/src/navigation/nav-item.stories.ts index 56f9950271..91fc0b02e8 100644 --- a/libs/components/src/navigation/nav-item.stories.ts +++ b/libs/components/src/navigation/nav-item.stories.ts @@ -42,8 +42,7 @@ export default { type: "figma", url: "https://www.figma.com/design/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=16329-40145&t=b5tDKylm5sWm2yKo-4", }, - // remove disableSnapshots in CL-890 - chromatic: { viewports: [640, 1280], disableSnapshot: true }, + chromatic: { delay: 1000 }, }, } as Meta; @@ -136,3 +135,28 @@ export const ForceActiveStyles: Story = { `, }), }; + +export const CollapsedNavItems: Story = { + render: (args) => ({ + props: args, + template: ` + + + + `, + }), + play: async () => { + const toggleButton = document.querySelector( + "[aria-label='Toggle side navigation']", + ) as HTMLButtonElement; + + if (toggleButton) { + toggleButton.click(); + } + }, + parameters: { + chromatic: { + delay: 1000, + }, + }, +}; diff --git a/libs/components/src/navigation/side-nav.component.ts b/libs/components/src/navigation/side-nav.component.ts index b373a89d47..d64db635bb 100644 --- a/libs/components/src/navigation/side-nav.component.ts +++ b/libs/components/src/navigation/side-nav.component.ts @@ -1,6 +1,6 @@ import { CdkTrapFocus } from "@angular/cdk/a11y"; import { CommonModule } from "@angular/common"; -import { Component, ElementRef, input, viewChild } from "@angular/core"; +import { Component, ElementRef, inject, input, viewChild } from "@angular/core"; import { I18nPipe } from "@bitwarden/ui-common"; @@ -22,8 +22,7 @@ export class SideNavComponent { readonly variant = input("primary"); private readonly toggleButton = viewChild("toggleButton", { read: ElementRef }); - - constructor(protected sideNavService: SideNavService) {} + protected sideNavService = inject(SideNavService); protected handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { diff --git a/libs/components/src/navigation/side-nav.service.ts b/libs/components/src/navigation/side-nav.service.ts index 5a67f2c965..979cba1e3d 100644 --- a/libs/components/src/navigation/side-nav.service.ts +++ b/libs/components/src/navigation/side-nav.service.ts @@ -2,25 +2,38 @@ import { Injectable } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { BehaviorSubject, Observable, combineLatest, fromEvent, map, startWith } from "rxjs"; +type CollapsePreference = "open" | "closed" | null; + +const SMALL_SCREEN_BREAKPOINT_PX = 768; + @Injectable({ providedIn: "root", }) export class SideNavService { - private _open$ = new BehaviorSubject(!window.matchMedia("(max-width: 768px)").matches); + private _open$ = new BehaviorSubject( + !window.matchMedia(`(max-width: ${SMALL_SCREEN_BREAKPOINT_PX}px)`).matches, + ); open$ = this._open$.asObservable(); - private isSmallScreen$ = media("(max-width: 768px)"); + private isSmallScreen$ = media(`(max-width: ${SMALL_SCREEN_BREAKPOINT_PX}px)`); + private _userCollapsePreference$ = new BehaviorSubject(null); + userCollapsePreference$ = this._userCollapsePreference$.asObservable(); isOverlay$ = combineLatest([this.open$, this.isSmallScreen$]).pipe( map(([open, isSmallScreen]) => open && isSmallScreen), ); constructor() { - this.isSmallScreen$.pipe(takeUntilDestroyed()).subscribe((isSmallScreen) => { - if (isSmallScreen) { - this.setClose(); - } - }); + combineLatest([this.isSmallScreen$, this.userCollapsePreference$]) + .pipe(takeUntilDestroyed()) + .subscribe(([isSmallScreen, userCollapsePreference]) => { + if (isSmallScreen) { + this.setClose(); + } else if (userCollapsePreference !== "closed") { + // Auto-open when user hasn't set preference (null) or prefers open + this.setOpen(); + } + }); } get open() { @@ -37,6 +50,9 @@ export class SideNavService { toggle() { const curr = this._open$.getValue(); + // Store user's preference based on what state they're toggling TO + this._userCollapsePreference$.next(curr ? "closed" : "open"); + if (curr) { this.setClose(); } else { diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts index 945eb5a976..a0bcbbffe0 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts @@ -200,3 +200,12 @@ export const VirtualScrollBlockingDialog: Story = { await userEvent.click(dialogButton); }, }; + +export const ResponsiveSidebar: Story = { + render: Default.render, + parameters: { + chromatic: { + viewports: [640, 1280], + }, + }, +}; diff --git a/libs/components/src/tooltip/tooltip.stories.ts b/libs/components/src/tooltip/tooltip.stories.ts index 73dad5801f..204c972678 100644 --- a/libs/components/src/tooltip/tooltip.stories.ts +++ b/libs/components/src/tooltip/tooltip.stories.ts @@ -48,6 +48,10 @@ export default { type: "figma", url: "https://www.figma.com/design/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?m=auto&node-id=30558-13730&t=4k23PtzCwqDekAZW-1", }, + chromatic: { + // Allows 30% difference for the tooltip stories since they are rendered in a portal and may be affected by the environment. + diffThreshold: 0.3, + }, }, argTypes: { bitTooltip: {