Skip to content

Commit

Permalink
feat(UsaModalCloseButton): implement UsaModalCloseButton component
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #57
  • Loading branch information
patrickcate committed Jan 16, 2022
1 parent f41f05e commit 95ae5bb
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/components/UsaModalCloseButton/UsaModalCloseButton.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import UsaModalCloseButton from './UsaModalCloseButton.vue'

const defaultProps = {
ariaLabel: 'Close Modal',
}

export default {
component: UsaModalCloseButton,
title: 'Components/UsaModalCloseButton',
argTypes: {
ariaLabel: {
control: { type: 'text' },
},
defaultSlot: {
control: { type: 'text' },
},
},
args: {
ariaLabel: defaultProps.ariaLabel,
defaultSlot: '',
},
}

const DefaultTemplate = (args, { argTypes }) => ({
components: { UsaModalCloseButton },
props: Object.keys(argTypes),
setup() {
return { ...args }
},
template: `<UsaModalCloseButton :aria-label="ariaLabel"><template #default="svgSpritePath">${args.defaultSlot}</template></UsaModalCloseButton>`,
})

export const DefaultModalCloseButton = DefaultTemplate.bind({})
DefaultModalCloseButton.args = {
...defaultProps,
}
DefaultModalCloseButton.storyName = 'Default'

export const CustomIconModalCloseButton = DefaultTemplate.bind({})
CustomIconModalCloseButton.args = {
...defaultProps,
defaultSlot: 'x',
}
CustomIconModalCloseButton.storyName = 'Custom Icon'
84 changes: 84 additions & 0 deletions src/components/UsaModalCloseButton/UsaModalCloseButton.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import '@module/uswds/dist/css/uswds.min.css'
import { mount } from '@cypress/vue'
import UsaModalCloseButton from './UsaModalCloseButton.vue'

describe('UsaModalCloseButton', () => {
it('renders the component', () => {
mount(UsaModalCloseButton, {
props: {
ariaLabel: 'Close Modal',
},
global: {
provide: {
'vueUswds.svgSpritePath': '/test.svg',
},
},
})

cy.get('button.usa-modal__close')
.should('have.class', 'usa-button')
.and('have.attr', 'aria-label')
.and('contain', 'Close Modal')

cy.get('svg.usa-icon')
.as('svgIcon')
.should('have.attr', 'aria-hidden')
.and('contain', 'true')

cy.get('@svgIcon').should('have.attr', 'focusable').and('contain', 'false')

cy.get('@svgIcon').should('have.attr', 'role').and('contain', 'img')

cy.get('@svgIcon')
.find('use')
.should('have.attr', 'xlink:href')
.and('contain', '/test.svg#close')
})

it('emit `closeModal` event when clicked', () => {
mount(UsaModalCloseButton, {
props: {
ariaLabel: 'Close Modal',
},
}).as('wrapper')

// We need to force the click event since the button is displayed off-screen.
cy.get('.usa-modal__close').click({ force: true })

cy.get('@wrapper')
.vue()
.then(vm => {
expect(vm.emitted()).to.have.property('closeModal')
expect(vm.emitted('closeModal')).to.have.length(1)
})
})

it('shows scoped slot content', () => {
mount(UsaModalCloseButton, {
props: {
ariaLabel: 'Close Modal',
},
slots: {
default: props => `${props.svgSpritePath}`,
},
global: {
provide: {
'vueUswds.svgSpritePath': '/test.svg',
},
},
})

cy.get('.usa-modal__close').should('contain', '/test.svg')
})

it('warns in console about required `aria-label` prop', () => {
cy.stub(window.console, 'warn').as('consoleWarn')

mount(UsaModalCloseButton, {})

cy.get('@consoleWarn').should(
'be.calledWith',
`[Vue warn]: Missing required prop: "ariaLabel"`
)
})
})
35 changes: 35 additions & 0 deletions src/components/UsaModalCloseButton/UsaModalCloseButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup>
import { inject } from 'vue'
import { SVG_SPRITE_PATH } from '@/utils/constants.js'
const svgSpritePath = inject('vueUswds.svgSpritePath', SVG_SPRITE_PATH)
const emit = defineEmits(['closeModal'])
defineProps({
ariaLabel: {
type: String,
required: true,
},
})
</script>

<template>
<button
class="usa-button usa-modal__close"
:aria-label="ariaLabel"
@click="emit('closeModal')"
>
<slot :svgSpritePath="svgSpritePath">
<svg
v-if="svgSpritePath"
class="usa-icon"
aria-hidden="true"
focusable="false"
role="img"
>
<use v-bind="{ 'xlink:href': `${svgSpritePath}#close}` }"></use>
</svg>
</slot>
</button>
</template>
4 changes: 4 additions & 0 deletions src/components/UsaModalCloseButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import UsaModalCloseButton from './UsaModalCloseButton.vue'

export { UsaModalCloseButton }
export default UsaModalCloseButton

0 comments on commit 95ae5bb

Please sign in to comment.