Skip to content

Commit

Permalink
feat(b-form-tags): add reset method (#6104)
Browse files Browse the repository at this point in the history
* feat(b-form-tags): add `reset` method

* Update form-tags.js
  • Loading branch information
jacobmllr95 committed Nov 30, 2020
1 parent 2dc6b9d commit d610291
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 36 deletions.
2 changes: 2 additions & 0 deletions src/components/form-tags/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ The default slot scope properties are as follows:
| `isLimitReached` | Boolean | <span class="badge badge-secondary">v2.17.0+</span> `true` if a `limit` is configured and the amount of tags has reached the limit |
| `disableAddButton` | Boolean | Will be `true` if the tag(s) in the input cannot be added (all invalid and/or duplicates) |
| `disabled` | Boolean | `true` if the component is in the disabled state. Value of the `disabled` prop |
| `required` | Boolean | <span class="badge badge-secondary">v2.20.0+</span> The value of the `required` prop |
| `form` | String | <span class="badge badge-secondary">v2.20.0+</span> The value of the `form` prop |
| `state` | Boolean | The contextual state of the component. Value of the `state` prop. Possible values are `true`, `false` or `null` |
| `size` | String | The value of the `size` prop |
| `limit` | String | <span class="badge badge-secondary">v2.17.0+</span> The value of the `limit` prop |
Expand Down
26 changes: 24 additions & 2 deletions src/components/form-tags/form-tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Vue from '../../vue'
import { NAME_FORM_TAGS } from '../../constants/components'
import { CODE_BACKSPACE, CODE_DELETE, CODE_ENTER } from '../../constants/key-codes'
import { EVENT_OPTIONS_PASSIVE } from '../../constants/events'
import { SLOT_NAME_DEFAULT } from '../../constants/slot-names'
import { RX_SPACES } from '../../constants/regex'
import cssEscape from '../../utils/css-escape'
Expand All @@ -19,7 +20,7 @@ import {
requestAF,
select
} from '../../utils/dom'
import { stopEvent } from '../../utils/events'
import { eventOn, eventOff, stopEvent } from '../../utils/events'
import { pick } from '../../utils/object'
import { isEvent, isNumber, isString } from '../../utils/inspect'
import { escapeRegExp, toString, trim, trimLeft } from '../../utils/string'
Expand Down Expand Up @@ -227,7 +228,8 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
return {
input: this.onInputInput,
change: this.onInputChange,
keydown: this.onInputKeydown
keydown: this.onInputKeydown,
reset: this.reset
}
},
computedSeparator() {
Expand Down Expand Up @@ -315,6 +317,16 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
// if the cleaned tags are not equal to the value prop
this.tags = cleanTags(this.value)
},
mounted() {
// Listen for form reset events, to reset the tags input
const $form = closest('form', this.$el)
if ($form) {
eventOn($form, 'reset', this.reset, EVENT_OPTIONS_PASSIVE)
this.$on('hook:beforeDestroy', () => {
eventOff($form, 'reset', this.reset, EVENT_OPTIONS_PASSIVE)
})
}
},
methods: {
addTag(newTag) {
newTag = isString(newTag) ? newTag : this.newTag
Expand Down Expand Up @@ -368,6 +380,15 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
this.focus()
})
},
reset() {
this.newTag = ''
this.tags = []

this.$nextTick(() => {
this.removedTags = []
this.tagsState = cleanTagsState()
})
},
// --- Input element event handlers ---
onInputInput(evt) {
/* istanbul ignore next: hard to test composition events */
Expand Down Expand Up @@ -746,6 +767,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
// Methods
removeTag: this.removeTag,
addTag: this.addTag,
reset: this.reset,
// <input> :id="inputId"
inputId: computedInputId,
// Invalid/Duplicate state information
Expand Down
149 changes: 149 additions & 0 deletions src/components/form-tags/form-tags.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,155 @@ describe('form-tags', () => {
wrapper.destroy()
})

it('reset() method works', async () => {
const wrapper = mount(BFormTags, {
propsData: {
value: ['one', 'two'],
addOnChange: true,
tagValidator: tag => tag.length < 4
}
})

expect(wrapper.element.tagName).toBe('DIV')
expect(wrapper.vm.tags).toEqual(['one', 'two'])
expect(wrapper.vm.newTag).toEqual('')

const $input = wrapper.find('input')
expect($input.exists()).toBe(true)
expect($input.element.value).toBe('')
expect($input.element.type).toBe('text')

$input.element.value = 'three'
await $input.trigger('input')
await $input.trigger('change')
expect(wrapper.vm.newTag).toEqual('three')
expect(wrapper.vm.tags).toEqual(['one', 'two'])
expect(wrapper.vm.tagsState.invalid).toContain('three')

const $tags = wrapper.findAll('.badge')
expect($tags.length).toBe(2)
await $tags
.at(1)
.find('button')
.trigger('click')
expect(wrapper.vm.tags).toEqual(['one'])
expect(wrapper.vm.removedTags).toContain('two')

wrapper.vm.reset()
await waitNT(wrapper.vm)

expect(wrapper.vm.newTag).toEqual('')
expect(wrapper.vm.tags).toEqual([])
expect(wrapper.vm.removedTags).toEqual([])
expect(wrapper.vm.tagsState.invalid).toEqual([])

wrapper.destroy()
})

it('native reset event works', async () => {
const wrapper = mount(BFormTags, {
propsData: {
value: ['one', 'two'],
addOnChange: true,
tagValidator: tag => tag.length < 4
}
})

expect(wrapper.element.tagName).toBe('DIV')
expect(wrapper.vm.tags).toEqual(['one', 'two'])
expect(wrapper.vm.newTag).toEqual('')

const $input = wrapper.find('input')
expect($input.exists()).toBe(true)
expect($input.element.value).toBe('')
expect($input.element.type).toBe('text')

$input.element.value = 'three'
await $input.trigger('input')
await $input.trigger('change')
expect(wrapper.vm.newTag).toEqual('three')
expect(wrapper.vm.tags).toEqual(['one', 'two'])
expect(wrapper.vm.tagsState.invalid).toContain('three')

const $tags = wrapper.findAll('.badge')
expect($tags.length).toBe(2)
await $tags
.at(1)
.find('button')
.trigger('click')
expect(wrapper.vm.tags).toEqual(['one'])
expect(wrapper.vm.removedTags).toContain('two')

await $input.trigger('reset')
await waitNT(wrapper.vm)

expect(wrapper.vm.newTag).toEqual('')
expect(wrapper.vm.tags).toEqual([])
expect(wrapper.vm.removedTags).toEqual([])
expect(wrapper.vm.tagsState.invalid).toEqual([])

wrapper.destroy()
})

it('form native reset event triggers reset', async () => {
const App = {
render(h) {
return h('form', [
h(BFormTags, {
props: {
value: ['one', 'two'],
addOnChange: true,
tagValidator: tag => tag.length < 4
}
})
])
}
}
const wrapper = mount(App, {
attachTo: createContainer()
})

expect(wrapper.element.tagName).toBe('FORM')

const formTags = wrapper.findComponent(BFormTags)
expect(formTags.exists()).toBe(true)
expect(formTags.element.tagName).toBe('DIV')
expect(formTags.vm.tags).toEqual(['one', 'two'])
expect(formTags.vm.newTag).toEqual('')

const $input = formTags.find('input')
expect($input.exists()).toBe(true)
expect($input.element.value).toBe('')
expect($input.element.type).toBe('text')

$input.element.value = 'three'
await $input.trigger('input')
await $input.trigger('change')
expect(formTags.vm.newTag).toEqual('three')
expect(formTags.vm.tags).toEqual(['one', 'two'])
expect(formTags.vm.tagsState.invalid).toContain('three')

const $tags = formTags.findAll('.badge')
expect($tags.length).toBe(2)
await $tags
.at(1)
.find('button')
.trigger('click')
expect(formTags.vm.tags).toEqual(['one'])
expect(formTags.vm.removedTags).toContain('two')

// Trigger form's native reset event
wrapper.find('form').trigger('reset')
await waitNT(formTags.vm)

expect(formTags.vm.newTag).toEqual('')
expect(formTags.vm.tags).toEqual([])
expect(formTags.vm.removedTags).toEqual([])
expect(formTags.vm.tagsState.invalid).toEqual([])

wrapper.destroy()
})

it('focuses input when wrapper div clicked', async () => {
const wrapper = mount(BFormTags, {
attachTo: createContainer(),
Expand Down
1 change: 1 addition & 0 deletions src/components/form-tags/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export declare const FormTagsPlugin: BvPlugin
export declare class BFormTags extends BvComponent {
focus: () => void
blur: () => void
reset: () => void
}

// Component: b-form-tag
Expand Down

0 comments on commit d610291

Please sign in to comment.