From 9e6a1031f18261e9d5d95ca22fb4b32c21db69d8 Mon Sep 17 00:00:00 2001 From: Patrick Cate Date: Thu, 22 Feb 2024 00:28:50 -0500 Subject: [PATCH] fix(UsaNav): move mobile menu functionality to `UsaHeader` component Previously the `UsaNav` component needed to be nested under a `UsaNavbar` component. This was not consistent with USWDS styling however. This change makes it so the `UsaNav` can just be a child of a `UsaHeader` component. --- src/components/UsaHeader/UsaHeader.test.js | 110 ++++++++++++++++++ src/components/UsaHeader/UsaHeader.vue | 16 +++ src/components/UsaNav/UsaNav.test.js | 127 +++++++-------------- src/components/UsaNavbar/UsaNavbar.test.js | 43 ++++++- src/components/UsaNavbar/UsaNavbar.vue | 23 ++-- 5 files changed, 219 insertions(+), 100 deletions(-) diff --git a/src/components/UsaHeader/UsaHeader.test.js b/src/components/UsaHeader/UsaHeader.test.js index c59ded6f..2c4a88e8 100644 --- a/src/components/UsaHeader/UsaHeader.test.js +++ b/src/components/UsaHeader/UsaHeader.test.js @@ -1,6 +1,8 @@ import '@module/@uswds/uswds/dist/css/uswds.min.css' import { h } from 'vue' import UsaHeader from './UsaHeader.vue' +import UsaNavbar from '@/components/UsaNavbar' +import UsaNav from '@/components/UsaNav' describe('UsaHeader', () => { it('renders the component', () => { @@ -88,6 +90,114 @@ describe('UsaHeader', () => { cy.get('span').should('contain', 'Megamenu: false') }) + it('provide reactive values to `UsaNav` and `UsaNavbar` components', () => { + const childComponent = { + components: { UsaNavbar, UsaNav }, + template: ` + Test Navbar + + + + + `, + } + + // eslint-disable-next-line cypress/no-assigning-return-values + const wrapper = cy + .mount(UsaHeader, { + slots: { + default: () => h(childComponent), + }, + }) + .its('wrapper') + .as('wrapper') + + cy.get('div.usa-overlay') + .as('overlay') + .should('not.have.class', 'is-visible') + .and('not.be.visible') + + cy.get('.usa-navbar').should('contain', 'Test Navbar') + + cy.get('button.usa-menu-btn') + .as('menuButton') + .should( + 'have.attr', + 'aria-controls', + '__vuswds-id-global-mobile-header-menu' + ) + + cy.get('nav.usa-nav') + .as('nav') + .should('have.id', '__vuswds-id-global-mobile-header-menu') + + cy.get('@nav').should('not.have.class', 'is-visible').and('not.be.visible') + cy.get('@nav').find('> div.usa-nav__inner').should('not.exist') + + cy.get('@nav').find('> button.usa-nav__close').as('closeButton') + + cy.get('body > .usa-overlay').should('not.exist') + cy.get('body > nav').should('not.exist') + cy.get('body > :not(nav)').should('not.have.attr', 'aria-hidden') + cy.get('body > :not(.usa-overlay)').should('not.have.attr', 'aria-hidden') + + wrapper.vue().then(vm => { + const usaHeaderComponent = vm.findComponent(UsaHeader) + expect(usaHeaderComponent.emitted()).to.not.have.property( + 'mobileMenuOpen' + ) + + // Click mobile menu button. + cy.get('@menuButton').click() + + wrapper.vue().then(vm => { + const usaHeaderComponent = vm.findComponent(UsaHeader) + + expect(usaHeaderComponent.emitted()).to.have.property('mobileMenuOpen') + + const currentEvent = usaHeaderComponent.emitted('mobileMenuOpen') + expect(currentEvent).to.have.length(1) + expect(currentEvent[currentEvent.length - 1]).to.contain(true) + }) + }) + + cy.get('body > .usa-overlay').should('exist') + cy.get('body > nav').should('exist') + cy.get('body > :not(nav)') + .should('have.attr', 'aria-hidden') + .and('contain', true) + cy.get('body > :not(.usa-overlay)').should('have.attr', 'aria-hidden') + + cy.get('@overlay').should('have.class', 'is-visible').and('be.visible') + cy.get('@nav').should('have.class', 'is-visible').and('be.visible') + + cy.get('@closeButton').should('have.focus') + + // Click mobile menu close button. + cy.get('@closeButton').click() + + wrapper.vue().then(vm => { + const usaHeaderComponent = vm.findComponent(UsaHeader) + expect(usaHeaderComponent.emitted()).to.have.property('mobileMenuOpen') + + const currentEvent = usaHeaderComponent.emitted('mobileMenuOpen') + expect(currentEvent).to.have.length(2) + expect(currentEvent[currentEvent.length - 1]).to.contain(false) + }) + + cy.get('@menuButton').should('have.focus') + + cy.get('body > .usa-overlay').should('not.exist') + cy.get('body > nav').should('not.exist') + cy.get('body > :not(nav)').should('not.have.attr', 'aria-hidden') + cy.get('body > :not(.usa-overlay)').should('not.have.attr', 'aria-hidden') + + cy.get('@overlay') + .should('not.have.class', 'is-visible') + .and('not.be.visible') + cy.get('@nav').should('not.have.class', 'is-visible').and('not.be.visible') + }) + it('adds custom CSS classes', () => { cy.mount(UsaHeader, { props: { diff --git a/src/components/UsaHeader/UsaHeader.vue b/src/components/UsaHeader/UsaHeader.vue index 664b5861..c8aa07a5 100644 --- a/src/components/UsaHeader/UsaHeader.vue +++ b/src/components/UsaHeader/UsaHeader.vue @@ -1,5 +1,8 @@