Skip to content

Commit d610291

Browse files
authored
feat(b-form-tags): add reset method (bootstrap-vue#6104)
* feat(b-form-tags): add `reset` method * Update form-tags.js
1 parent 2dc6b9d commit d610291

File tree

5 files changed

+235
-36
lines changed

5 files changed

+235
-36
lines changed

src/components/form-tags/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ The default slot scope properties are as follows:
379379
| `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 |
380380
| `disableAddButton` | Boolean | Will be `true` if the tag(s) in the input cannot be added (all invalid and/or duplicates) |
381381
| `disabled` | Boolean | `true` if the component is in the disabled state. Value of the `disabled` prop |
382+
| `required` | Boolean | <span class="badge badge-secondary">v2.20.0+</span> The value of the `required` prop |
383+
| `form` | String | <span class="badge badge-secondary">v2.20.0+</span> The value of the `form` prop |
382384
| `state` | Boolean | The contextual state of the component. Value of the `state` prop. Possible values are `true`, `false` or `null` |
383385
| `size` | String | The value of the `size` prop |
384386
| `limit` | String | <span class="badge badge-secondary">v2.17.0+</span> The value of the `limit` prop |

src/components/form-tags/form-tags.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import Vue from '../../vue'
44
import { NAME_FORM_TAGS } from '../../constants/components'
55
import { CODE_BACKSPACE, CODE_DELETE, CODE_ENTER } from '../../constants/key-codes'
6+
import { EVENT_OPTIONS_PASSIVE } from '../../constants/events'
67
import { SLOT_NAME_DEFAULT } from '../../constants/slot-names'
78
import { RX_SPACES } from '../../constants/regex'
89
import cssEscape from '../../utils/css-escape'
@@ -19,7 +20,7 @@ import {
1920
requestAF,
2021
select
2122
} from '../../utils/dom'
22-
import { stopEvent } from '../../utils/events'
23+
import { eventOn, eventOff, stopEvent } from '../../utils/events'
2324
import { pick } from '../../utils/object'
2425
import { isEvent, isNumber, isString } from '../../utils/inspect'
2526
import { escapeRegExp, toString, trim, trimLeft } from '../../utils/string'
@@ -227,7 +228,8 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
227228
return {
228229
input: this.onInputInput,
229230
change: this.onInputChange,
230-
keydown: this.onInputKeydown
231+
keydown: this.onInputKeydown,
232+
reset: this.reset
231233
}
232234
},
233235
computedSeparator() {
@@ -315,6 +317,16 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
315317
// if the cleaned tags are not equal to the value prop
316318
this.tags = cleanTags(this.value)
317319
},
320+
mounted() {
321+
// Listen for form reset events, to reset the tags input
322+
const $form = closest('form', this.$el)
323+
if ($form) {
324+
eventOn($form, 'reset', this.reset, EVENT_OPTIONS_PASSIVE)
325+
this.$on('hook:beforeDestroy', () => {
326+
eventOff($form, 'reset', this.reset, EVENT_OPTIONS_PASSIVE)
327+
})
328+
}
329+
},
318330
methods: {
319331
addTag(newTag) {
320332
newTag = isString(newTag) ? newTag : this.newTag
@@ -368,6 +380,15 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
368380
this.focus()
369381
})
370382
},
383+
reset() {
384+
this.newTag = ''
385+
this.tags = []
386+
387+
this.$nextTick(() => {
388+
this.removedTags = []
389+
this.tagsState = cleanTagsState()
390+
})
391+
},
371392
// --- Input element event handlers ---
372393
onInputInput(evt) {
373394
/* istanbul ignore next: hard to test composition events */
@@ -746,6 +767,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({
746767
// Methods
747768
removeTag: this.removeTag,
748769
addTag: this.addTag,
770+
reset: this.reset,
749771
// <input> :id="inputId"
750772
inputId: computedInputId,
751773
// Invalid/Duplicate state information

src/components/form-tags/form-tags.spec.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,155 @@ describe('form-tags', () => {
528528
wrapper.destroy()
529529
})
530530

531+
it('reset() method works', async () => {
532+
const wrapper = mount(BFormTags, {
533+
propsData: {
534+
value: ['one', 'two'],
535+
addOnChange: true,
536+
tagValidator: tag => tag.length < 4
537+
}
538+
})
539+
540+
expect(wrapper.element.tagName).toBe('DIV')
541+
expect(wrapper.vm.tags).toEqual(['one', 'two'])
542+
expect(wrapper.vm.newTag).toEqual('')
543+
544+
const $input = wrapper.find('input')
545+
expect($input.exists()).toBe(true)
546+
expect($input.element.value).toBe('')
547+
expect($input.element.type).toBe('text')
548+
549+
$input.element.value = 'three'
550+
await $input.trigger('input')
551+
await $input.trigger('change')
552+
expect(wrapper.vm.newTag).toEqual('three')
553+
expect(wrapper.vm.tags).toEqual(['one', 'two'])
554+
expect(wrapper.vm.tagsState.invalid).toContain('three')
555+
556+
const $tags = wrapper.findAll('.badge')
557+
expect($tags.length).toBe(2)
558+
await $tags
559+
.at(1)
560+
.find('button')
561+
.trigger('click')
562+
expect(wrapper.vm.tags).toEqual(['one'])
563+
expect(wrapper.vm.removedTags).toContain('two')
564+
565+
wrapper.vm.reset()
566+
await waitNT(wrapper.vm)
567+
568+
expect(wrapper.vm.newTag).toEqual('')
569+
expect(wrapper.vm.tags).toEqual([])
570+
expect(wrapper.vm.removedTags).toEqual([])
571+
expect(wrapper.vm.tagsState.invalid).toEqual([])
572+
573+
wrapper.destroy()
574+
})
575+
576+
it('native reset event works', async () => {
577+
const wrapper = mount(BFormTags, {
578+
propsData: {
579+
value: ['one', 'two'],
580+
addOnChange: true,
581+
tagValidator: tag => tag.length < 4
582+
}
583+
})
584+
585+
expect(wrapper.element.tagName).toBe('DIV')
586+
expect(wrapper.vm.tags).toEqual(['one', 'two'])
587+
expect(wrapper.vm.newTag).toEqual('')
588+
589+
const $input = wrapper.find('input')
590+
expect($input.exists()).toBe(true)
591+
expect($input.element.value).toBe('')
592+
expect($input.element.type).toBe('text')
593+
594+
$input.element.value = 'three'
595+
await $input.trigger('input')
596+
await $input.trigger('change')
597+
expect(wrapper.vm.newTag).toEqual('three')
598+
expect(wrapper.vm.tags).toEqual(['one', 'two'])
599+
expect(wrapper.vm.tagsState.invalid).toContain('three')
600+
601+
const $tags = wrapper.findAll('.badge')
602+
expect($tags.length).toBe(2)
603+
await $tags
604+
.at(1)
605+
.find('button')
606+
.trigger('click')
607+
expect(wrapper.vm.tags).toEqual(['one'])
608+
expect(wrapper.vm.removedTags).toContain('two')
609+
610+
await $input.trigger('reset')
611+
await waitNT(wrapper.vm)
612+
613+
expect(wrapper.vm.newTag).toEqual('')
614+
expect(wrapper.vm.tags).toEqual([])
615+
expect(wrapper.vm.removedTags).toEqual([])
616+
expect(wrapper.vm.tagsState.invalid).toEqual([])
617+
618+
wrapper.destroy()
619+
})
620+
621+
it('form native reset event triggers reset', async () => {
622+
const App = {
623+
render(h) {
624+
return h('form', [
625+
h(BFormTags, {
626+
props: {
627+
value: ['one', 'two'],
628+
addOnChange: true,
629+
tagValidator: tag => tag.length < 4
630+
}
631+
})
632+
])
633+
}
634+
}
635+
const wrapper = mount(App, {
636+
attachTo: createContainer()
637+
})
638+
639+
expect(wrapper.element.tagName).toBe('FORM')
640+
641+
const formTags = wrapper.findComponent(BFormTags)
642+
expect(formTags.exists()).toBe(true)
643+
expect(formTags.element.tagName).toBe('DIV')
644+
expect(formTags.vm.tags).toEqual(['one', 'two'])
645+
expect(formTags.vm.newTag).toEqual('')
646+
647+
const $input = formTags.find('input')
648+
expect($input.exists()).toBe(true)
649+
expect($input.element.value).toBe('')
650+
expect($input.element.type).toBe('text')
651+
652+
$input.element.value = 'three'
653+
await $input.trigger('input')
654+
await $input.trigger('change')
655+
expect(formTags.vm.newTag).toEqual('three')
656+
expect(formTags.vm.tags).toEqual(['one', 'two'])
657+
expect(formTags.vm.tagsState.invalid).toContain('three')
658+
659+
const $tags = formTags.findAll('.badge')
660+
expect($tags.length).toBe(2)
661+
await $tags
662+
.at(1)
663+
.find('button')
664+
.trigger('click')
665+
expect(formTags.vm.tags).toEqual(['one'])
666+
expect(formTags.vm.removedTags).toContain('two')
667+
668+
// Trigger form's native reset event
669+
wrapper.find('form').trigger('reset')
670+
await waitNT(formTags.vm)
671+
672+
expect(formTags.vm.newTag).toEqual('')
673+
expect(formTags.vm.tags).toEqual([])
674+
expect(formTags.vm.removedTags).toEqual([])
675+
expect(formTags.vm.tagsState.invalid).toEqual([])
676+
677+
wrapper.destroy()
678+
})
679+
531680
it('focuses input when wrapper div clicked', async () => {
532681
const wrapper = mount(BFormTags, {
533682
attachTo: createContainer(),

src/components/form-tags/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export declare const FormTagsPlugin: BvPlugin
99
export declare class BFormTags extends BvComponent {
1010
focus: () => void
1111
blur: () => void
12+
reset: () => void
1213
}
1314

1415
// Component: b-form-tag

0 commit comments

Comments
 (0)