Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.

Commit cb76f59

Browse files
committed
feat(BModal): adapt close button for better customization. Allow toggle directive.
1 parent aaa7363 commit cb76f59

File tree

2 files changed

+127
-6
lines changed

2 files changed

+127
-6
lines changed

packages/bootstrap-vue-next/src/components/BModal.vue

+22-6
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,18 @@
3636
</slot>
3737
</component>
3838
<template v-if="!hideHeaderCloseBoolean">
39-
<button v-if="hasHeaderCloseSlot" type="button" @click="hide('close')">
39+
<BButton
40+
v-if="hasHeaderCloseSlot"
41+
v-bind="headerCloseAttrs"
42+
@click="hide('close')"
43+
>
4044
<slot name="header-close" />
41-
</button>
45+
</BButton>
4246
<BCloseButton
4347
v-else
4448
ref="closeButton"
45-
:aria-label="headerCloseLabel"
4649
:white="headerCloseWhite"
50+
v-bind="headerCloseAttrs"
4751
@click="hide('close')"
4852
/>
4953
</template>
@@ -132,8 +136,10 @@ const props = withDefaults(
132136
headerBgVariant?: ColorVariant | null
133137
headerBorderVariant?: ColorVariant | null
134138
headerClass?: ClassValue
139+
headerCloseClass?: ClassValue
135140
headerCloseLabel?: string
136141
headerCloseWhite?: Booleanish
142+
headerCloseVariant?: ButtonVariant | null
137143
headerTextVariant?: ColorVariant | null
138144
hideBackdrop?: Booleanish
139145
hideFooter?: Booleanish
@@ -172,6 +178,10 @@ const props = withDefaults(
172178
headerBgVariant: null,
173179
headerBorderVariant: null,
174180
headerClass: undefined,
181+
headerCloseClass: undefined,
182+
headerCloseLabel: 'Close',
183+
headerCloseWhite: false,
184+
headerCloseVariant: 'secondary',
175185
footerBgVariant: null,
176186
footerBorderVariant: null,
177187
footerClass: undefined,
@@ -190,8 +200,6 @@ const props = withDefaults(
190200
cancelVariant: 'secondary',
191201
centered: false,
192202
fullscreen: false,
193-
headerCloseLabel: 'Close',
194-
headerCloseWhite: false,
195203
hideBackdrop: false,
196204
hideFooter: false,
197205
hideHeader: false,
@@ -251,7 +259,7 @@ const slots = useSlots()
251259
252260
const computedId = useId(() => props.id, 'modal')
253261
254-
const modelValue = useVModel(props, 'modelValue', emit)
262+
const modelValue = useVModel(props, 'modelValue', emit, {passive: true})
255263
256264
const busyBoolean = useBooleanish(() => props.busy)
257265
const lazyBoolean = useBooleanish(() => props.lazy)
@@ -339,6 +347,14 @@ const headerClasses = computed(() => [
339347
},
340348
])
341349
350+
const headerCloseClasses = computed(() => [props.headerCloseClass])
351+
352+
const headerCloseAttrs = computed(() => ({
353+
'variant': hasHeaderCloseSlot.value ? props.headerCloseVariant : undefined,
354+
'class': headerCloseClasses.value,
355+
'aria-label': props.headerCloseLabel,
356+
}))
357+
342358
const footerClasses = computed(() => [
343359
props.footerClass,
344360
{

packages/bootstrap-vue-next/src/components/modal.spec.ts

+105
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {enableAutoUnmount, mount} from '@vue/test-utils'
22
import {afterEach, beforeEach, describe, expect, it} from 'vitest'
33
import BModal from './BModal.vue'
4+
import BCloseButton from './BButton/BCloseButton.vue'
5+
import BButton from './BButton/BButton.vue'
46
// import BTransition from './BTransition/BTransition.vue'
57

68
describe('modal', () => {
@@ -313,6 +315,109 @@ describe('modal', () => {
313315
expect($div3.exists()).toBe(true)
314316
})
315317

318+
it('nested div BCloseButton has class when prop headerCloseClass', () => {
319+
const wrapper = mount(BModal, {
320+
global: {stubs: {teleport: true}},
321+
props: {headerCloseClass: 'foobar'},
322+
})
323+
const $div = wrapper.get('div')
324+
const $bclosebutton = $div.getComponent(BCloseButton)
325+
expect($bclosebutton.classes()).toContain('foobar')
326+
})
327+
328+
it('nested div BCloseButton has class when prop headerCloseWhite', () => {
329+
const wrapper = mount(BModal, {
330+
global: {stubs: {teleport: true}},
331+
props: {headerCloseWhite: true},
332+
})
333+
const $div = wrapper.get('div')
334+
const $bclosebutton = $div.getComponent(BCloseButton)
335+
expect($bclosebutton.classes()).toContain('btn-close-white')
336+
})
337+
338+
it('nested div BCloseButton has no variant class when headerCloseVariant', () => {
339+
const wrapper = mount(BModal, {
340+
global: {stubs: {teleport: true}},
341+
props: {headerCloseVariant: 'warning'},
342+
})
343+
const $div = wrapper.get('div')
344+
const $bclosebutton = $div.getComponent(BCloseButton)
345+
expect($bclosebutton.classes()).not.toContain('btn-warning')
346+
})
347+
348+
it('nested div BCloseButton has aria-label to be Close by default', () => {
349+
const wrapper = mount(BModal, {
350+
global: {stubs: {teleport: true}},
351+
})
352+
const $div = wrapper.get('div')
353+
const $bclosebutton = $div.getComponent(BCloseButton)
354+
expect($bclosebutton.attributes('aria-label')).toBe('Close')
355+
})
356+
357+
it('nested div BCloseButton has aria-label to be prop headerCloseLabel', () => {
358+
const wrapper = mount(BModal, {
359+
global: {stubs: {teleport: true}},
360+
props: {headerCloseLabel: 'foobar'},
361+
})
362+
const $div = wrapper.get('div')
363+
const $bclosebutton = $div.getComponent(BCloseButton)
364+
expect($bclosebutton.attributes('aria-label')).toBe('foobar')
365+
})
366+
367+
it('nested div BButton has class when prop headerCloseClass', () => {
368+
const wrapper = mount(BModal, {
369+
global: {stubs: {teleport: true}},
370+
props: {headerCloseClass: 'foobar'},
371+
slots: {'header-close': 'foobar'},
372+
})
373+
const $div = wrapper.get('div')
374+
const $bbutton = $div.getComponent(BButton)
375+
expect($bbutton.classes()).toContain('foobar')
376+
})
377+
378+
it('nested div BButton has class when prop headerCloseWhite', () => {
379+
const wrapper = mount(BModal, {
380+
global: {stubs: {teleport: true}},
381+
props: {headerCloseWhite: true},
382+
slots: {'header-close': 'foobar'},
383+
})
384+
const $div = wrapper.get('div')
385+
const $bbutton = $div.getComponent(BButton)
386+
expect($bbutton.classes()).not.toContain('btn-close-white')
387+
})
388+
389+
it('nested div BButton has variant class when headerCloseVariant', () => {
390+
const wrapper = mount(BModal, {
391+
global: {stubs: {teleport: true}},
392+
props: {headerCloseVariant: 'warning'},
393+
slots: {'header-close': 'foobar'},
394+
})
395+
const $div = wrapper.get('div')
396+
const $bbutton = $div.getComponent(BButton)
397+
expect($bbutton.classes()).toContain('btn-warning')
398+
})
399+
400+
it('nested div BButton has aria-label to be Close by default', () => {
401+
const wrapper = mount(BModal, {
402+
global: {stubs: {teleport: true}},
403+
slots: {'header-close': 'foobar'},
404+
})
405+
const $div = wrapper.get('div')
406+
const $bbutton = $div.getComponent(BButton)
407+
expect($bbutton.attributes('aria-label')).toBe('Close')
408+
})
409+
410+
it('nested div BButton has aria-label to be prop headerCloseLabel', () => {
411+
const wrapper = mount(BModal, {
412+
global: {stubs: {teleport: true}},
413+
props: {headerCloseLabel: 'foobar'},
414+
slots: {'header-close': 'foobar'},
415+
})
416+
const $div = wrapper.get('div')
417+
const $bbutton = $div.getComponent(BButton)
418+
expect($bbutton.attributes('aria-label')).toBe('foobar')
419+
})
420+
316421
// Test isActive states
317422

318423
// Test emit states

0 commit comments

Comments
 (0)