Skip to content

Commit

Permalink
feat(UsaSearch): implement UsaSearch component
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #65
  • Loading branch information
patrickcate committed Feb 19, 2022
1 parent fe68e70 commit 9282bc0
Show file tree
Hide file tree
Showing 4 changed files with 458 additions and 0 deletions.
151 changes: 151 additions & 0 deletions src/components/UsaSearch/UsaSearch.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import UsaSearch from './UsaSearch.vue'

const defaultProps = {
variant: UsaSearch.props.variant.default,
modelValue: UsaSearch.props.modelValue.default,
label: UsaSearch.props.label.default,
buttonLabel: UsaSearch.props.buttonLabel.default,
inputAttrs: UsaSearch.props.inputAttrs.default(),
id: UsaSearch.props.id.default,
customClasses: UsaSearch.props.customClasses.default(),
}

export default {
component: UsaSearch,
title: 'Components/UsaSearch',
argTypes: {
variant: {
options: ['small', 'medium', 'big'],
control: {
type: 'select',
},
},
modelValue: {
control: { type: 'text' },
},
label: {
control: { type: 'text' },
},
buttonLabel: {
control: { type: 'text' },
},
inputAttrs: {
control: { type: 'object' },
},
id: {
control: { type: 'text' },
},
customClasses: {
control: { type: 'object' },
},
iconSlot: {
control: { type: 'text' },
},
},
args: {
variant: defaultProps.variant,
modelValue: defaultProps.modelValue,
label: defaultProps.label,
buttonLabel: defaultProps.buttonLabel,
inputAttrs: defaultProps.inputAttrs,
id: defaultProps.id,
customClasses: defaultProps.customClasses,
iconSlot: '',
},
}

const DefaultTemplate = (args, { argTypes }) => ({
components: { UsaSearch },
props: Object.keys(argTypes),
setup() {
return { ...args }
},
template: `<UsaSearch
v-model="modelValue"
:variant="variant"
:label="label"
:buttonLabel="buttonLabel"
:inputAttrs="inputAttrs"
:id="id"
:customClasses="customClasses"
>
<template v-if="${!!args.iconSlot}" #icon>${args.iconSlot}</template>
</UsaSearch>`,
})

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

export const DefaultValueSearch = DefaultTemplate.bind({})
DefaultValueSearch.args = {
...defaultProps,
modelValue: 'Test search term',
}
DefaultValueSearch.storyName = 'Default Value'

export const SmallVariantSearch = DefaultTemplate.bind({})
SmallVariantSearch.args = {
...defaultProps,
variant: 'small',
}
SmallVariantSearch.storyName = 'Small'

export const BigVariantSearch = DefaultTemplate.bind({})
BigVariantSearch.args = {
...defaultProps,
variant: 'big',
}
BigVariantSearch.storyName = 'Big'

export const CustomLabelSearch = DefaultTemplate.bind({})
CustomLabelSearch.args = {
...defaultProps,
label: 'Custom form label',
}
CustomLabelSearch.storyName = 'Custom Form Label'

export const CustomButtonLabelSearch = DefaultTemplate.bind({})
CustomButtonLabelSearch.args = {
...defaultProps,
buttonLabel: 'Submit',
}
CustomButtonLabelSearch.storyName = 'Custom Button Label'

export const InputAttrsSearch = DefaultTemplate.bind({})
InputAttrsSearch.args = {
...defaultProps,
inputAttrs: {
placeholder: 'Enter search terms',
},
}
InputAttrsSearch.storyName = 'Custom Input Attributes'

export const IconSlotSearch = DefaultTemplate.bind({})
IconSlotSearch.args = {
...defaultProps,
variant: 'small',
iconSlot: '<strong>-></strong>',
}
IconSlotSearch.storyName = 'Icon Slot'

export const CustomIdSearch = DefaultTemplate.bind({})
CustomIdSearch.args = {
...defaultProps,
id: 'custom-id',
}
CustomIdSearch.storyName = 'Custom ID'

export const CustomClassesSearch = DefaultTemplate.bind({})
CustomClassesSearch.args = {
...defaultProps,
customClasses: {
label: ['test-label-class'],
input: ['test-input-class'],
button: ['test-button-class'],
icon: ['test-icon-class'],
},
}
CustomClassesSearch.storyName = 'Custom CSS Classes'
195 changes: 195 additions & 0 deletions src/components/UsaSearch/UsaSearch.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import '@module/uswds/dist/css/uswds.min.css'
import { mount } from '@cypress/vue'
import UsaSearch from './UsaSearch.vue'
import { h } from 'vue'

describe('UsaSearch', () => {
it('renders the component', () => {
mount(UsaSearch, {})

cy.get('form.usa-search')
.should('have.class', 'usa-search--medium')
.and('have.attr', 'role')
.and('contain', 'search')

cy.get('input.usa-input')
.as('input')
.should('have.attr', 'type')
.and('contain', 'search')

cy.get('label.usa-label')
.as('label')
.should('have.class', 'usa-sr-only')
.and('have.attr', 'for')
cy.get('@label').should('contain', 'Search')

cy.get('@input').should('have.attr', 'name').and('contain', 'search')
cy.get('@input').should('have.attr', 'id')

cy.get('button.usa-button')
.should('have.attr', 'type')
.and('contain', 'submit')

cy.get('span.usa-search__submit-text').should('contain', 'Search')

cy.get('img.usa-search__submit-icon')
.as('buttonIcon')
.should('have.attr', 'src')
.and('contain', '/assets/img/usa-icons-bg/search--white.svg')

cy.get('@buttonIcon').should('have.attr', 'alt').and('contain', 'Search')
})

it('displays each variant size type', () => {
mount(UsaSearch, {}).as('wrapper')

cy.get('.usa-search').should('have.class', 'usa-search--medium')

cy.get('.usa-search__submit-text').should('exist')
cy.get('.usa-search__submit-icon').should('not.be.visible')

cy.get('@wrapper').invoke('setProps', { variant: 'big' })

cy.get('.usa-search').should('have.class', 'usa-search--big')

cy.get('.usa-search__submit-text').should('exist')
cy.get('.usa-search__submit-icon').should('not.be.visible')

cy.get('@wrapper').invoke('setProps', { variant: 'small' })

cy.get('.usa-search').should('have.class', 'usa-search--small')

cy.get('.usa-search__submit-text').should('not.exist')
cy.get('.usa-search__submit-icon').should('be.visible')

cy.get('@wrapper').invoke('setProps', { variant: 'medium' })

cy.get('.usa-search').should('have.class', 'usa-search--medium')

cy.get('.usa-search__submit-text').should('exist')
cy.get('.usa-search__submit-icon').should('not.be.visible')
})

it('uses custom `id`, `label`, `inputAttrs`, and `buttonLabel` prop values', () => {
mount(UsaSearch, {
props: {
label: 'Test label',
id: 'custom-id',
buttonLabel: 'Test button label',
inputAttrs: {
placeholder: 'Test placeholder',
},
},
})

cy.get('.usa-label').as('label').should('contain', 'Test label')
cy.get('@label').should('have.attr', 'for').and('contain', 'custom-id')

cy.get('.usa-input')
.should('have.id', 'custom-id')
.and('have.attr', 'placeholder')
.and('contain', 'Test placeholder')

cy.get('.usa-search__submit-text').should('contain', 'Test button label')
})

it('uses `icon` slot', () => {
mount(UsaSearch, {
props: {
// Icon only displays with small variant.
variant: 'small',
},
slots: {
icon: () =>
h(
'strong',
{ className: 'usa-search__submit-icon' },
'Custom icon slot'
),
},
})

cy.get('.usa-button > strong').should('contain', 'Custom icon slot')
})

it('icon image path uses custom injected `imagePath` value', () => {
mount(UsaSearch, {
props: {},
global: {
provide: {
'vueUswds.imagePath': '/custom/image/path',
},
},
})

cy.get('.usa-search__submit-icon')
.should('have.attr', 'src')
.and('contain', '/custom/image/path/usa-icons-bg/search--white.svg')
})

it('emits input event when changed', () => {
mount(UsaSearch, {
props: {
modelValue: 'Test',
},
}).as('wrapper')

cy.get('.usa-input').as('input').should('have.value', 'Test')

cy.get('@wrapper')
.vue()
.then(vm => {
expect(vm.emitted()).to.not.have.property('update:modalValue')
})

cy.get('@input').type(' search term')

cy.get('@wrapper')
.vue()
.then(vm => {
expect(vm.emitted()).to.have.property('update:modelValue')
const currentRangeEvent = vm.emitted('update:modelValue')
expect(currentRangeEvent).to.have.length(12)
expect(currentRangeEvent[currentRangeEvent.length - 1]).to.contain(
'Test search term'
)
})
})

it('uses custom CSS classes', () => {
mount(UsaSearch, {
props: {
customClasses: {
label: ['test-label-class'],
input: ['test-input-class'],
button: ['test-button-class'],
icon: ['test-icon-class'],
},
},
attrs: {
class: ['test-component-class'],
},
})

cy.get('.usa-search').should('have.class', 'test-component-class')
cy.get('.usa-label').should('have.class', 'test-label-class')
cy.get('.usa-input').should('have.class', 'test-input-class')
cy.get('.usa-button').should('have.class', 'test-button-class')
cy.get('.usa-search__submit-icon').should('have.class', 'test-icon-class')
})

it('warns in console invalid `variant` prop value', () => {
cy.stub(window.console, 'warn').as('consoleWarn')

mount(UsaSearch, {
props: {
variant: 'invalidvariant',
},
})

cy.get('@consoleWarn').should(
'be.calledWith',
`'invalidvariant' is not a valid search variant`
)
})
})
Loading

0 comments on commit 9282bc0

Please sign in to comment.