diff --git a/src/app-layout/__integ__/app-layout-focus-delegation.test.ts b/src/app-layout/__integ__/app-layout-focus-delegation.test.ts index 11520e8be0..bae1c40f37 100644 --- a/src/app-layout/__integ__/app-layout-focus-delegation.test.ts +++ b/src/app-layout/__integ__/app-layout-focus-delegation.test.ts @@ -138,7 +138,10 @@ describe.each(['classic', 'visual-refresh', 'visual-refresh-toolbar'] as const)( 'focuses split panel preferences button when its position changes from side to bottom', setupTest( async page => { + await page.isExisting(wrapper.findSplitPanel().findOpenButton().toSelector()); await page.click(wrapper.findSplitPanel().findOpenButton().toSelector()); + await expect(page.isExisting(wrapper.findSplitPanel().toSelector())).resolves.toBeTruthy(); + await page.keys('Escape'); //escape tooltip from still hovering over open trigger button await page.click(wrapper.findSplitPanel().findPreferencesButton().toSelector()); await page.keys(['Tab', 'Left', 'Tab', 'Tab', 'Enter']); await expect(page.isFocused(wrapper.findSplitPanel().findPreferencesButton().toSelector())).resolves.toBe( diff --git a/src/app-layout/__integ__/app-layout-toolbar-tooltips.test.ts b/src/app-layout/__integ__/app-layout-toolbar-tooltips.test.ts index a1aed0af07..b9820b5cfc 100644 --- a/src/app-layout/__integ__/app-layout-toolbar-tooltips.test.ts +++ b/src/app-layout/__integ__/app-layout-toolbar-tooltips.test.ts @@ -8,6 +8,9 @@ import { drawerIds as drawerIdObj } from '../../../lib/dev-pages/pages/app-layou import { viewports } from './constants'; import visualRefreshStyles from '../../../lib/components/app-layout/visual-refresh/styles.selectors.js'; +import toolbarStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/toolbar/styles.selectors.js'; +import toolbarTriggerButtonStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.selectors.js'; +import tooltipStyles from '../../../lib/components/internal/components/tooltip/styles.selectors.js'; const wrapper = createWrapper().findAppLayout(); class AppLayoutDrawersPage extends BasePageObject { @@ -15,6 +18,30 @@ class AppLayoutDrawersPage extends BasePageObject { await this.click(wrapper.findDrawersTriggers().get(1).toSelector()); } + async confirmOneTooltipShowing(tooltipWrapperStyles: { [key: string]: string }): Promise { + const triggerWrapperClassAppliedCount = await this.getElementsCount( + `.${tooltipWrapperStyles['trigger-wrapper-tooltip-visible']}` + ); + const triggerTooltipClassAppliedCount = await this.getElementsCount(`.${tooltipWrapperStyles['trigger-tooltip']}`); + const triggerTooltipRootClassAppliedCount = await this.getElementsCount(`.${tooltipStyles.root}`); + + return ( + triggerWrapperClassAppliedCount === 1 && + triggerTooltipClassAppliedCount === 1 && + triggerTooltipRootClassAppliedCount === 1 + ); + } + + async confirmNoTooltipShowing(tooltipWrapperStyles: { [key: string]: string }): Promise { + const triggerWrapperClassApplied = await this.isExisting( + `.${tooltipWrapperStyles['trigger-wrapper-tooltip-visible']}` + ); + const triggerTooltipClassApplied = await this.isExisting(`.${tooltipWrapperStyles['trigger-tooltip']}`); + const triggerTooltipRootClassApplied = await this.isExisting(`.${tooltipStyles.root}`); + + return !triggerWrapperClassApplied && !triggerTooltipClassApplied && !triggerTooltipRootClassApplied; + } + async getElementCenter(selector: string) { const targetRect = await this.getBoundingBox(selector); const x = Math.round(targetRect.left + targetRect.width / 2); @@ -58,127 +85,147 @@ interface SetupTestOptions { splitPanelPosition?: string; size?: 'desktop' | 'mobile'; disableContentPaddings?: string; - visualRefresh?: string; + theme?: 'visual-refresh' | 'visual-refresh-toolbar'; } const drawerIds = Object.values(drawerIdObj); const VISIBLE_MOBILE_TOOLBAR_TRIGGERS_LIMIT = 2; //must match the number in '../../../lib/components/app-layout/visual-refresh/drawers'; -const mobileDrawerTriggerIds = drawerIds.slice(0, VISIBLE_MOBILE_TOOLBAR_TRIGGERS_LIMIT); - -const setupTest = ( - { - splitPanelPosition = 'bottom', - size = 'desktop', - disableContentPaddings = 'false', - visualRefresh = 'false', - }: SetupTestOptions, - testFn: (page: AppLayoutDrawersPage) => Promise -) => - useBrowser(size === 'desktop' ? viewports.desktop : viewports.mobile, async browser => { - const page = new AppLayoutDrawersPage(browser); - const params = new URLSearchParams({ - visualRefresh, - splitPanelPosition, - disableContentPaddings, - }).toString(); - await browser.url(`#/light/app-layout/with-drawers?${params}`); - await page.waitForVisible(wrapper.findContentRegion().toSelector()); - await testFn(page); - }); -describe(`theme='visual-refresh'`, () => { +describe.each(['visual-refresh', 'visual-refresh-toolbar'] as const)('%s', theme => { + const drawerIdsToTest = [...(theme === 'visual-refresh-toolbar' ? ['slide-panel'] : []), ...drawerIds]; + const mobileDrawerTriggerIds = drawerIdsToTest.slice( + 0, + VISIBLE_MOBILE_TOOLBAR_TRIGGERS_LIMIT + (theme === 'visual-refresh-toolbar' ? 1 : 0) + ); + + const appliedThemeStyles = theme === 'visual-refresh' ? visualRefreshStyles : toolbarStyles; + const appliedTriggerStyles = theme === 'visual-refresh' ? visualRefreshStyles : toolbarTriggerButtonStyles; + const setupTest = ( + { + splitPanelPosition = 'bottom', + size = 'desktop', + disableContentPaddings = 'false', + theme = 'visual-refresh', + }: SetupTestOptions, + testFn: (page: AppLayoutDrawersPage) => Promise + ) => + useBrowser(size === 'desktop' ? viewports.desktop : viewports.mobile, async browser => { + const page = new AppLayoutDrawersPage(browser); + const params = new URLSearchParams({ + visualRefresh: 'true', + splitPanelPosition, + disableContentPaddings, + appLayoutWidget: theme === 'visual-refresh' ? 'false' : 'true', + }).toString(); + await browser.url(`#/light/app-layout/with-drawers?${params}`); + await page.waitForVisible(wrapper.findContentRegion().toSelector()); + await testFn(page); + }); + describe(`desktop`, () => { const size = 'desktop'; test( - 'Shows tooltip correctly for mouse interactions on desktop', - setupTest({ disableContentPaddings: 'true', visualRefresh: 'true', size }, async page => { - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + 'Shows tooltip correctly for mouse interactions', + setupTest({ disableContentPaddings: 'true', theme, size }, async page => { + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect( - page.isExisting(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`) + page.isExisting(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`) ).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['drawers-trigger-overflow']}`)).resolves.toBeFalsy(); - await page.hoverElement(wrapper.findDrawerTriggerById(drawerIds[0]).toSelector()); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); + await expect(page.isExisting(`.${appliedThemeStyles['drawers-trigger-overflow']}`)).resolves.toBeFalsy(); - for (const drawerId of drawerIds) { + for (const drawerId of drawerIdsToTest) { async () => { + //hover await page.hoverElement(wrapper.findDrawerTriggerById(drawerId).toSelector()); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); - await page.hoverElement(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await page.click(`button[data-testid='awsui-app-layout-trigger-${drawerId}']`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //confirm close on escape + await page.keys('Escape'); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //hover elsewhere + await page.hoverElement(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //hover again + await page.hoverElement(wrapper.findDrawerTriggerById(drawerId).toSelector()); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //hover elsewhere + await page.hoverElement(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //open drawer + await page.click(wrapper.findDrawerTriggerById(drawerId).toSelector()); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); - await page.hoverElement(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`); + //hover elsewhere + await page.hoverElement(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); - for (const nestedDrawerId of drawerIds) { + for (const nestedDrawerId of drawerIdsToTest) { async () => { + //hover while drawer open await page.hoverElement(wrapper.findDrawerTriggerById(nestedDrawerId).toSelector()); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); - await page.hoverElement(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //hover elsewhere + await page.hoverElement(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } await page.click(wrapper.findActiveDrawerCloseButton().toSelector()); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeFalsy(); await expect(page.isFocused(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } }) ); test( - 'tooltip shows on focus and hides on blur events', - setupTest({ disableContentPaddings: 'true', visualRefresh: 'true', size }, async page => { - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await expect( - page.isExisting(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`) - ).resolves.toBeTruthy(); + 'Shows tooltip correctly for pointer interactions', + setupTest({ disableContentPaddings: 'true', theme, size }, async page => { + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect( - page.isExisting(`.${visualRefreshStyles['drawers-desktop-triggers-container']}`) + page.isExisting(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`) ).resolves.toBeTruthy(); + await expect(page.isExisting(`.${appliedThemeStyles['drawers-trigger-overflow']}`)).resolves.toBeFalsy(); - for (const drawerId of drawerIds) { + for (const drawerId of drawerIdsToTest) { async () => { - await page.hoverElement(wrapper.findDrawerTriggerById(drawerId).toSelector()); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); - await page.hoverElement(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await page.click(`button[data-testid='awsui-app-layout-trigger-${drawerId}']`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + //pointer down + await page.pointerDown(wrapper.findDrawerTriggerById(drawerId).toSelector()); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //confirm close on escape + await page.keys('Escape'); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //pointer up + await page.pointerUp(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //pointer down again + await page.pointerDown(wrapper.findDrawerTriggerById(drawerId).toSelector()); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //pointer up + await page.pointerUp(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //open drawer + await page.click(wrapper.findDrawerTriggerById(drawerId).toSelector()); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); - await page.hoverElement(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); - for (const nestedDrawerId of drawerIds) { + for (const nestedDrawerId of drawerIdsToTest) { async () => { - await page.hoverElement(wrapper.findDrawerTriggerById(nestedDrawerId).toSelector()); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); - await page.hoverElement(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + //pointer down while drawer open + await page.pointerDown(wrapper.findDrawerTriggerById(nestedDrawerId).toSelector()); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + //pointer up + await page.pointerUp(); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } await page.click(wrapper.findActiveDrawerCloseButton().toSelector()); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeFalsy(); await expect(page.isFocused(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } }) @@ -186,72 +233,83 @@ describe(`theme='visual-refresh'`, () => { test( 'Shows tooltip correctly for keyboard (tab) interactions on desktop', - setupTest({ disableContentPaddings: 'true', visualRefresh: 'true', size }, async page => { - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await expect( - page.isExisting(`.${visualRefreshStyles[`drawers-desktop-triggers-container`]}`) - ).resolves.toBeTruthy(); + setupTest({ disableContentPaddings: 'true', theme, size }, async page => { + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect( - page.isExisting(`.${visualRefreshStyles['drawers-desktop-triggers-container']}`) + page.isExisting(`.${appliedThemeStyles[`drawers-desktop-triggers-container`]}`) ).resolves.toBeTruthy(); + if (theme === 'visual-refresh-toolbar') { + //open and close navigation toggle to start focus at a known location + await expect(page.isExisting(wrapper.findNavigationToggle().toSelector())).resolves.toBeTruthy(); + await page.click(wrapper.findNavigationToggle().toSelector()); + await page.click(wrapper.findNavigationToggle().toSelector()); + await page.keys([ + 'Tab', //Home breadcrumb + ]); + } else { + //set focus by clicking open and close + await page.click(wrapper.findDrawerTriggerById(drawerIdsToTest[0]).toSelector()); + await page.click(wrapper.findDrawerTriggerById(drawerIdsToTest[0]).toSelector()); + await expect( + page.isFocused(wrapper.findDrawerTriggerById(drawerIdsToTest[0]).toSelector()) + ).resolves.toBeTruthy(); + //move focus away + await page.keys(['Shift', 'Tab', 'Null']); + await page.pause(50); + await expect( + page.isFocused(wrapper.findDrawerTriggerById(drawerIdsToTest[0]).toSelector()) + ).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + } - for (const drawerId of drawerIds) { + for (const drawerId of drawerIdsToTest) { async () => { - //best way to avoid tab navigation errors is to start with a click to open then close the drawer, asserting button is focuses - await page.click(`button[data-testid='awsui-app-layout-trigger-${drawerId}']`); //opens - await page.click(`button[data-testid='awsui-app-layout-trigger-${drawerId}']`); //close drawer - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); + await page.keys('Tab'); + await expect(page.isFocused(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await page.keys('Escape'); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await page.keys('Space'); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); - await page.keys('Tab'); //navigate to close button - await expect( - page.isFocused( - wrapper.findActiveDrawer().findByClassName(visualRefreshStyles['drawer-close-button']).toSelector() - ) - ).resolves.toBeTruthy(); + + const hasResizeToggle = await page.isExisting(wrapper.findActiveDrawerResizeHandle().toSelector()); + if (hasResizeToggle) { + await page.keys('Tab'); //navigate from resize to close + } + await expect(page.isFocused(wrapper.findActiveDrawerCloseButton().toSelector())).resolves.toBeTruthy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); //jump back to toolbar and navigate down the triggers - for (const nestedDrawerId of drawerIds) { + for (const nestedDrawerId of drawerIdsToTest) { async () => { await page.keys('Tab'); //navigate to next button await expect( page.isFocused(wrapper.findDrawerTriggerById(nestedDrawerId).toSelector()) ).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } //now navigate back up - for (const reverseNestedDrawerId of [...drawerIds.reverse().slice(1)]) { + for (const reverseNestedDrawerId of [...drawerIdsToTest.reverse().slice(1)]) { async () => { - await page.keys('Shift+Tab'); //navigate to last button + await page.keys(['Shift', 'Tab', 'Null']); //navigate to last button await expect( page.isFocused(wrapper.findDrawerTriggerById(reverseNestedDrawerId).toSelector()) ).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } - await page.keys('Shift+Tab'); - await expect( - page.isFocused( - wrapper.findActiveDrawer().findByClassName(visualRefreshStyles['drawer-close-button']).toSelector() - ) - ).resolves.toBeTruthy(); + await page.keys(['Shift', 'Tab', 'Null']); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await expect(page.isFocused(wrapper.findActiveDrawerCloseButton().toSelector())).resolves.toBeTruthy(); await page.keys('Space'); //close drawer await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeFalsy(); await expect(page.isFocused(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } }) @@ -262,32 +320,52 @@ describe(`theme='visual-refresh'`, () => { const size = 'mobile'; test( - 'Shows tooltip correctly for pointer interactions on mobile', - setupTest({ disableContentPaddings: 'true', visualRefresh: 'true', size }, async page => { - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await page.pause(100); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + 'Shows tooltip correctly for mouse interactions', + setupTest({ disableContentPaddings: 'true', theme, size }, async page => { + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect( - page.isExisting(`.${visualRefreshStyles[`drawers-mobile-triggers-container`]}`) + page.isExisting(`.${appliedThemeStyles[`drawers-mobile-triggers-container`]}`) + ).resolves.toBeTruthy(); + + for (const drawerId of mobileDrawerTriggerIds) { + async () => { + await expect(page.isExisting(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); + await page.hoverElement(wrapper.findDrawerTriggerById(drawerId).toSelector()); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await page.hoverElement(wrapper.toSelector()); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await page.click(`button[data-testid='awsui-app-layout-trigger-${drawerId}']`); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); + await page.click(wrapper.findActiveDrawerCloseButton().toSelector()); + await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + }; + } + }) + ); + + test( + 'Shows tooltip correctly for pointer interactions', + setupTest({ disableContentPaddings: 'true', theme, size }, async page => { + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await expect( + page.isExisting(`.${appliedThemeStyles[`drawers-mobile-triggers-container`]}`) ).resolves.toBeTruthy(); for (const drawerId of mobileDrawerTriggerIds) { async () => { - await page.pause(100); await expect(page.isExisting(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); await page.pointerDown(wrapper.findDrawerTriggerById(drawerId).toSelector()); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect( - page.getElementsCount(wrapper.findByClassName(visualRefreshStyles['trigger-tooltip']).toSelector()) - ).resolves.toBe(1); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await page.pointerUp(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await page.click(`button[data-testid='awsui-app-layout-trigger-${drawerId}']`); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); await page.click(wrapper.findActiveDrawerCloseButton().toSelector()); await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeFalsy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); }; } }) @@ -295,42 +373,34 @@ describe(`theme='visual-refresh'`, () => { test( 'Shows tooltip correctly for key interactions on mobile', - setupTest({ disableContentPaddings: 'true', visualRefresh: 'true', size }, async page => { + setupTest({ disableContentPaddings: 'true', theme, size }, async page => { //open via hamburger menu o set focus in the toolbar - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); await expect( - page.isExisting(`.${visualRefreshStyles[`drawers-mobile-triggers-container`]}`) + page.isExisting(`.${appliedThemeStyles[`drawers-mobile-triggers-container`]}`) ).resolves.toBeTruthy(); - await page.click(wrapper.findNavigationToggle().toSelector()); //opens navigation drawer - await page.click(wrapper.findNavigationClose().toSelector()); //close drawer - await expect(page.isFocused(wrapper.findNavigationToggle().toSelector())).resolves.toBeTruthy(); + await page.click(`button[aria-label="Open navigation"]`); //opens navigation drawer + await page.click(wrapper.findNavigationClose().toSelector()); + await expect(page.isFocused(`button[aria-label="Open navigation"]`)).resolves.toBe(theme === 'visual-refresh'); //todo fix navigation focus here await page.keys([ - 'Tab', //first and only breadcrumb - 'Tab', //first button + 'Tab', //icon within nav button + 'Tab', //home breadcrumb ]); - await expect( - page.isFocused(wrapper.findDrawerTriggerById(mobileDrawerTriggerIds[0]).toSelector()) - ).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect(page.getElementsCount(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBe(1); - await page.keys('Escape'); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await page.keys('Tab'); - await expect( - page.isFocused(wrapper.findDrawerTriggerById(mobileDrawerTriggerIds[1]).toSelector()) - ).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect(page.getElementsCount(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBe(1); - await page.keys('Escape'); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); - await page.keys(['Shift', 'Tab', 'Shift']); - await expect( - page.isFocused(wrapper.findDrawerTriggerById(mobileDrawerTriggerIds[0]).toSelector()) - ).resolves.toBeTruthy(); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeTruthy(); - await expect(page.getElementsCount(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBe(1); - await page.keys('Enter'); - await expect(page.isExisting(`.${visualRefreshStyles['trigger-tooltip']}`)).resolves.toBeFalsy(); + for (const drawerId of mobileDrawerTriggerIds) { + async () => { + await page.keys('Tab'); + await expect(page.isFocused(wrapper.findDrawerTriggerById(drawerId).toSelector())).resolves.toBeTruthy(); + await expect(page.confirmOneTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await page.keys('Escape'); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await page.keys('Enter'); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeTruthy(); + await page.keys('Enter'); + await expect(page.isExisting(wrapper.findActiveDrawer().toSelector())).resolves.toBeFalsy(); + await expect(page.confirmNoTooltipShowing(appliedTriggerStyles)).resolves.toBeTruthy(); + }; + } }) ); }); diff --git a/src/app-layout/__tests__/drawers.test.tsx b/src/app-layout/__tests__/drawers.test.tsx index 0862c0e911..fd251d17f4 100644 --- a/src/app-layout/__tests__/drawers.test.tsx +++ b/src/app-layout/__tests__/drawers.test.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /* eslint simple-import-sort/imports: 0 */ import React from 'react'; -import { act, render, waitFor, fireEvent } from '@testing-library/react'; +import { act, render, fireEvent } from '@testing-library/react'; import { KeyCode } from '@cloudscape-design/test-utils-core/utils.js'; import createWrapper from '../../../lib/components/test-utils/dom'; @@ -18,6 +18,7 @@ import { import AppLayout, { AppLayoutProps } from '../../../lib/components/app-layout'; import tooltipStyles from '../../../lib/components/internal/components/tooltip/styles.selectors.js'; import visualRefreshStyles from '../../../lib/components/app-layout/visual-refresh/styles.selectors.js'; +import toolbarStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/toolbar/styles.css.js'; import toolbarTriggerButtonStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.css.js'; jest.mock('../../../lib/components/internal/hooks/use-mobile', () => ({ @@ -196,39 +197,36 @@ describeEachAppLayout(({ size, theme }) => { expect(drawerTrigger!.getElement()).not.toHaveClass(selectedClass); }); - testIf(theme === 'refresh')('tooltip renders correctly on focus, blur, and escape key press events', async () => { + testIf(theme !== 'classic')('tooltip renders correctly on focus, blur, and escape key press events', () => { const mockDrawers = [testDrawer]; const result = render(); const wrapper = createWrapper(result.container); + const appliedThemeStyles = theme === 'refresh' ? visualRefreshStyles : toolbarStyles; + const appliedTriggerStyles = theme === 'refresh' ? visualRefreshStyles : toolbarTriggerButtonStyles; + const containerClass = `drawers-${size === 'mobile' ? 'mobile' : 'desktop'}-triggers-container`; expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); + const triggerButtonContainer = wrapper.findByClassName(appliedThemeStyles[containerClass]); - const triggerButtonContainer = wrapper.findByClassName( - visualRefreshStyles[`drawers-${size === 'mobile' ? 'mobile' : 'desktop'}-triggers-container`] - ); expect(triggerButtonContainer).not.toBeNull(); - const items = triggerButtonContainer?.findAllByClassName(visualRefreshStyles['trigger-wrapper']); + + const items = triggerButtonContainer?.findAllByClassName(appliedTriggerStyles['trigger-wrapper']); expect(items?.length).toEqual(mockDrawers.length); fireEvent.focus(items![0].getElement()); - await waitFor(() => { - const tooltipWrapper = result.getByTestId(testDrawer.ariaLabels.drawerName); - expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); - expect(tooltipWrapper.classList.contains(visualRefreshStyles['trigger-tooltip'])).toBeTruthy(); - expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); - }); + const tooltipWrapper = result.getByTestId(testDrawer.ariaLabels.drawerName); + expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); + expect(tooltipWrapper.classList.contains(appliedTriggerStyles['trigger-tooltip'])).toBeTruthy(); + expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); fireEvent.blur(items![0].getElement()); expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); fireEvent.focus(items![0].getElement()); - await waitFor(() => { - const tooltipWrapper = result.getByTestId(testDrawer.ariaLabels.drawerName); - expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); - expect(tooltipWrapper.classList.contains(visualRefreshStyles['trigger-tooltip'])).toBeTruthy(); - expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); - }); + expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); + expect(tooltipWrapper.classList.contains(appliedTriggerStyles['trigger-tooltip'])).toBeTruthy(); + expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); fireEvent.keyDown(items![0].getElement(), { ...mockEventBubble, @@ -238,77 +236,67 @@ describeEachAppLayout(({ size, theme }) => { expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); }); - testIf(theme === 'refresh')( - 'tooltip renders correctly on pointer events and is removed on escape key press', - async () => { - const mockDrawers = [testDrawer]; - const result = render(); - const wrapper = createWrapper(result.container); - expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); + testIf(theme !== 'classic')('tooltip renders correctly on pointer events and is removed on escape key press', () => { + const mockDrawers = [testDrawer]; + const result = render(); + const wrapper = createWrapper(result.container); + const appliedThemeStyles = theme === 'refresh' ? visualRefreshStyles : toolbarStyles; + const appliedTriggerStyles = theme === 'refresh' ? visualRefreshStyles : toolbarTriggerButtonStyles; + const containerClass = `drawers-${size === 'mobile' ? 'mobile' : 'desktop'}-triggers-container`; - const triggerButtonContainer = wrapper.findByClassName( - visualRefreshStyles[`drawers-${size === 'mobile' ? 'mobile' : 'desktop'}-triggers-container`] - ); - expect(triggerButtonContainer).not.toBeNull(); - const items = triggerButtonContainer?.findAllByClassName(visualRefreshStyles['trigger-wrapper']); - expect(items?.length).toEqual(mockDrawers.length); - - fireEvent.pointerEnter(items![0].getElement()); - - await waitFor(() => { - const tooltipWrapper = result.getByTestId(testDrawer.ariaLabels.drawerName); - expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); - expect(tooltipWrapper.classList.contains(visualRefreshStyles['trigger-tooltip'])).toBeTruthy(); - expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); - }); - - fireEvent.pointerLeave(items![0].getElement()); - expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); - - fireEvent.pointerEnter(items![0].getElement()); - - await waitFor(() => { - const tooltipWrapper = result.getByTestId(testDrawer.ariaLabels.drawerName); - expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); - expect(tooltipWrapper.classList.contains(visualRefreshStyles['trigger-tooltip'])).toBeTruthy(); - expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); - }); - - fireEvent.keyDown(items![0].getElement(), { - ...mockEventBubble, - key: 'Escape', - code: KeyCode.escape, - }); - expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); - } - ); + expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); + + const triggerButtonContainer = wrapper.findByClassName(appliedThemeStyles[containerClass]); + expect(triggerButtonContainer).not.toBeNull(); + const items = triggerButtonContainer?.findAllByClassName(appliedTriggerStyles['trigger-wrapper']); + expect(items?.length).toEqual(mockDrawers.length); + + fireEvent.pointerEnter(items![0].getElement()); + + const tooltipWrapper = result.getByTestId(testDrawer.ariaLabels.drawerName); + expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); + expect(tooltipWrapper.classList.contains(appliedTriggerStyles['trigger-tooltip'])).toBeTruthy(); + expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); + + fireEvent.pointerLeave(items![0].getElement()); + expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); + + fireEvent.pointerEnter(items![0].getElement()); + + expect(tooltipWrapper.classList.contains(tooltipStyles.root)).toBeTruthy(); + expect(tooltipWrapper.classList.contains(appliedTriggerStyles['trigger-tooltip'])).toBeTruthy(); + expect(result.getByText(testDrawer.ariaLabels.drawerName)).toBeTruthy(); + + fireEvent.keyDown(items![0].getElement(), { + ...mockEventBubble, + key: 'Escape', + code: KeyCode.escape, + }); + expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); + }); - testIf(theme === 'refresh')('tooltip does not render on trigger focus via close button', async () => { + testIf(theme !== 'classic')('tooltip does not render on trigger focus via close button', () => { const mockDrawers = [testDrawer]; const result = render(); const wrapper = createWrapper(result.container); + const appliedThemeStyles = theme === 'refresh' ? visualRefreshStyles : toolbarStyles; + const containerClass = `drawers-${size === 'mobile' ? 'mobile' : 'desktop'}-triggers-container`; expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); - const triggerButtonContainer = wrapper.findByClassName( - visualRefreshStyles[`drawers-${size === 'mobile' ? 'mobile' : 'desktop'}-triggers-container`] - ); + const triggerButtonContainer = wrapper.findByClassName(appliedThemeStyles[containerClass]); expect(triggerButtonContainer).not.toBeNull(); const drawerTrigger = triggerButtonContainer!.find( `button[data-testid="awsui-app-layout-trigger-${testDrawer.id}"]` ); drawerTrigger?.click(); - await waitFor(() => { - expect(result.getByText('Security')).toBeTruthy(); - expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); - }); + expect(result.getByText('Security')).toBeTruthy(); + expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); const closeButton = wrapper.findDrawer()?.find(`button[title="${testDrawer.ariaLabels.drawerName}-close-button"]`); expect(closeButton).not.toBeNull(); closeButton?.click(); - await waitFor(() => { - expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); - }); + expect(() => result.getByTestId(testDrawer.ariaLabels.drawerName)).toThrow(); }); }); diff --git a/src/app-layout/__tests__/trigger-button.test.tsx b/src/app-layout/__tests__/trigger-button.test.tsx index 9230092265..8a7824a1c1 100644 --- a/src/app-layout/__tests__/trigger-button.test.tsx +++ b/src/app-layout/__tests__/trigger-button.test.tsx @@ -34,6 +34,11 @@ const mockProps = { hasTooltip: false, isForPreviousActiveDrawer: false, }; +const mockHorizontalToolbarProps = { + hideTooltipOnFocus: false, + hasOpenDrawer: false, + isMobile: false, +}; const mockOtherEl = { class: 'other-el-class', text: 'other-element', @@ -84,7 +89,7 @@ const renderVisualRefreshToolbarTriggerButton = ( props: Partial = {}, ref: React.Ref = null ) => { - const renderProps = { ...mockProps, ...props }; + const renderProps = { ...mockProps, ...mockHorizontalToolbarProps, ...props }; const { container, rerender, getByTestId, getByText } = render(
@@ -92,7 +97,7 @@ const renderVisualRefreshToolbarTriggerButton = (
); const wrapper = createWrapper(container).findByClassName(toolbarTriggerButtonStyles['trigger-wrapper'])!; - return { wrapper, rerender, getByTestId, getByText }; + return { wrapper, rerender, getByTestId, getByText, container }; }; describe('Visual refresh trigger-button (not in appLayoutWidget toolbar)', () => { @@ -160,7 +165,6 @@ describe('Visual refresh trigger-button (not in appLayoutWidget toolbar)', () => ); - const ref: React.MutableRefObject = React.createRef(); const { wrapper, getByTestId } = renderVisualRefreshTriggerButton( { iconName: hasIconSvg ? undefined : (mockProps.iconName as IconProps.Name), @@ -169,8 +173,7 @@ describe('Visual refresh trigger-button (not in appLayoutWidget toolbar)', () => { isMobile, hasOpenDrawer, - }, - ref + } ); expect(wrapper).not.toBeNull(); @@ -465,4 +468,174 @@ describe('Visual Refresh Toolbar trigger-button', () => { expect(button).toBeTruthy(); expect(button?.findIcon()).toBeNull(); }); + + describe('Shared trigger wrapper events', () => { + describe.each([true, false] as const)('isMobile=%s', isMobile => { + describe.each([true, false] as const)('hastTooltip=%s', hasTooltip => { + test('Is focusable using the forwarded ref with no tooltip', () => { + const ref: React.MutableRefObject = React.createRef(); + const { wrapper, getByTestId, getByText } = renderVisualRefreshToolbarTriggerButton( + { + hasTooltip, + isMobile, + hideTooltipOnFocus: true, + }, + ref + ); + expect(getByTestId(mockTestId)).toBeTruthy(); + const button = wrapper!.find('button'); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + expect(() => getByText(mockTooltipText)).toThrow(); + expect(button).toBeTruthy(); + expect(document.activeElement).not.toBe(button!.getElement()); + (ref.current as any)?.focus(mockEventBubbleWithShiftFocus); + expect(document.activeElement).toBe(button!.getElement()); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + }); + + test('pointer events work properly', () => { + const { wrapper, getByText, getByTestId } = renderVisualRefreshToolbarTriggerButton({ + hasTooltip, + isMobile, + }); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + expect(() => getByText(mockTooltipText)).toThrow(); + fireEvent.pointerEnter(wrapper!.getElement()); + if (hasTooltip) { + expect(getByText(mockTooltipText)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(true); + //trigger event again to assert the tooltip remains + fireEvent.pointerDown(wrapper!.getElement()); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(true); + } else { + expect(() => getByText(mockTooltipText)).toThrow(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + } + fireEvent.pointerLeave(wrapper!.getElement(), mockEventBubble); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(() => getByText(mockTooltipText)).toThrow(); + }); + + test('Focus and blur events work properly', () => { + const { wrapper, getByText, getByTestId } = renderVisualRefreshToolbarTriggerButton({ + hasTooltip, + isMobile, + }); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + expect(() => getByText(mockTooltipText)).toThrow(); + fireEvent.focus(wrapper!.getElement()); + + if (hasTooltip) { + expect(getByText(mockTooltipText)).toBeTruthy(); + } else { + expect(() => getByText(mockTooltipText)).toThrow(); + } + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(hasTooltip); + + fireEvent.blur(wrapper!.getElement()); + expect(wrapper.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(() => getByText(mockTooltipText)).toThrow(); + }); + + testIf(hasTooltip)('Tooltip can be hidden on escape key press when open', async () => { + const { wrapper, getByText, getByTestId } = await renderVisualRefreshToolbarTriggerButton({ + hasTooltip, + isMobile, + }); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + expect(() => getByText(mockTooltipText)).toThrow(); + fireEvent.focus(wrapper!.getElement()); + expect(getByText(mockTooltipText)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(true); + + fireEvent.keyDown(wrapper!.getElement(), { + ...mockEventBubble, + key: 'Escape', + code: KeyCode.escape, + }); + + expect(wrapper.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(() => getByText(mockTooltipText)).toThrow(); + }); + + testIf(hasTooltip)( + 'Does not show tooltip on pointerEnter or focus when there is no arialLabel nor tooltipText', + async () => { + const { wrapper, getByTestId } = await renderVisualRefreshToolbarTriggerButton({ + ariaLabel: '', + tooltipText: '', + }); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + + fireEvent.pointerEnter(wrapper!.getElement()); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + + fireEvent.focus(wrapper!.getElement()); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + } + ); + }); + testIf(isMobile)('Does not show tooltip if hasOpenDrawer', async () => { + const { wrapper, getByTestId } = await renderVisualRefreshToolbarTriggerButton({ + isMobile, + hasOpenDrawer: true, + }); + expect(getByTestId(mockTestId)).toBeTruthy(); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper!.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + fireEvent.focus(wrapper!.getElement()); + expect( + wrapper!.getElement().classList.contains(toolbarTriggerButtonStyles['trigger-wrapper-tooltip-visible']) + ).toBe(false); + expect(wrapper.findByClassName(toolbarTriggerButtonStyles['trigger-tooltip'])).toBeNull(); + }); + }); + }); }); diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index c367ca135f..4169a07728 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -33,8 +33,11 @@ interface DrawerTriggersProps { drawers: ReadonlyArray; onActiveDrawerChange: ((drawerId: string | null) => void) | undefined; + splitPanelOpen?: boolean; + splitPanelPosition?: AppLayoutProps.SplitPanelPreferences['position']; splitPanelToggleProps: SplitPanelToggleProps | undefined; splitPanelFocusRef: React.Ref | undefined; + // splitPanelResizeRef: RefObject | undefined; onSplitPanelToggle: (() => void) | undefined; } @@ -44,7 +47,10 @@ export function DrawerTriggers({ drawers, drawersFocusRef, onActiveDrawerChange, + splitPanelOpen, + splitPanelPosition = 'bottom', splitPanelFocusRef, + // splitPanelResizeRef, splitPanelToggleProps, onSplitPanelToggle, }: DrawerTriggersProps) { @@ -83,10 +89,12 @@ export function DrawerTriggers({ const { visibleItems, overflowItems } = splitItems(drawers, getIndexOfOverflowItem(), activeDrawerId ?? null); const overflowMenuHasBadge = !!overflowItems.find(item => item.badge); const toolsOnlyMode = drawers.length === 1 && drawers[0].id === TOOLS_DRAWER_ID; + const hasOpenDrawer = + (!!activeDrawerId && activeDrawerId !== null) || (splitPanelPosition === 'side' && splitPanelOpen); return (