+283
-10
lines changedFilter options
+283
-10
lines changed Original file line number Diff line number Diff line change
@@ -313,6 +313,58 @@ component's value.
313
313
Want a fancy popup with a dark background instead of a light background? Set the `dark` prop to
314
314
`true` to enable the dark background.
315
315
316
+
### Button only mode
317
+
318
+
<span class="badge badge-info small">v2.7.0+</span>
319
+
320
+
Fancy just a button that launches the date picker dialog, or want to provide your own optional text
321
+
input field? Use the `button-only` prop to render the datepicker as a dropdown button. The formatted
322
+
date label will be rendered with the class `sr-only` (available only to screen readers).
323
+
324
+
In the following simple example, we are placing the datepicker (button only mode) as an append to a
325
+
`<b-input-group>`:
326
+
327
+
```html
328
+
<template>
329
+
<div>
330
+
<label for="example-input">Choose a date</label>
331
+
<b-input-group class="mb-3">
332
+
<b-form-input
333
+
id="example-input"
334
+
v-model="value"
335
+
type="text"
336
+
placeholder="YYYY-MM-DD"
337
+
></b-form-input>
338
+
<b-input-group-append>
339
+
<b-form-datepicker
340
+
v-model="value"
341
+
button-only
342
+
right
343
+
locale="en-US"
344
+
aria-controls="example-input"
345
+
></b-form-datepicker>
346
+
</b-input-group-append">
347
+
</b-input-group>
348
+
<p>Value: '{{ value }}'</p>
349
+
</div>
350
+
</template>
351
+
352
+
<script>
353
+
export default {
354
+
data() {
355
+
return {
356
+
value: ''
357
+
}
358
+
}
359
+
}
360
+
</script>
361
+
362
+
<!-- b-form-datepicker-button-only.vue -->
363
+
```
364
+
365
+
Control the size of the button via the `size` prop, and the button variant via the `button-variant`
366
+
prop.
367
+
316
368
### Date string format
317
369
318
370
<span class="badge badge-info small">v2.6.0+</span>
@@ -331,7 +383,8 @@ properties for the `Intl.DateTimeFormat` object (see also
331
383
:date-format-options="{ year: 'numeric', month: 'short', day: '2-digit', weekday: 'short' }"
332
384
locale="en"
333
385
></b-form-datepicker>
334
-
<label for="datepicker-dateformat2">Short date format</label>
386
+
387
+
<label class="mt-3" for="datepicker-dateformat2">Short date format</label>
335
388
<b-form-datepicker
336
389
id="datepicker-dateformat2"
337
390
:date-format-options="{ year: 'numeric', month: 'numeric', day: 'numeric' }"
Original file line number Diff line number Diff line change
@@ -108,6 +108,15 @@ const propsMixin = {
108
108
type: String,
109
109
default: null
110
110
},
111
+
buttonOnly: {
112
+
type: Boolean,
113
+
default: false
114
+
},
115
+
buttonVariant: {
116
+
// Applicable in button only mode
117
+
type: String,
118
+
default: 'secondary'
119
+
},
111
120
calendarWidth: {
112
121
// Width of the calendar dropdown
113
122
type: String,
@@ -310,7 +319,10 @@ export const BFormDatepicker = /*#__PURE__*/ Vue.extend({
310
319
this.localYMD = formatYMD(newVal) || ''
311
320
},
312
321
localYMD(newVal) {
313
-
this.$emit('input', this.valueAsDate ? parseYMD(newVal) || null : newVal || '')
322
+
// We only update the v-model when the datepicker is open
323
+
if (this.isVisible) {
324
+
this.$emit('input', this.valueAsDate ? parseYMD(newVal) || null : newVal || '')
325
+
}
314
326
},
315
327
calendarYM(newVal, oldVal) /* istanbul ignore next */ {
316
328
// Displayed calendar month has changed
Original file line number Diff line number Diff line change
@@ -42,9 +42,11 @@ describe('form-date', () => {
42
42
await waitRAF()
43
43
44
44
expect(wrapper.classes()).toContain('b-form-datepicker')
45
+
expect(wrapper.classes()).toContain('b-form-btn-label-control')
45
46
expect(wrapper.classes()).toContain('form-control')
46
47
expect(wrapper.classes()).toContain('dropdown')
47
48
expect(wrapper.classes()).not.toContain('show')
49
+
expect(wrapper.classes()).not.toContain('btn-group')
48
50
expect(wrapper.attributes('role')).toEqual('group')
49
51
50
52
expect(wrapper.find('.dropdown-menu').exists()).toBe(true)
@@ -54,6 +56,7 @@ describe('form-date', () => {
54
56
55
57
expect(wrapper.find('label.form-control').exists()).toBe(true)
56
58
expect(wrapper.find('label.form-control').attributes('for')).toEqual('test-base')
59
+
expect(wrapper.find('label.form-control').classes()).not.toContain('sr-only')
57
60
58
61
expect(wrapper.find('input[type="hidden"]').exists()).toBe(false)
59
62
@@ -66,6 +69,48 @@ describe('form-date', () => {
66
69
wrapper.destroy()
67
70
})
68
71
72
+
it('has expected base structure in button-only mode', async () => {
73
+
const wrapper = mount(BFormDatepicker, {
74
+
attachToDocument: true,
75
+
propsData: {
76
+
id: 'test-button-only',
77
+
buttonOnly: true
78
+
}
79
+
})
80
+
81
+
expect(wrapper.isVueInstance()).toBe(true)
82
+
expect(wrapper.is('div')).toBe(true)
83
+
await waitNT(wrapper.vm)
84
+
await waitRAF()
85
+
86
+
expect(wrapper.classes()).toContain('b-form-datepicker')
87
+
expect(wrapper.classes()).not.toContain('b-form-btn-label-control')
88
+
expect(wrapper.classes()).not.toContain('form-control')
89
+
expect(wrapper.classes()).toContain('dropdown')
90
+
expect(wrapper.classes()).not.toContain('show')
91
+
expect(wrapper.classes()).toContain('btn-group')
92
+
expect(wrapper.attributes('role')).not.toEqual('group')
93
+
94
+
expect(wrapper.find('.dropdown-menu').exists()).toBe(true)
95
+
expect(wrapper.find('.dropdown-menu').classes()).not.toContain('show')
96
+
expect(wrapper.find('.dropdown-menu').attributes('role')).toEqual('dialog')
97
+
expect(wrapper.find('.dropdown-menu').attributes('aria-modal')).toEqual('false')
98
+
99
+
expect(wrapper.find('label.form-control').exists()).toBe(true)
100
+
expect(wrapper.find('label.form-control').attributes('for')).toEqual('test-button-only')
101
+
expect(wrapper.find('label.form-control').classes()).toContain('sr-only')
102
+
103
+
expect(wrapper.find('input[type="hidden"]').exists()).toBe(false)
104
+
105
+
const $btn = wrapper.find('button#test-button-only')
106
+
expect($btn.exists()).toBe(true)
107
+
expect($btn.attributes('aria-haspopup')).toEqual('dialog')
108
+
expect($btn.attributes('aria-expanded')).toEqual('false')
109
+
expect($btn.find('svg.bi-calendar').exists()).toBe(true)
110
+
111
+
wrapper.destroy()
112
+
})
113
+
69
114
it('renders hidden input when name prop is set', async () => {
70
115
const wrapper = mount(BFormDatepicker, {
71
116
attachToDocument: true,
Original file line number Diff line number Diff line change
@@ -69,6 +69,16 @@
69
69
"prop": "direction",
70
70
"description": "Set to the string 'rtl' or 'ltr' to explicitly force the calendar to render in right-to-left or left-ro-right (respectively) mode. Defaults to the resolved locale's directionality"
71
71
},
72
+
{
73
+
"prop": "buttonOnly",
74
+
"version": "2.7.0",
75
+
"description": "Renders the datepicker as a dropdown button instead of a form-control"
76
+
},
77
+
{
78
+
"prop": "buttonVariant",
79
+
"version": "2.7.0",
80
+
"description": "The button variant to use when in `button-only` mode. Has no effect if prop `button-only` is not set"
81
+
},
72
82
{
73
83
"prop": "calendarWidth",
74
84
"version": "2.6.0",
Original file line number Diff line number Diff line change
@@ -208,6 +208,60 @@ control the positioning of the popup calendar.
208
208
Refer to the [`<b-dropdown>` documentation](/docs/components/dropdown) for details on the effects
209
209
and usage of these props.
210
210
211
+
### Button only mode
212
+
213
+
<span class="badge badge-info small">v2.7.0+</span>
214
+
215
+
Fancy just a button that launches the timepicker dialog, or want to provide your own optional text
216
+
input field? Use the `button-only` prop to render the timepicker as a dropdown button. The formatted
217
+
time label will be rendered with the class `sr-only` (available only to screen readers).
218
+
219
+
In the following simple example, we are placing the timepicker (button only mode) as an append to a
220
+
`<b-input-group>`:
221
+
222
+
```html
223
+
<template>
224
+
<div>
225
+
<label for="example-input">Choose a time</label>
226
+
<b-input-group class="mb-3">
227
+
<b-form-input
228
+
id="example-input"
229
+
v-model="value"
230
+
type="text"
231
+
placeholder="HH:mm:ss"
232
+
></b-form-input>
233
+
<b-input-group-append>
234
+
<b-form-timepicker
235
+
v-model="value"
236
+
button-only
237
+
right
238
+
show-seconds
239
+
:hour12="false"
240
+
locale="en-US"
241
+
aria-controls="example-input"
242
+
></b-form-timepicker>
243
+
</b-input-group-append">
244
+
</b-input-group>
245
+
<p>Value: '{{ value }}'</p>
246
+
</div>
247
+
</template>
248
+
249
+
<script>
250
+
export default {
251
+
data() {
252
+
return {
253
+
value: ''
254
+
}
255
+
}
256
+
}
257
+
</script>
258
+
259
+
<!-- b-form-timepicker-button-only.vue -->
260
+
```
261
+
262
+
Control the size of the button via the `size` prop, and the button variant via the `button-variant`
263
+
prop.
264
+
211
265
## Internationalization
212
266
213
267
Internationalization of the time interface is provided via
Original file line number Diff line number Diff line change
@@ -90,6 +90,15 @@ const propsMixin = {
90
90
type: [Number, String],
91
91
default: 1
92
92
},
93
+
buttonOnly: {
94
+
type: Boolean,
95
+
default: false
96
+
},
97
+
buttonVariant: {
98
+
// Applicable in button only mode
99
+
type: String,
100
+
default: 'secondary'
101
+
},
93
102
nowButton: {
94
103
type: Boolean,
95
104
default: false
@@ -240,7 +249,12 @@ export const BFormTimepicker = /*#__PURE__*/ Vue.extend({
240
249
this.localHMS = newVal || ''
241
250
},
242
251
localHMS(newVal) {
243
-
this.$emit('input', newVal || '')
252
+
// We only update hte v-model value when the timepicker
253
+
// is open, to prevent cursor jumps when bound to a
254
+
// text input in button only mode
255
+
if (this.isVisible) {
256
+
this.$emit('input', newVal || '')
257
+
}
244
258
}
245
259
},
246
260
methods: {
Original file line number Diff line number Diff line change
@@ -41,9 +41,11 @@ describe('form-timepicker', () => {
41
41
await waitRAF()
42
42
43
43
expect(wrapper.classes()).toContain('b-form-timepicker')
44
+
expect(wrapper.classes()).toContain('b-form-btn-label-control')
44
45
expect(wrapper.classes()).toContain('form-control')
45
46
expect(wrapper.classes()).toContain('dropdown')
46
47
expect(wrapper.classes()).not.toContain('show')
48
+
expect(wrapper.classes()).not.toContain('btn-group')
47
49
expect(wrapper.attributes('role')).toEqual('group')
48
50
49
51
expect(wrapper.find('.dropdown-menu').exists()).toBe(true)
@@ -66,6 +68,49 @@ describe('form-timepicker', () => {
66
68
wrapper.destroy()
67
69
})
68
70
71
+
it('has expected default structure when button-only is true', async () => {
72
+
const wrapper = mount(BFormTimepicker, {
73
+
attachToDocument: true,
74
+
propsData: {
75
+
id: 'test-button-only',
76
+
buttonOnly: true
77
+
}
78
+
})
79
+
80
+
expect(wrapper.isVueInstance()).toBe(true)
81
+
expect(wrapper.is('div')).toBe(true)
82
+
await waitNT(wrapper.vm)
83
+
await waitRAF()
84
+
85
+
expect(wrapper.classes()).toContain('b-form-timepicker')
86
+
expect(wrapper.classes()).not.toContain('b-form-btn-label-control')
87
+
expect(wrapper.classes()).not.toContain('form-control')
88
+
expect(wrapper.classes()).toContain('dropdown')
89
+
expect(wrapper.classes()).not.toContain('show')
90
+
expect(wrapper.classes()).toContain('btn-group')
91
+
expect(wrapper.attributes('role')).not.toEqual('group')
92
+
93
+
expect(wrapper.find('.dropdown-menu').exists()).toBe(true)
94
+
expect(wrapper.find('.dropdown-menu').classes()).not.toContain('show')
95
+
expect(wrapper.find('.dropdown-menu').attributes('role')).toEqual('dialog')
96
+
expect(wrapper.find('.dropdown-menu').attributes('aria-modal')).toEqual('false')
97
+
98
+
expect(wrapper.find('label.form-control').exists()).toBe(true)
99
+
expect(wrapper.find('label.form-control').attributes('for')).toEqual('test-button-only')
100
+
expect(wrapper.find('label.form-control').text()).toContain('No time selected')
101
+
expect(wrapper.find('label.form-control').classes()).toContain('sr-only')
102
+
103
+
expect(wrapper.find('input[type="hidden"]').exists()).toBe(false)
104
+
105
+
const $btn = wrapper.find('button#test-button-only')
106
+
expect($btn.exists()).toBe(true)
107
+
expect($btn.attributes('aria-haspopup')).toEqual('dialog')
108
+
expect($btn.attributes('aria-expanded')).toEqual('false')
109
+
expect($btn.find('svg.bi-clock').exists()).toBe(true)
110
+
111
+
wrapper.destroy()
112
+
})
113
+
69
114
it('renders hidden input when name prop is set', async () => {
70
115
const wrapper = mount(BFormTimepicker, {
71
116
attachToDocument: true,
Original file line number Diff line number Diff line change
@@ -62,6 +62,16 @@
62
62
"prop": "hideHeader",
63
63
"description": "When set, visually hides the selected date header"
64
64
},
65
+
{
66
+
"prop": "buttonOnly",
67
+
"version": "2.7.0",
68
+
"description": "Renders the datepicker as a dropdown button instead of a form-control"
69
+
},
70
+
{
71
+
"prop": "buttonVariant",
72
+
"version": "2.7.0",
73
+
"description": "The button variant to use when in `button-only` mode. Has no effect if prop `button-only` is not set"
74
+
},
65
75
{
66
76
"prop": "menuClass",
67
77
"description": "Class (or classes) to apply to to popup menu wrapper"
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4