Skip to content

Commit 4046a53

Browse files
authored
feat(b-nav): add card header support (#3883)
1 parent 8f8b7aa commit 4046a53

File tree

6 files changed

+198
-18
lines changed

6 files changed

+198
-18
lines changed

src/components/card/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,63 @@ assistive technologies – such as screen readers. Ensure that information denot
495495
either obvious from the content itself (e.g. the visible text), or is included through alternative
496496
means, such as additional text hidden with the `.sr-only` class.
497497

498+
## Nav integration
499+
500+
Integrate [`<b-nav>`](/docs/components/nav) into card headers easily.
501+
502+
**Using the `header` slot**:
503+
504+
```html
505+
<div>
506+
<b-card title="Card Title" body-class="text-center">
507+
<b-nav slot="header" card-header tabs>
508+
<b-nav-item active>Active</b-nav-item>
509+
<b-nav-item>Inactive</b-nav-item>
510+
<b-nav-item disabled>Disabled</b-nav-item>
511+
</b-nav>
512+
513+
<b-card-text>
514+
With supporting text below as a natural lead-in to additional content.
515+
</b-card-text>
516+
517+
<b-button variant="primary">Go somewhere</b-button>
518+
</b-card>
519+
</div>
520+
521+
<!-- card-with-nav-header-slot.vue -->
522+
```
523+
524+
**Using `<b-card-header>` sub-component:**
525+
526+
```html
527+
<div>
528+
<b-card no-body>
529+
<b-card-header>
530+
<b-nav card-header tabs>
531+
<b-nav-item active>Active</b-nav-item>
532+
<b-nav-item>Inactive</b-nav-item>
533+
<b-nav-item disabled>Disabled</b-nav-item>
534+
</b-nav>
535+
</b-card-header>
536+
537+
<b-card-body class="text-center">
538+
<b-card-title>Card Title</b-card-title>
539+
540+
<b-card-text>
541+
With supporting text below as a natural lead-in to additional content.
542+
</b-card-text>
543+
544+
<b-button variant="primary">Go somewhere</b-button>
545+
</b-card-body>
546+
</b-card>
547+
</div>
548+
549+
<!-- card-with-nav-header-component.vue -->
550+
```
551+
552+
For more information on using `<b-nav>` in card headers, refer to the
553+
[Navs documentation](/docs/components/nav).
554+
498555
## Card groups
499556

500557
In addition to styling the content within cards, BootstrapVue includes a `<b-card-group>` component

src/components/nav/README.md

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,10 @@ Use `<b-nav-item-dropdown>` to place dropdown items within your nav.
170170
toggle-class="nav-link-custom"
171171
right
172172
>
173-
<b-dropdown-item>one</b-dropdown-item>
174-
<b-dropdown-item>two</b-dropdown-item>
173+
<b-dropdown-item>One</b-dropdown-item>
174+
<b-dropdown-item>Two</b-dropdown-item>
175175
<b-dropdown-divider></b-dropdown-divider>
176-
<b-dropdown-item>three</b-dropdown-item>
176+
<b-dropdown-item>Three</b-dropdown-item>
177177
</b-nav-item-dropdown>
178178
</b-nav>
179179
</div>
@@ -220,6 +220,96 @@ render the menu contents only when it is shown by setting the `lazy` prop to tru
220220
See the [`<b-tabs>`](/docs/components/tabs) component for creating tabbable panes of local content
221221
(not suited for navigation).
222222

223+
## Card integration
224+
225+
Use a `<b-nav>` in a [`<b-card>`](/docs/components/card) header, by enabling the `card-header` prop
226+
on `<b-nav>` and setting either the `pills` or `tabs` props:
227+
228+
**Tabs style:**
229+
230+
```html
231+
<div>
232+
<b-card title="Card Title" body-class="text-center">
233+
<b-nav slot="header" card-header tabs>
234+
<b-nav-item active>Active</b-nav-item>
235+
<b-nav-item>Inactive</b-nav-item>
236+
<b-nav-item disabled>Disabled</b-nav-item>
237+
<b-nav-item-dropdown text="Dropdown" right>
238+
<b-dropdown-item>One</b-dropdown-item>
239+
<b-dropdown-item>Two</b-dropdown-item>
240+
<b-dropdown-item>Three</b-dropdown-item>
241+
</b-nav-item-dropdown>
242+
</b-nav>
243+
244+
<b-card-text>
245+
With supporting text below as a natural lead-in to additional content.
246+
</b-card-text>
247+
248+
<b-button variant="primary">Go somewhere</b-button>
249+
</b-card>
250+
</div>
251+
252+
<!-- nav-card-tabs.vue -->
253+
```
254+
255+
**Pill style:**
256+
257+
```html
258+
<div>
259+
<b-card title="Card Title" body-class="text-center">
260+
<b-nav slot="header" card-header pills>
261+
<b-nav-item active>Active</b-nav-item>
262+
<b-nav-item>Inactive</b-nav-item>
263+
<b-nav-item disabled>Disabled</b-nav-item>
264+
<b-nav-item-dropdown text="Dropdown" right>
265+
<b-dropdown-item>One</b-dropdown-item>
266+
<b-dropdown-item>Two</b-dropdown-item>
267+
<b-dropdown-item>Three</b-dropdown-item>
268+
</b-nav-item-dropdown>
269+
</b-nav>
270+
271+
<b-card-text>
272+
With supporting text below as a natural lead-in to additional content.
273+
</b-card-text>
274+
275+
<b-button variant="primary">Go somewhere</b-button>
276+
</b-card>
277+
</div>
278+
279+
<!-- nav-card-pills.vue -->
280+
```
281+
282+
**Plain style:**
283+
284+
The `card-header` prop is only needed when you are applying `tabs` or `pills` style.
285+
286+
```html
287+
<div>
288+
<b-card title="Card Title" body-class="text-center">
289+
<b-nav slot="header">
290+
<b-nav-item active>Active</b-nav-item>
291+
<b-nav-item>Inactive</b-nav-item>
292+
<b-nav-item disabled>Disabled</b-nav-item>
293+
<b-nav-item-dropdown text="Dropdown" right>
294+
<b-dropdown-item>One</b-dropdown-item>
295+
<b-dropdown-item>Two</b-dropdown-item>
296+
<b-dropdown-item>Three</b-dropdown-item>
297+
</b-nav-item-dropdown>
298+
</b-nav>
299+
300+
<b-card-text>
301+
With supporting text below as a natural lead-in to additional content.
302+
</b-card-text>
303+
304+
<b-button variant="primary">Go somewhere</b-button>
305+
</b-card>
306+
</div>
307+
308+
<!-- nav-card-plain.vue -->
309+
```
310+
311+
The `card-header` prop has no effect if the `<b-nav>` is `vertical`.
312+
223313
## Accessibility
224314

225315
If you're using `<b-nav>` to provide a navigation bar, be sure to add a `role="navigation"` to the

src/components/nav/nav.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export const props = {
3535
small: {
3636
type: Boolean,
3737
default: false
38+
},
39+
cardHeader: {
40+
// Set to true if placing in a card header
41+
type: Boolean,
42+
default: false
3843
}
3944
}
4045

@@ -58,7 +63,9 @@ export const BNav = /*#__PURE__*/ Vue.extend({
5863
staticClass: 'nav',
5964
class: {
6065
'nav-tabs': props.tabs,
61-
'nav-pills': props.pills,
66+
'nav-pills': props.pills && !props.tabs,
67+
'card-header-tabs': !props.vertical && props.cardHeader && props.tabs,
68+
'card-header-pills': !props.vertical && props.cardHeader && props.pills && !props.tabs,
6269
'flex-column': props.vertical,
6370
'nav-fill': !props.vertical && props.fill,
6471
'nav-justified': !props.vertical && props.justified,

src/components/nav/nav.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,36 @@ describe('nav', () => {
179179
expect(wrapper.classes().length).toBe(2)
180180
expect(wrapper.text()).toBe('')
181181
})
182+
183+
it('applies card-header-tabs class when tabs and card-header props set', async () => {
184+
const wrapper = mount(BNav, {
185+
propsData: {
186+
tabs: true,
187+
cardHeader: true
188+
}
189+
})
190+
191+
expect(wrapper.is('ul')).toBe(true)
192+
expect(wrapper.classes()).toContain('nav')
193+
expect(wrapper.classes()).toContain('nav-tabs')
194+
expect(wrapper.classes()).toContain('card-header-tabs')
195+
expect(wrapper.classes().length).toBe(3)
196+
expect(wrapper.text()).toBe('')
197+
})
198+
199+
it('applies card-header-pills class when pills and card-header props set', async () => {
200+
const wrapper = mount(BNav, {
201+
propsData: {
202+
pills: true,
203+
cardHeader: true
204+
}
205+
})
206+
207+
expect(wrapper.is('ul')).toBe(true)
208+
expect(wrapper.classes()).toContain('nav')
209+
expect(wrapper.classes()).toContain('nav-pills')
210+
expect(wrapper.classes()).toContain('card-header-pills')
211+
expect(wrapper.classes().length).toBe(3)
212+
expect(wrapper.text()).toBe('')
213+
})
182214
})

src/components/tabs/tabs.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -232,17 +232,10 @@ export const BTabs = /*#__PURE__*/ Vue.extend({
232232
// This computed prop is sniffed by the tab child
233233
return !this.noFade
234234
},
235-
navStyle() {
236-
return this.pills ? 'pills' : 'tabs'
237-
},
238235
localNavClass() {
239236
const classes = []
240-
if (this.card) {
241-
if (this.vertical) {
242-
classes.push('card-header', 'h-100', 'border-bottom-0', 'rounded-0')
243-
} else {
244-
classes.push(`card-header-${this.navStyle}`)
245-
}
237+
if (this.card && this.vertical) {
238+
classes.push('card-header', 'h-100', 'border-bottom-0', 'rounded-0')
246239
}
247240
return [...classes, this.navClass]
248241
}
@@ -640,7 +633,8 @@ export const BTabs = /*#__PURE__*/ Vue.extend({
640633
tabs: !this.noNavStyle && !this.pills,
641634
pills: !this.noNavStyle && this.pills,
642635
vertical: this.vertical,
643-
small: this.small
636+
small: this.small,
637+
cardHeader: this.card && !this.vertical
644638
}
645639
},
646640
[this.normalizeSlot('tabs-start') || h(), buttons, this.normalizeSlot('tabs-end') || h()]

src/directives/scrollspy/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ as well.
3030
<template>
3131
<div>
3232
<b-card no-body>
33-
<b-nav pills slot="header" v-b-scrollspy:nav-scroller>
33+
<b-nav pills card-header slot="header" v-b-scrollspy:nav-scroller>
3434
<b-nav-item href="#fat" @click="scrollIntoView">@fat</b-nav-item>
3535
<b-nav-item href="#mdo" @click="scrollIntoView">@mdo</b-nav-item>
3636
<b-nav-item-dropdown text="Dropdown 1,2,3" right-alignment>
@@ -111,15 +111,15 @@ also be active. Scroll the area next to the navbar and watch the active class ch
111111
<b-col cols="4">
112112
<b-navbar v-b-scrollspy:scrollspy-nested class="flex-column">
113113
<b-navbar-brand href="#">Navbar</b-navbar-brand>
114-
<b-nav pills class="flex-column">
114+
<b-nav pills vertical>
115115
<b-nav-item href="#item-1">Item 1</b-nav-item>
116-
<b-nav pills class="flex-column">
116+
<b-nav pills vertical>
117117
<b-nav-item class="ml-3 my-1" href="#item-1-1">Item 1-1</b-nav-item>
118118
<b-nav-item class="ml-3 my-1" href="#item-1-2">Item 1-2</b-nav-item>
119119
</b-nav>
120120
<b-nav-item href="#item-2">Item 2</b-nav-item>
121121
<b-nav-item href="#item-3">Item 3</b-nav-item>
122-
<b-nav pills class="flex-column">
122+
<b-nav pills vertical>
123123
<b-nav-item class="ml-3 my-1" href="#item-3-1">Item 3-1</b-nav-item>
124124
<b-nav-item class="ml-3 my-1" href="#item-3-2">Item 3-2</b-nav-item>
125125
</b-nav>

0 commit comments

Comments
 (0)