Skip to content

Commit 56b3958

Browse files
authored
fix(tables): fix issue with sticky columns when table is not responsive but has sticky headers (fixes #4354) (#4356)
1 parent 677de40 commit 56b3958

File tree

7 files changed

+256
-8
lines changed

7 files changed

+256
-8
lines changed

src/components/table/helpers/mixin-tbody-row.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ export default {
8989
const hasDetailsSlot = this.hasNormalizedSlot(detailsSlotName)
9090
const formatted = this.getFormattedValue(item, field)
9191
const key = field.key
92+
const stickyColumn =
93+
!this.isStacked && (this.isResponsive || this.stickyHeader) && field.stickyColumn
9294
// We only uses the helper components for sticky columns to
9395
// improve performance of BTable/BTableLite by reducing the
9496
// total number of vue instances created during render
95-
const cellTag = field.stickyColumn
97+
const cellTag = stickyColumn
9698
? field.isRowHeader
9799
? BTh
98100
: BTd
@@ -118,11 +120,11 @@ export default {
118120
: this.getTdValues(item, key, field.tdAttr, {}))
119121
}
120122
}
121-
if (field.stickyColumn) {
123+
if (stickyColumn) {
122124
// We are using the helper BTd or BTh
123125
data.props = {
124126
stackedHeading: this.isStacked ? field.label : null,
125-
stickyColumn: field.stickyColumn,
127+
stickyColumn: true,
126128
variant: cellVariant
127129
}
128130
} else {

src/components/table/table-sticky-column.spec.js

Lines changed: 220 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ const fields = [
1111
]
1212

1313
describe('table > sticky columns', () => {
14-
it('has expected classes when sticky column is enabled', async () => {
14+
it('has expected classes when sticky column is enabled and responsive', async () => {
1515
const wrapper = mount(BTable, {
1616
propsData: {
1717
responsive: true,
18+
footClone: true,
1819
items: items,
1920
fields: fields
2021
}
@@ -24,14 +25,102 @@ describe('table > sticky columns', () => {
2425
expect(wrapper.is(BTable)).toBe(true)
2526
expect(wrapper.is('div')).toBe(true)
2627
expect(wrapper.classes()).toContain('table-responsive')
28+
expect(wrapper.classes()).not.toContain('b-table-sticky-header')
2729
const table = wrapper.find('table')
2830
expect(table.classes()).toContain('table')
2931
expect(table.classes()).toContain('b-table')
3032

31-
const trs = wrapper.findAll('tbody > tr')
33+
// Body
34+
let trs = wrapper.findAll('tbody > tr')
3235
expect(trs.length).toBe(2)
36+
let cells = trs.at(0).findAll('th, td')
37+
expect(cells.length).toBe(3)
38+
39+
// First column should be BTh with sticky classes
40+
expect(cells.at(0).is(BTh)).toBe(true)
41+
expect(cells.at(0).is('th')).toBe(true)
42+
expect(cells.at(0).classes()).toContain('b-table-sticky-column')
43+
44+
// Second column should be BTd with sticky classes
45+
expect(cells.at(1).is(BTd)).toBe(true)
46+
expect(cells.at(1).is('td')).toBe(true)
47+
expect(cells.at(1).classes()).toContain('b-table-sticky-column')
48+
49+
// Third column should be td
50+
expect(cells.at(2).is(BTd)).toBe(false)
51+
expect(cells.at(2).is(BTh)).toBe(false)
52+
expect(cells.at(2).is('td')).toBe(true)
53+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
54+
55+
// Header cells
56+
trs = wrapper.findAll('thead > tr')
57+
expect(trs.length).toBe(1)
58+
cells = trs.at(0).findAll('th')
59+
expect(cells.length).toBe(3)
60+
61+
// First column should be BTh with sticky classes
62+
expect(cells.at(0).is(BTh)).toBe(true)
63+
expect(cells.at(0).is('th')).toBe(true)
64+
expect(cells.at(0).classes()).toContain('b-table-sticky-column')
65+
66+
// Second column should be BTh with sticky classes
67+
expect(cells.at(1).is(BTh)).toBe(true)
68+
expect(cells.at(1).is('th')).toBe(true)
69+
expect(cells.at(1).classes()).toContain('b-table-sticky-column')
70+
71+
// Third column should be BTh
72+
expect(cells.at(2).is(BTh)).toBe(true)
73+
expect(cells.at(2).is('th')).toBe(true)
74+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
75+
76+
// Footer cells
77+
trs = wrapper.findAll('tfoot > tr')
78+
expect(trs.length).toBe(1)
79+
cells = trs.at(0).findAll('th')
80+
expect(cells.length).toBe(3)
81+
82+
// First column should be BTh with sticky classes
83+
expect(cells.at(0).is(BTh)).toBe(true)
84+
expect(cells.at(0).is('th')).toBe(true)
85+
expect(cells.at(0).classes()).toContain('b-table-sticky-column')
86+
87+
// Second column should be BTh with sticky classes
88+
expect(cells.at(1).is(BTh)).toBe(true)
89+
expect(cells.at(1).is('th')).toBe(true)
90+
expect(cells.at(1).classes()).toContain('b-table-sticky-column')
91+
92+
// Third column should be BTh
93+
expect(cells.at(2).is(BTh)).toBe(true)
94+
expect(cells.at(2).is('th')).toBe(true)
95+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
3396

34-
const cells = trs.at(0).findAll('th, td')
97+
wrapper.destroy()
98+
})
99+
100+
it('has expected classes when sticky column is enabled with sticky headers', async () => {
101+
const wrapper = mount(BTable, {
102+
propsData: {
103+
responsive: false,
104+
stickyHeader: true,
105+
footClone: true,
106+
items: items,
107+
fields: fields
108+
}
109+
})
110+
111+
expect(wrapper).toBeDefined()
112+
expect(wrapper.is(BTable)).toBe(true)
113+
expect(wrapper.is('div')).toBe(true)
114+
expect(wrapper.classes()).not.toContain('table-responsive')
115+
expect(wrapper.classes()).toContain('b-table-sticky-header')
116+
const table = wrapper.find('table')
117+
expect(table.classes()).toContain('table')
118+
expect(table.classes()).toContain('b-table')
119+
120+
// Tbody cells
121+
let trs = wrapper.findAll('tbody > tr')
122+
expect(trs.length).toBe(2)
123+
let cells = trs.at(0).findAll('th, td')
35124
expect(cells.length).toBe(3)
36125

37126
// First column should be BTh with sticky classes
@@ -50,6 +139,134 @@ describe('table > sticky columns', () => {
50139
expect(cells.at(2).is('td')).toBe(true)
51140
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
52141

142+
// Header cells
143+
trs = wrapper.findAll('thead > tr')
144+
expect(trs.length).toBe(1)
145+
cells = trs.at(0).findAll('th')
146+
expect(cells.length).toBe(3)
147+
148+
// First column should be BTh with sticky classes
149+
expect(cells.at(0).is(BTh)).toBe(true)
150+
expect(cells.at(0).is('th')).toBe(true)
151+
expect(cells.at(0).classes()).toContain('b-table-sticky-column')
152+
153+
// Second column should be BTh with sticky classes
154+
expect(cells.at(1).is(BTh)).toBe(true)
155+
expect(cells.at(1).is('th')).toBe(true)
156+
expect(cells.at(1).classes()).toContain('b-table-sticky-column')
157+
158+
// Third column should be BTh
159+
expect(cells.at(2).is(BTh)).toBe(true)
160+
expect(cells.at(2).is('th')).toBe(true)
161+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
162+
163+
// Footer cells
164+
trs = wrapper.findAll('tfoot > tr')
165+
expect(trs.length).toBe(1)
166+
167+
cells = trs.at(0).findAll('th')
168+
expect(cells.length).toBe(3)
169+
170+
// First column should be BTh with sticky classes
171+
expect(cells.at(0).is(BTh)).toBe(true)
172+
expect(cells.at(0).is('th')).toBe(true)
173+
expect(cells.at(0).classes()).toContain('b-table-sticky-column')
174+
175+
// Second column should be BTh with sticky classes
176+
expect(cells.at(1).is(BTh)).toBe(true)
177+
expect(cells.at(1).is('th')).toBe(true)
178+
expect(cells.at(1).classes()).toContain('b-table-sticky-column')
179+
180+
// Third column should be BTh
181+
expect(cells.at(2).is(BTh)).toBe(true)
182+
expect(cells.at(2).is('th')).toBe(true)
183+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
184+
185+
wrapper.destroy()
186+
})
187+
188+
it('does not have sticky classes when sticky column is enabled and not responsive and no sticky header', async () => {
189+
const wrapper = mount(BTable, {
190+
propsData: {
191+
responsive: false,
192+
stickyHeader: false,
193+
footClone: true,
194+
items: items,
195+
fields: fields
196+
}
197+
})
198+
199+
expect(wrapper).toBeDefined()
200+
expect(wrapper.is(BTable)).toBe(true)
201+
expect(wrapper.is('table')).toBe(true)
202+
expect(wrapper.classes()).not.toContain('table-responsive')
203+
expect(wrapper.classes()).not.toContain('b-table-sticky-header')
204+
expect(wrapper.classes()).toContain('table')
205+
expect(wrapper.classes()).toContain('b-table')
206+
207+
// Body
208+
let trs = wrapper.findAll('tbody > tr')
209+
expect(trs.length).toBe(2)
210+
let cells = trs.at(0).findAll('th, td')
211+
expect(cells.length).toBe(3)
212+
213+
// First column should be th
214+
expect(cells.at(0).is(BTh)).toBe(false)
215+
expect(cells.at(0).is('th')).toBe(true)
216+
expect(cells.at(0).classes()).not.toContain('b-table-sticky-column')
217+
218+
// Second column should be td
219+
expect(cells.at(1).is(BTd)).toBe(false)
220+
expect(cells.at(1).is('td')).toBe(true)
221+
expect(cells.at(1).classes()).not.toContain('b-table-sticky-column')
222+
223+
// Third column should be td
224+
expect(cells.at(2).is(BTd)).toBe(false)
225+
expect(cells.at(2).is('td')).toBe(true)
226+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
227+
228+
// Header cells
229+
trs = wrapper.findAll('thead > tr')
230+
expect(trs.length).toBe(1)
231+
cells = trs.at(0).findAll('th')
232+
expect(cells.length).toBe(3)
233+
234+
// First column should be BTh with sticky classes
235+
expect(cells.at(0).is(BTh)).toBe(true)
236+
expect(cells.at(0).is('th')).toBe(true)
237+
expect(cells.at(0).classes()).not.toContain('b-table-sticky-column')
238+
239+
// Second column should be BTh with sticky classes
240+
expect(cells.at(1).is(BTh)).toBe(true)
241+
expect(cells.at(1).is('th')).toBe(true)
242+
expect(cells.at(1).classes()).not.toContain('b-table-sticky-column')
243+
244+
// Third column should be BTh
245+
expect(cells.at(2).is(BTh)).toBe(true)
246+
expect(cells.at(2).is('th')).toBe(true)
247+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
248+
249+
// Footer cells
250+
trs = wrapper.findAll('tfoot > tr')
251+
expect(trs.length).toBe(1)
252+
cells = trs.at(0).findAll('th')
253+
expect(cells.length).toBe(3)
254+
255+
// First column should be BTh with sticky classes
256+
expect(cells.at(0).is(BTh)).toBe(true)
257+
expect(cells.at(0).is('th')).toBe(true)
258+
expect(cells.at(0).classes()).not.toContain('b-table-sticky-column')
259+
260+
// Second column should be BTh with sticky classes
261+
expect(cells.at(1).is(BTh)).toBe(true)
262+
expect(cells.at(1).is('th')).toBe(true)
263+
expect(cells.at(1).classes()).not.toContain('b-table-sticky-column')
264+
265+
// Third column should be BTh
266+
expect(cells.at(2).is(BTh)).toBe(true)
267+
expect(cells.at(2).is('th')).toBe(true)
268+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
269+
53270
wrapper.destroy()
54271
})
55272
})

src/components/table/tbody.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ export const BTbody = /*#__PURE__*/ Vue.extend({
5353
// Sticky headers are only supported in thead
5454
return false
5555
},
56+
hasStickyHeader() {
57+
// Sniffed by <b-tr> / <b-td> / <b-th>
58+
// Needed to handle header background classes, due to lack of
59+
// background color inheritance with Bootstrap v4 table CSS
60+
return !this.isStacked && this.bvTable.stickyHeader
61+
},
5662
tableVariant() /* istanbul ignore next: Not currently sniffed in tests */ {
5763
// Sniffed by <b-tr> / <b-td> / <b-th>
5864
return this.bvTable.tableVariant

src/components/table/td.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,18 @@ export const BTd = /*#__PURE__*/ Vue.extend({
8585
// Sticky headers only apply to cells in table `thead`
8686
return this.bvTableTr.isStickyHeader
8787
},
88-
isStickyColumn() {
88+
hasStickyHeader() {
8989
// Needed to handle header background classes, due to lack of
9090
// background color inheritance with Bootstrap v4 table CSS
91+
return this.bvTableTr.hasStickyHeader
92+
},
93+
isStickyColumn() {
94+
// Needed to handle background classes, due to lack of
95+
// background color inheritance with Bootstrap v4 table CSS
9196
// Sticky column cells are only available in responsive
9297
// mode (horizontal scrolling) or when sticky header mode
9398
// Applies to cells in `thead`, `tbody` and `tfoot`
94-
return !this.isStacked && (this.isResponsive || this.isStickyHeader) && this.stickyColumn
99+
return !this.isStacked && (this.isResponsive || this.hasStickyHeader) && this.stickyColumn
95100
},
96101
rowVariant() {
97102
return this.bvTableTr.variant

src/components/table/tfoot.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ export const BTfoot = /*#__PURE__*/ Vue.extend({
4949
// Sticky headers are only supported in thead
5050
return false
5151
},
52+
hasStickyHeader() {
53+
// Sniffed by <b-tr> / <b-td> / <b-th>
54+
// Needed to handle header background classes, due to lack of
55+
// background color inheritance with Bootstrap v4 table CSS
56+
return !this.isStacked && this.bvTable.stickyHeader
57+
},
5258
tableVariant() /* istanbul ignore next: Not currently sniffed in tests */ {
5359
// Sniffed by <b-tr> / <b-td> / <b-th>
5460
return this.bvTable.tableVariant

src/components/table/thead.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ export const BThead = /*#__PURE__*/ Vue.extend({
5252
// Sticky headers only apply to cells in table `thead`
5353
return !this.isStacked && this.bvTable.stickyHeader
5454
},
55+
hasStickyHeader() {
56+
// Sniffed by <b-tr> / <b-td> / <b-th>
57+
// Needed to handle header background classes, due to lack of
58+
// background color inheritance with Bootstrap v4 table CSS
59+
return !this.isStacked && this.bvTable.stickyHeader
60+
},
5561
tableVariant() {
5662
// Sniffed by <b-tr> / <b-td> / <b-th>
5763
return this.bvTable.tableVariant

src/components/table/tr.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ export const BTr = /*#__PURE__*/ Vue.extend({
5959
// Sticky headers are only supported in thead
6060
return this.bvTableRowGroup.isStickyHeader
6161
},
62+
hasStickyHeader() {
63+
// Sniffed by <b-tr> / <b-td> / <b-th>
64+
// Needed to handle header background classes, due to lack of
65+
// background color inheritance with Bootstrap v4 table CSS
66+
return !this.isStacked && this.bvTableRowGroup.hasStickyHeader
67+
},
6268
tableVariant() {
6369
// Sniffed by <b-td> / <b-th>
6470
return this.bvTableRowGroup.tableVariant

0 commit comments

Comments
 (0)