🌐 AI搜索 & 代理 主页
Skip to content

Commit ec7fa35

Browse files
authored
Merge branch 'dev' into feat-form-select-option-groups
2 parents 9badfd0 + 30029e3 commit ec7fa35

File tree

11 files changed

+298
-123
lines changed

11 files changed

+298
-123
lines changed

src/components/dropdown/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,58 @@ split button its own variant via the `split-variant` prop.
308308
<!-- b-dropdown-split-variant.vue -->
309309
```
310310

311+
### Block level dropdowns
312+
313+
By default dropdowns act like buttons and are displayed inline. To create block level dropdowns
314+
(that span to the full width of a parent) set the `block` prop. Both, regular and split button
315+
dropdowns are supported.
316+
317+
```html
318+
<div>
319+
<b-dropdown text="Block Level Dropdown" block variant="primary" class="m-2">
320+
<b-dropdown-item href="#">Action</b-dropdown-item>
321+
<b-dropdown-item href="#">Another action</b-dropdown-item>
322+
<b-dropdown-item href="#">Something else here</b-dropdown-item>
323+
</b-dropdown>
324+
325+
<b-dropdown
326+
text="Block Level Split Dropdown"
327+
block
328+
split
329+
split-variant="outline-primary"
330+
variant="primary"
331+
class="m-2"
332+
>
333+
<b-dropdown-item href="#">Action</b-dropdown-item>
334+
<b-dropdown-item href="#">Another action</b-dropdown-item>
335+
<b-dropdown-item href="#">Something else here...</b-dropdown-item>
336+
</b-dropdown>
337+
</div>
338+
339+
<!-- b-dropdown-block.vue -->
340+
```
341+
342+
If you want the dropdown menu to span to the full width of the parent container too, add the `w-100`
343+
utility class to the `menu-class` prop.
344+
345+
```html
346+
<div>
347+
<b-dropdown
348+
text="Block Level Dropdown Menu"
349+
block
350+
variant="primary"
351+
class="m-2"
352+
menu-class="w-100"
353+
>
354+
<b-dropdown-item href="#">Action</b-dropdown-item>
355+
<b-dropdown-item href="#">Another action</b-dropdown-item>
356+
<b-dropdown-item href="#">Something else here</b-dropdown-item>
357+
</b-dropdown>
358+
</div>
359+
360+
<!-- b-dropdown-block-menu.vue -->
361+
```
362+
311363
### Dropdown sub-component color variants
312364

313365
Many of the supported dropdown [sub-components](#dropdown-supported-sub-components) provide a

src/components/dropdown/dropdown.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export const props = {
2424
type: String,
2525
default: () => getComponentConfig(NAME, 'variant')
2626
},
27+
block: {
28+
type: Boolean,
29+
default: false
30+
},
2731
menuClass: {
2832
type: [String, Array],
2933
default: null
@@ -84,9 +88,16 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
8488
this.directionClass,
8589
{
8690
show: this.visible,
87-
// Position `static` is needed to allow menu to "breakout" of the scrollParent boundaries
88-
// when boundary is anything other than `scrollParent`
89-
// See https://github.com/twbs/bootstrap/issues/24251#issuecomment-341413786
91+
// The 'btn-group' class is required in `split` mode for button alignment
92+
// It needs also to be applied when `block` is disabled to allow multiple
93+
// dropdowns to be aligned one line
94+
'btn-group': this.split || !this.block,
95+
// When `block` is enabled and we are in `split` mode the 'd-flex' class
96+
// needs to be applied to allow the buttons to stretch to full width
97+
'd-flex': this.block && this.split,
98+
// Position `static` is needed to allow menu to "breakout" of the `scrollParent`
99+
// boundaries when boundary is anything other than `scrollParent`
100+
// See: https://github.com/twbs/bootstrap/issues/24251#issuecomment-341413786
90101
'position-static': this.boundary !== 'scrollParent' || !this.boundary
91102
}
92103
]
@@ -115,9 +126,10 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
115126
const buttonContent = this.normalizeSlot('button-content') || this.html || stripTags(this.text)
116127
if (this.split) {
117128
const btnProps = {
118-
disabled: this.disabled,
119129
variant: this.splitVariant || this.variant,
120-
size: this.size
130+
size: this.size,
131+
block: this.block,
132+
disabled: this.disabled
121133
}
122134
// We add these as needed due to router-link issues with defined property with undefined/null values
123135
if (this.splitTo) {
@@ -149,10 +161,11 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
149161
staticClass: 'dropdown-toggle',
150162
class: this.toggleClasses,
151163
props: {
164+
tag: this.toggleTag,
152165
variant: this.variant,
153166
size: this.size,
154-
disabled: this.disabled,
155-
tag: this.toggleTag
167+
block: this.block && !this.split,
168+
disabled: this.disabled
156169
},
157170
attrs: {
158171
id: this.safeId('_BV_toggle_'),
@@ -186,7 +199,7 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
186199
return h(
187200
'div',
188201
{
189-
staticClass: 'dropdown btn-group b-dropdown',
202+
staticClass: 'dropdown b-dropdown',
190203
class: this.dropdownClasses,
191204
attrs: { id: this.safeId() }
192205
},

src/components/dropdown/dropdown.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,30 @@ describe('dropdown', () => {
247247
wrapper.destroy()
248248
})
249249

250+
it('should not have "btn-group" class when block is true', async () => {
251+
const wrapper = mount(BDropdown, {
252+
attachToDocument: true,
253+
propsData: {
254+
block: true
255+
}
256+
})
257+
expect(wrapper.classes()).not.toContain('btn-group')
258+
wrapper.destroy()
259+
})
260+
261+
it('should have "btn-group" and "d-flex" classes when block and split are true', async () => {
262+
const wrapper = mount(BDropdown, {
263+
attachToDocument: true,
264+
propsData: {
265+
block: true,
266+
split: true
267+
}
268+
})
269+
expect(wrapper.classes()).toContain('btn-group')
270+
expect(wrapper.classes()).toContain('d-flex')
271+
wrapper.destroy()
272+
})
273+
250274
it('should have "dropdown-toggle-no-caret" class when no-caret is true', async () => {
251275
const wrapper = mount(BDropdown, {
252276
attachToDocument: true,

src/components/dropdown/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
"prop": "toggleClass",
6868
"description": "CSS class (or classes) to add to the toggle button"
6969
},
70+
{
71+
"prop": "block",
72+
"description": "Renders a 100% width toggle button (expands to the width of it's parent container)"
73+
},
7074
{
7175
"prop": "noCaret",
7276
"description": "Hide the caret indicator on the toggle button"

src/components/form-input/README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ native browser HTML5 types: `text`, `password`, `email`, `number`, `url`, `tel`,
3535
<b-container fluid>
3636
<b-row class="my-1" v-for="type in types" :key="type">
3737
<b-col sm="3">
38-
<label :for="`type-${type}`">Type {{ type }}:</label>
38+
<label :for="`type-${type}`">Type <code>{{ type }}</code>:</label>
3939
</b-col>
4040
<b-col sm="9">
4141
<b-form-input :id="`type-${type}`" :type="type"></b-form-input>
@@ -50,13 +50,14 @@ native browser HTML5 types: `text`, `password`, `email`, `number`, `url`, `tel`,
5050
return {
5151
types: [
5252
'text',
53-
'password',
54-
'email',
5553
'number',
54+
'email',
55+
'password',
56+
'search',
5657
'url',
5758
'tel',
5859
'date',
59-
`time`,
60+
'time',
6061
'range',
6162
'color'
6263
]
@@ -75,8 +76,8 @@ rendered and a console warning will be issued.
7576

7677
- Not all browsers support all input types, nor do some types render in the same format across
7778
browser types/versions.
78-
- Browsers that do not support a particular type will fall back to a `text` input type (event
79-
through the rendered `type` attribute markup shows the requested type).
79+
- Browsers that do not support a particular type will fall back to a `text` input type (even though
80+
the rendered `type` attribute markup shows the requested type).
8081
- No testing is performed to see if the requested input type is supported by the browser.
8182
- Chrome lost support for `datetime` in version 26, Opera in version 15, and Safari in iOS 7.
8283
Instead of using `datetime`, since support should be deprecated, use `date` and `time` as two
@@ -85,13 +86,15 @@ rendered and a console warning will be issued.
8586
- For date and time style inputs, where supported, the displayed value in the GUI may be different
8687
than what is returned by it's value (i.e. ordering of year-month-date).
8788
- Regardless of input type, the value is **always** returned as a string representation.
88-
- `v-model.lazy` is not supported by `<b-form-input>` (nor any custom Vue component).
89+
- `v-model.lazy` is not supported by `<b-form-input>` (nor any custom Vue component). Use the `lazy`
90+
prop instead.
8991
- `v-model` modifiers `.number` and `.trim` can cause unexpected cursor jumps when the user is
9092
typing (this is a Vue issue with `v-model` on custom components). _Avoid using these modifiers_.
93+
Use the `number` or `trip` props instead.
9194
- Older version of Firefox may not support `readonly` for `range` type inputs.
9295
- Input types that do not support `min`, `max` and `step` (i.e. `text`, `password`, `tel`, `email`,
9396
`url`, etc) will silently ignore these values (although they will still be rendered on the input
94-
markup).
97+
markup) iv values are provided.
9598

9699
### Range type input
97100

@@ -461,9 +464,9 @@ from an array of options.
461464
Vue does not officially support `.lazy`, `.trim`, and `.number` modifiers on the `v-model` of custom
462465
component based inputs, and may generate a bad user experience. Avoid using Vue's native modifiers.
463466

464-
To get around this, `<b-form-input>` and `<b-form-textarea>` have two boolean props `trim` and
465-
`number` which emulate the native Vue `v-model` modifiers `.trim` and `.number` respectively.
466-
Emulation of the `.lazy` modifier is _not_ supported (listen for `change` or `blur` events instead).
467+
To get around this, `<b-form-input>` and `<b-form-textarea>` have three boolean props `trim`,
468+
`number`, and `lazy` which emulate the native Vue `v-model` modifiers `.trim` and `.number` and
469+
`.lazy` respectively. The `lazy` prop will update the v-model on `change`/`blur`events.
467470

468471
**Notes:**
469472

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

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,6 @@ describe('form-input', () => {
416416
expect(wrapper.emitted('input').length).toEqual(1)
417417
expect(wrapper.emitted('input')[0][0]).toEqual('test')
418418

419-
expect(input.vm.localValue).toEqual('test')
420-
421419
wrapper.destroy()
422420
})
423421

@@ -593,7 +591,7 @@ describe('form-input', () => {
593591
input.element.focus()
594592
input.trigger('wheel', { deltaY: 33.33, deltaX: 0, deltaZ: 0, deltaMode: 0 })
595593

596-
// no-wheel=true will fire a blur event on the input when wheel fired
594+
// `:no-wheel="true"` will fire a blur event on the input when wheel fired
597595
expect(spy).toHaveBeenCalled()
598596

599597
wrapper.destroy()
@@ -620,7 +618,7 @@ describe('form-input', () => {
620618
input.element.focus()
621619
input.trigger('wheel', { deltaY: 33.33, deltaX: 0, deltaZ: 0, deltaMode: 0 })
622620

623-
// no-wheel=false will not fire a blur event on the input when wheel fired
621+
// `:no-wheel="false"` will not fire a blur event on the input when wheel fired
624622
expect(spy).not.toHaveBeenCalled()
625623

626624
wrapper.destroy()
@@ -677,16 +675,16 @@ describe('form-input', () => {
677675
await waitNT(wrapper.vm)
678676

679677
expect(input.element.value).toBe('123.450')
680-
// Pre converted value as string
681-
expect(wrapper.emitted('input')).toBeDefined()
682-
expect(wrapper.emitted('input').length).toBe(1)
683-
expect(wrapper.emitted('input')[0].length).toEqual(1)
684-
expect(wrapper.emitted('input')[0][0]).toEqual('123.450')
685-
// v-model update event (should emit a numerical value)
678+
// `v-model` update event (should emit a numerical value)
686679
expect(wrapper.emitted('update')).toBeDefined()
687680
expect(wrapper.emitted('update').length).toBe(1)
688681
expect(wrapper.emitted('update')[0].length).toEqual(1)
689682
expect(wrapper.emitted('update')[0][0]).toBeCloseTo(123.45)
683+
// Pre converted value as string (raw input value)
684+
expect(wrapper.emitted('input')).toBeDefined()
685+
expect(wrapper.emitted('input').length).toBe(1)
686+
expect(wrapper.emitted('input')[0].length).toEqual(1)
687+
expect(wrapper.emitted('input')[0][0]).toEqual('123.450')
690688

691689
// Update the input to be different string-wise, but same numerically
692690
input.element.value = '123.4500'
@@ -697,11 +695,11 @@ describe('form-input', () => {
697695
// Should emit a new input event
698696
expect(wrapper.emitted('input').length).toEqual(2)
699697
expect(wrapper.emitted('input')[1][0]).toEqual('123.4500')
700-
// Should emit a new update event
701-
expect(wrapper.emitted('update').length).toBe(2)
698+
// `v-model` value stays the same and update event shouldn't be emitted again
699+
expect(wrapper.emitted('update').length).toBe(1)
702700
expect(wrapper.emitted('update')[0][0]).toBeCloseTo(123.45)
703701

704-
// Updating the v-model to new numeric value
702+
// Updating the `v-model` to new numeric value
705703
wrapper.setProps({
706704
value: 45.6
707705
})
@@ -711,6 +709,63 @@ describe('form-input', () => {
711709
wrapper.destroy()
712710
})
713711

712+
it('"lazy" modifier prop works', async () => {
713+
const wrapper = mount(BFormInput, {
714+
propsData: {
715+
type: 'text',
716+
lazy: true
717+
}
718+
})
719+
720+
const input = wrapper.find('input')
721+
input.element.value = 'a'
722+
input.trigger('input')
723+
await waitNT(wrapper.vm)
724+
expect(input.element.value).toBe('a')
725+
// `v-model` update event should not have emitted
726+
expect(wrapper.emitted('update')).not.toBeDefined()
727+
728+
input.element.value = 'ab'
729+
input.trigger('input')
730+
await waitNT(wrapper.vm)
731+
expect(input.element.value).toBe('ab')
732+
// `v-model` update event should not have emitted
733+
expect(wrapper.emitted('update')).not.toBeDefined()
734+
735+
// trigger a change event
736+
input.trigger('change')
737+
await waitNT(wrapper.vm)
738+
expect(input.element.value).toBe('ab')
739+
// `v-model` update event should have emitted
740+
expect(wrapper.emitted('update')).toBeDefined()
741+
expect(wrapper.emitted('update').length).toEqual(1)
742+
expect(wrapper.emitted('update')[0][0]).toBe('ab')
743+
744+
input.element.value = 'abc'
745+
input.trigger('input')
746+
await waitNT(wrapper.vm)
747+
expect(input.element.value).toBe('abc')
748+
// `v-model` update event should not have emitted new event
749+
expect(wrapper.emitted('update').length).toEqual(1)
750+
751+
input.element.value = 'abcd'
752+
input.trigger('input')
753+
await waitNT(wrapper.vm)
754+
expect(input.element.value).toBe('abcd')
755+
// `v-model` update event should not have emitted new event
756+
expect(wrapper.emitted('update').length).toEqual(1)
757+
758+
// Trigger a blur event
759+
input.trigger('blur')
760+
await waitNT(wrapper.vm)
761+
expect(input.element.value).toBe('abcd')
762+
// `v-model` update event should have emitted
763+
expect(wrapper.emitted('update').length).toEqual(2)
764+
expect(wrapper.emitted('update')[1][0]).toBe('abcd')
765+
766+
wrapper.destroy()
767+
})
768+
714769
it('focus() and blur() methods work', async () => {
715770
const wrapper = mount(BFormInput, {
716771
mountToDocument: true

0 commit comments

Comments
 (0)