Skip to content

Commit 066574a

Browse files
authored
fix: avoid doubled footer border when all rows visible (#10475)
1 parent 3c9f554 commit 066574a

20 files changed

+226
-69
lines changed

packages/grid/src/styles/vaadin-grid-base-styles.js

Lines changed: 96 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import '@vaadin/component-base/src/styles/style-props.js';
77
import { css } from 'lit';
88

99
export const gridStyles = css`
10+
/* stylelint-disable no-duplicate-selectors */
1011
:host {
1112
display: flex;
1213
max-width: 100%;
@@ -163,50 +164,113 @@ export const gridStyles = css`
163164
164165
/* Row and cell borders */
165166
166-
[part~='last-header-row']::before,
167-
[part~='first-footer-row']::before {
168-
position: absolute;
169-
inset-inline: 0;
167+
[part~='row'] {
168+
&::after {
169+
top: 0;
170+
bottom: calc(var(--_row-border-width) * -1);
171+
}
172+
}
173+
174+
[part~='cell'] {
170175
border-block: var(--_row-border-width) var(--_border-color);
171-
transform: translateX(var(--_grid-horizontal-scroll-position));
176+
border-inline: var(--_column-border-width) var(--_border-color);
177+
border-top-style: solid;
178+
179+
&::after {
180+
top: calc(var(--_row-border-width) * -1);
181+
bottom: calc(var(--_row-border-width) * -1);
182+
}
172183
}
173184
174-
:host([overflow~='top']) [part~='last-header-row']::before {
175-
content: '';
176-
bottom: calc(var(--_row-border-width) * -1);
177-
border-bottom-style: solid;
185+
[part~='last-header-row'],
186+
[part~='first-footer-row'] {
187+
&::before {
188+
position: absolute;
189+
inset-inline: 0;
190+
border-block: var(--_row-border-width) var(--_border-color);
191+
transform: translateX(var(--_grid-horizontal-scroll-position));
192+
}
178193
}
179194
180-
:host([overflow~='bottom']) [part~='first-footer-row']::before,
181-
:host(:not([overflow~='top'])) #scroller:not([empty-state]) [part~='first-footer-row']::before {
182-
content: '';
183-
top: calc(var(--_row-border-width) * -1);
184-
border-top-style: solid;
195+
[part~='first-header-row'] {
196+
[part~='cell'] {
197+
border-top-style: none;
198+
}
199+
200+
[part~='cell']::after {
201+
top: 0;
202+
}
203+
}
204+
205+
[part~='last-header-row'] {
206+
:host([overflow~='top']) &::before {
207+
content: '';
208+
bottom: calc(var(--_row-border-width) * -1);
209+
border-bottom-style: solid;
210+
}
211+
}
212+
213+
#table:not([has-header]) [part~='first-row'] {
214+
[part~='body-cell'] {
215+
border-top-style: none;
216+
}
217+
218+
[part~='body-cell']::after {
219+
top: 0;
220+
}
185221
}
186222
187223
[part~='body-row'] {
188224
scroll-margin-bottom: var(--_row-border-width);
189225
}
190226
191-
/* stylelint-disable-next-line no-duplicate-selectors */
192-
[part~='cell'] {
193-
border-block: var(--_row-border-width) var(--_border-color);
194-
border-inline: var(--_column-border-width) var(--_border-color);
227+
[part~='last-row'] {
228+
&::after {
229+
bottom: 0;
230+
}
231+
232+
[part~='cell']:not([part~='details-opened-row-cell']) {
233+
border-bottom-style: solid;
234+
}
195235
}
196236
197-
[part~='header-cell']:not([part~='first-header-row-cell']),
198-
[part~='footer-cell']:not([part~='first-footer-row-cell']),
199-
[part~='body-cell']:not([part~='first-row-cell']),
200-
#table[has-header] [part~='first-row-cell'] {
201-
border-top-style: solid;
237+
#table:not([has-footer]) [part~='last-row'] {
238+
:host([all-rows-visible]) &,
239+
:host([overflow~='top']) &,
240+
:host([overflow~='bottom']) & {
241+
&::after,
242+
[part~='cell']:not([part~='details-opened-row-cell'])::after {
243+
bottom: 0;
244+
}
245+
246+
[part~='cell']:not([part~='details-opened-row-cell']) {
247+
border-bottom-style: none;
248+
}
249+
}
202250
}
203251
204-
[part~='details-opened-row-cell'],
205-
#table[has-footer] [part~='last-row-cell'],
206-
#table[has-footer] [part~='last-row'] [part~='details-cell'],
207-
:host(:not([overflow~='bottom']):not([overflow~='top'])) [part~='last-row-cell'],
208-
:host(:not([overflow~='bottom']):not([overflow~='top'])) [part~='last-row'] [part~='details-cell'] {
209-
border-bottom-style: solid;
252+
[part~='first-footer-row'] {
253+
&::after {
254+
top: calc(var(--_row-border-width) * -1);
255+
}
256+
257+
[part~='cell'] {
258+
border-top-style: none;
259+
}
260+
261+
:host([overflow~='bottom']) &::before,
262+
:host(:not([overflow~='top']):not([all-rows-visible])) #scroller:not([empty-state]) &::before {
263+
content: '';
264+
top: calc(var(--_row-border-width) * -1);
265+
border-top-style: solid;
266+
}
267+
}
268+
269+
[part~='last-footer-row'] {
270+
&::after,
271+
[part~='cell']::after {
272+
bottom: 0;
273+
}
210274
}
211275
212276
[part~='header-cell']:not([part~='first-column-cell']),
@@ -225,12 +289,10 @@ export const gridStyles = css`
225289
226290
/* Row and cell background */
227291
228-
/* stylelint-disable-next-line no-duplicate-selectors */
229292
[part~='row'] {
230293
background-color: var(--vaadin-grid-row-background-color, var(--vaadin-background-color));
231294
}
232295
233-
/* stylelint-disable-next-line no-duplicate-selectors */
234296
[part~='cell'] {
235297
background-color: inherit;
236298
background-repeat: no-repeat;
@@ -477,14 +539,14 @@ export const gridStyles = css`
477539
:is([part~='row'], [part~='cell'])::after {
478540
position: absolute;
479541
z-index: 3;
480-
inset: 0;
542+
inset-inline: 0;
481543
pointer-events: none;
482544
outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
483545
outline-offset: calc(var(--vaadin-focus-ring-width) * -1);
484546
}
485547
486-
[part~='cell']::after {
487-
inset-block: calc(var(--_row-border-width) * -1);
548+
[part~='row']::after {
549+
transform: translateX(var(--_grid-horizontal-scroll-position));
488550
}
489551
490552
[part~='cell']:where(:not([part~='details-cell']))::after {
@@ -499,33 +561,6 @@ export const gridStyles = css`
499561
inset-inline-end: 0;
500562
}
501563
502-
[part~='row']::after {
503-
inset: 0 0 calc(var(--_row-border-width) * -1);
504-
transform: translateX(var(--_grid-horizontal-scroll-position));
505-
}
506-
507-
[part~='first-header-row-cell']::after,
508-
#table:not([has-header]) [part~='first-row-cell']::after {
509-
top: 0;
510-
}
511-
512-
[part~='first-footer-row']::after {
513-
top: calc(var(--_row-border-width) * -1);
514-
}
515-
516-
[part~='last-row']::after,
517-
[part~='last-footer-row']::after,
518-
[part~='last-footer-row-cell']::after {
519-
bottom: 0;
520-
}
521-
522-
[part~='last-row'] [part~='details-cell']::after,
523-
[part~='last-row']:not([part~='details-opened-row']) [part~='last-row-cell']::after {
524-
:host([overflow~='top']) #table:not([has-footer]) & {
525-
bottom: 0;
526-
}
527-
}
528-
529564
:host([navigating]) [part~='row']:focus,
530565
:host([navigating]) [part~='cell']:focus {
531566
outline: 0;

packages/grid/test/column-rendering.test.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ import { flushGrid, getCellContent, getHeaderCellContent } from './helpers.js';
196196
// Disable cell padding for this test
197197
fixtureSync(`
198198
<style>
199+
vaadin-grid {
200+
--vaadin-grid-row-border-width: 0px;
201+
}
199202
vaadin-grid-cell-content {
200203
padding: 0;
201204
}

packages/grid/test/visual/base/grid-focus.test.js

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ describe('grid focus', () => {
2121
<vaadin-grid-column data-name="Last" width="200px"></vaadin-grid-column>
2222
</vaadin-grid-column-group>
2323
<vaadin-grid-column data-name="City" width="200px"></vaadin-grid-column>
24+
25+
<div slot="empty-state">
26+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio laborum optio quo perferendis unde, fuga reprehenderit molestias cum laboriosam ipsa enim voluptatem iusto fugit. Sed, veniam repudiandae consectetur recusandae laudantium.
27+
</div>
2428
</vaadin-grid>
2529
<style>
2630
vaadin-grid {
@@ -102,6 +106,57 @@ describe('grid focus', () => {
102106
});
103107
});
104108

109+
describe('first footer row', () => {
110+
beforeEach(async () => {
111+
// Focus first footer row
112+
await sendKeys({ press: 'Tab' });
113+
await sendKeys({ press: 'Tab' });
114+
await sendKeys({ press: 'Tab' });
115+
await sendKeys({ press: 'ArrowLeft' });
116+
});
117+
118+
it('default', async () => {
119+
await visualDiff(element, 'first-footer-row');
120+
});
121+
122+
it('first cell', async () => {
123+
await sendKeys({ press: 'ArrowRight' });
124+
await visualDiff(element, 'first-footer-row-first-cell');
125+
});
126+
127+
it('last cell', async () => {
128+
await sendKeys({ press: 'ArrowRight' });
129+
await sendKeys({ press: 'End' });
130+
await visualDiff(element, 'first-footer-row-last-cell');
131+
});
132+
});
133+
134+
describe('last footer row', () => {
135+
beforeEach(async () => {
136+
// Focus last footer row
137+
await sendKeys({ press: 'Tab' });
138+
await sendKeys({ press: 'Tab' });
139+
await sendKeys({ press: 'Tab' });
140+
await sendKeys({ press: 'ArrowLeft' });
141+
await sendKeys({ press: 'ArrowDown' });
142+
});
143+
144+
it('default', async () => {
145+
await visualDiff(element, 'last-footer-row');
146+
});
147+
148+
it('first cell', async () => {
149+
await sendKeys({ press: 'ArrowRight' });
150+
await visualDiff(element, 'last-footer-row-first-cell');
151+
});
152+
153+
it('last cell', async () => {
154+
await sendKeys({ press: 'ArrowRight' });
155+
await sendKeys({ press: 'End' });
156+
await visualDiff(element, 'last-footer-row-last-cell');
157+
});
158+
});
159+
105160
describe('first body row', () => {
106161
beforeEach(async () => {
107162
// Focus first body row
@@ -168,7 +223,10 @@ describe('grid focus', () => {
168223
it('details opened', async () => {
169224
element.openItemDetails(element.items.at(-1));
170225
await nextRender();
171-
await sendKeys({ press: 'ArrowDown' }); // ensure details row is visible
226+
227+
// Ensure the details row is fully visible
228+
await sendKeys({ press: 'ArrowDown' });
229+
await sendKeys({ press: 'ArrowDown' });
172230
await visualDiff(element, 'last-body-row-details-opened');
173231
});
174232

@@ -207,24 +265,59 @@ describe('grid focus', () => {
207265
column.footerRenderer = null;
208266
});
209267
await nextRender();
210-
});
211-
212-
it('last body row', async () => {
213268
await sendKeys({ press: 'Tab' });
214269
await sendKeys({ press: 'Tab' });
215270
await sendKeys({ press: 'ArrowLeft' });
216271
await sendKeys({ press: 'End' });
272+
});
273+
274+
it('last body row', async () => {
217275
await visualDiff(element, 'without-footer-last-body-row');
218276
});
219277

220278
it('last body row cell', async () => {
221-
await sendKeys({ press: 'Tab' });
222-
await sendKeys({ press: 'Tab' });
223-
await sendKeys({ press: 'ArrowLeft' });
224-
await sendKeys({ press: 'End' });
225279
await sendKeys({ press: 'ArrowRight' });
226280
await visualDiff(element, 'without-footer-last-body-row-cell');
227281
});
282+
283+
it('last body row details opened', async () => {
284+
element.openItemDetails(element.items.at(-1));
285+
await nextRender();
286+
287+
// Ensure the details row is fully visible
288+
await sendKeys({ press: 'ArrowDown' });
289+
await sendKeys({ press: 'ArrowDown' });
290+
await visualDiff(element, 'without-footer-last-body-row-details-opened');
291+
});
292+
293+
it('last body row details opened cell', async () => {
294+
element.openItemDetails(element.items.at(-1));
295+
await nextRender();
296+
await sendKeys({ press: 'ArrowRight' });
297+
await sendKeys({ press: 'ArrowDown' });
298+
await visualDiff(element, 'without-footer-last-body-row-details-opened-cell');
299+
});
300+
});
301+
302+
describe('empty state', () => {
303+
beforeEach(async () => {
304+
element.items = [];
305+
await nextRender();
306+
await sendKeys({ press: 'Tab' });
307+
await sendKeys({ press: 'Tab' });
308+
});
309+
310+
it('default', async () => {
311+
await visualDiff(element, 'empty-state');
312+
});
313+
314+
it('without header and footer', async () => {
315+
[...element.querySelectorAll('vaadin-grid-column, vaadin-grid-column-group')].forEach((column) => {
316+
column.headerRenderer = null;
317+
column.footerRenderer = null;
318+
});
319+
await visualDiff(element, 'empty-state-without-header-and-footer');
320+
});
228321
});
229322

230323
describe('scrolling', () => {
8.59 KB
Loading
11.2 KB
Loading
10 KB
Loading
8.28 KB
Loading
9.91 KB
Loading
10 KB
Loading
8.3 KB
Loading

0 commit comments

Comments
 (0)