Skip to content

Commit

Permalink
feat(UsaNavDropdownButton): implement UsaNavDropdownButton component
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickcate committed Mar 12, 2022
1 parent 368d537 commit 31acdb7
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import UsaNavDropdownButton from './UsaNavDropdownButton.vue'
import { ref, reactive } from 'vue'

const dropdownId = ref('test-dropdown-id')
const dropdownItems = reactive({ 'test-dropdown-id': false })

export default {
component: UsaNavDropdownButton,
title: 'Components/UsaNavDropdownButton',
argTypes: {
defaultSlot: {
control: { type: 'text' },
},
},
args: {
defaultSlot: 'Test dropdown button',
},
decorators: [
() => ({
template: '<story />',
provide: {
dropdownId: dropdownId,
toggleDropdown: id => {
dropdownItems[id] = !dropdownItems[id]
},
dropdownItems: dropdownItems,
},
}),
],
}

const DefaultTemplate = (args, { argTypes }) => ({
components: { UsaNavDropdownButton },
props: Object.keys(argTypes),
setup() {
return { ...args }
},
template: `<div class="usa-header usa-header--basic"><ul class="usa-nav__primary usa-accordion"><li class="usa-nav__primary-item"><UsaNavDropdownButton>${args.defaultSlot}</UsaNavDropdownButton></li></ul></div>`,
})

export const DefaultNavDropdownButton = DefaultTemplate.bind({})
DefaultNavDropdownButton.args = {}
DefaultNavDropdownButton.storyName = 'Default'
105 changes: 105 additions & 0 deletions src/components/UsaNavDropdownButton/UsaNavDropdownButton.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import '@module/uswds/dist/css/uswds.min.css'
import { mount } from '@cypress/vue'
import { ref, reactive } from 'vue'
import UsaNavDropdownButton from './UsaNavDropdownButton.vue'

describe('UsaNavDropdownButton', () => {
it('renders the component', () => {
mount(UsaNavDropdownButton, {
attrs: {
'data-test': 'test-attr',
},
slots: {
default: () => 'Test dropdown button',
},
global: {
provide: {
dropdownId: ref('test-dropdown-id'),
dropdownItems: reactive({
'test-dropdown-id': false,
}),
toggleDropdown: cy.stub().as('toggleDropdown'),
},
},
}).as('wrapper')

cy.get('button.usa-accordion__button')
.as('button')
.should('have.class', 'usa-nav__link')

cy.get('@button')
.should('have.attr', 'data-test')
.and('contain', 'test-attr')

cy.get('@button').should('have.attr', 'aria-expanded').and('contain', false)

cy.get('@button')
.should('have.attr', 'aria-controls')
.and('contain', 'test-dropdown-id')

cy.get('@button').find('span').should('contain', 'Test dropdown button')

cy.get('@toggleDropdown').should('not.be.called')

cy.get('@button').click()

cy.get('@toggleDropdown').should('be.calledWith', 'test-dropdown-id')
})

it('renders as button with vue-router active prop and open attributes', () => {
const routerLinkStub = {
name: 'RouterLink',
data() {
return {
isActive: true,
isExactActive: false,
}
},
template:
'<slot :isActive="isActive" :isExactActive="isExactActive"></slot>',
}

mount(UsaNavDropdownButton, {
attrs: {
'data-test': 'test-attr',
},
slots: {
default: () => 'Test dropdown button',
},
global: {
stubs: { 'router-link': routerLinkStub },
provide: {
'vueUswds.routerComponentName': 'router-link',
dropdownId: ref('test-dropdown-id'),
dropdownItems: reactive({
'test-dropdown-id': true,
}),
toggleDropdown: cy.stub().as('toggleDropdown'),
},
},
}).as('wrapper')

cy.get('button.usa-accordion__button')
.as('button')
.should('have.class', 'usa-nav__link')
.and('have.class', 'usa-current')

cy.get('@button')
.should('have.attr', 'data-test')
.and('contain', 'test-attr')

cy.get('@button').should('have.attr', 'aria-expanded').and('contain', true)

cy.get('@button')
.should('have.attr', 'aria-controls')
.and('contain', 'test-dropdown-id')

cy.get('@button').find('span').should('contain', 'Test dropdown button')

cy.get('@toggleDropdown').should('not.be.called')

cy.get('@button').click()

cy.get('@toggleDropdown').should('be.calledWith', 'test-dropdown-id')
})
})
50 changes: 50 additions & 0 deletions src/components/UsaNavDropdownButton/UsaNavDropdownButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script>
export default {
inheritAttrs: false,
}
</script>

<script setup>
import { inject, toRef } from 'vue'
import { ROUTER_COMPONENT_NAME } from '@/utils/constants.js'
const routerComponentName = inject(
'vueUswds.routerComponentName',
ROUTER_COMPONENT_NAME
)
const dropdownId = inject('dropdownId')
const toggleDropdown = inject('toggleDropdown')
const dropdownItems = inject('dropdownItems')
const isOpen = toRef(dropdownItems, dropdownId.value)
</script>

<template>
<component
:is="routerComponentName"
v-if="routerComponentName"
v-slot="{ isActive, isExactActive }"
custom
>
<button
v-bind="$attrs"
class="usa-accordion__button usa-nav__link"
:class="[{ 'usa-current': isActive || isExactActive }]"
:aria-expanded="isOpen"
:aria-controls="dropdownId"
@click="toggleDropdown(dropdownId)"
>
<span><slot></slot></span>
</button>
</component>
<button
v-else
v-bind="$attrs"
class="usa-accordion__button usa-nav__link"
:aria-expanded="isOpen"
:aria-controls="dropdownId"
@click="toggleDropdown(dropdownId)"
>
<span><slot></slot></span>
</button>
</template>
4 changes: 4 additions & 0 deletions src/components/UsaNavDropdownButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import UsaNavDropdownButton from './UsaNavDropdownButton.vue'

export { UsaNavDropdownButton }
export default UsaNavDropdownButton

0 comments on commit 31acdb7

Please sign in to comment.