Skip to content

Commit

Permalink
feat!: add a default min-height to grid (#7964)
Browse files Browse the repository at this point in the history
* wip

* use a constant row height

* add tests

* revert changes to dev page

* update visual tests

* add fallback if constructed stylesheets are not supported

* add test

* remove duplicate import

* include scrollbar in measurement

---------

Co-authored-by: Tomi Virkki <virkki@vaadin.com>
  • Loading branch information
sissbruecker and tomivirkki authored Oct 14, 2024
1 parent 17fe568 commit 1cc5372
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 1 deletion.
3 changes: 3 additions & 0 deletions packages/component-base/src/browser-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ export const isTouch = (() => {
return false;
}
})();

export const supportsAdoptingStyleSheets =
window.ShadowRoot && 'adoptedStyleSheets' in Document.prototype && 'replace' in CSSStyleSheet.prototype;
44 changes: 43 additions & 1 deletion packages/grid/src/vaadin-grid-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
import { isElementHidden } from '@vaadin/a11y-base/src/focus-utils.js';
import { TabindexMixin } from '@vaadin/a11y-base/src/tabindex-mixin.js';
import { animationFrame, microTask } from '@vaadin/component-base/src/async.js';
import { isAndroid, isChrome, isFirefox, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js';
import {
isAndroid,
isChrome,
isFirefox,
isIOS,
isSafari,
isTouch,
supportsAdoptingStyleSheets,
} from '@vaadin/component-base/src/browser-utils.js';
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
import { getClosestElement } from '@vaadin/component-base/src/dom-utils.js';
import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
Expand Down Expand Up @@ -269,6 +277,16 @@ export const GridMixin = (superClass) =>
}),
).observe(this.$.table);

const minHeightObserver = new ResizeObserver(() =>
setTimeout(() => {
this.__updateMinHeight();
}),
);

minHeightObserver.observe(this.$.header);
minHeightObserver.observe(this.$.items);
minHeightObserver.observe(this.$.footer);

processTemplates(this);

this._tooltipController = new TooltipController(this);
Expand Down Expand Up @@ -1116,4 +1134,28 @@ export const GridMixin = (superClass) =>
this.__virtualizer.update(start, end);
}
}

/** @private */
__updateMinHeight() {
// Min height is calculated based on the header, footer and a single row
// For now use a hard-coded value for the row that matches a single default row in Lumo
const rowHeight = 36;
const headerHeight = this.$.header.clientHeight;
const footerHeight = this.$.footer.clientHeight;
const scrollbarHeight = this.$.table.offsetHeight - this.$.table.clientHeight;
const minHeight = headerHeight + rowHeight + footerHeight + scrollbarHeight;

// The style is set to host instead of the scroller so that the value can be overridden by the user with "grid { min-height: 0 }"
// Prefer setting style in adopted style sheet to avoid the need to add a confusing inline style on the host element
// If adopted style sheets are not supported, the style is set inline
if (!this.__minHeightStyleSheet && supportsAdoptingStyleSheets) {
this.__minHeightStyleSheet = new CSSStyleSheet();
this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, this.__minHeightStyleSheet];
}
if (this.__minHeightStyleSheet) {
this.__minHeightStyleSheet.replaceSync(`:host { --_grid-min-height: ${minHeight}px; }`);
} else {
this.style.setProperty('--_grid-min-height', `${minHeight}px`);
}
}
};
1 change: 1 addition & 0 deletions packages/grid/src/vaadin-grid-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const gridStyles = css`
flex-direction: column;
animation: 1ms vaadin-grid-appear;
height: 400px;
min-height: var(--_grid-min-height, 0);
flex: 1 1 auto;
align-self: stretch;
position: relative;
Expand Down
3 changes: 3 additions & 0 deletions packages/grid/test/min-height-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import '../theme/lumo/lit-all-imports.js';
import '../src/lit-all-imports.js';
import './min-height.common.js';
2 changes: 2 additions & 0 deletions packages/grid/test/min-height-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-grid.js';
import './min-height.common.js';
118 changes: 118 additions & 0 deletions packages/grid/test/min-height.common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync } from '@vaadin/testing-helpers';
import { flushGrid, infiniteDataProvider, nextResize } from './helpers.js';

describe('min-height', () => {
const rowHeight = 36;

let grid;

function verifyMinHeight(withHeader = false, withFooter = false) {
const height = grid.getBoundingClientRect().height;

const headerHeight = grid.$.header.getBoundingClientRect().height;
if (withHeader) {
expect(headerHeight).to.be.above(0);
} else {
expect(headerHeight).to.equal(0);
}

const footerHeight = grid.$.footer.getBoundingClientRect().height;
if (withFooter) {
expect(footerHeight).to.be.above(0);
} else {
expect(footerHeight).to.equal(0);
}

const expectedHeight = rowHeight + headerHeight + footerHeight;
expect(height).to.equal(expectedHeight);
}

beforeEach(async () => {
grid = fixtureSync(`
<vaadin-grid style="height: 0;">
<vaadin-grid-column></vaadin-grid-column>
</vaadin-grid>
`);
flushGrid(grid);
await nextResize(grid);
});

describe('without header or footer', () => {
it('should should have min-height of one row', () => {
verifyMinHeight();
});
});

describe('with header', () => {
beforeEach(async () => {
grid.querySelector('vaadin-grid-column').header = 'Header';
flushGrid(grid);
await nextResize(grid);
});

it('should should have min-height of header and one row', () => {
verifyMinHeight(true, false);
});
});

describe('with footer', () => {
beforeEach(async () => {
grid.querySelector('vaadin-grid-column').footerRenderer = (root) => {
root.textContent = 'Footer';
};
flushGrid(grid);
await nextResize(grid);
});

it('should should have min-height of footer and one row', () => {
verifyMinHeight(false, true);
});
});

describe('with header and footer', () => {
beforeEach(async () => {
grid.querySelector('vaadin-grid-column').header = 'Header';
grid.querySelector('vaadin-grid-column').footerRenderer = (root) => {
root.textContent = 'Footer';
};
flushGrid(grid);
await nextResize(grid);
});

it('should should have min-height of header, footer and one row', () => {
verifyMinHeight(true, true);
});
});

describe('with data', () => {
beforeEach(async () => {
grid.querySelector('vaadin-grid-column').path = 'value';
grid.querySelector('vaadin-grid-column').header = null;
grid.dataProvider = infiniteDataProvider;
flushGrid(grid);
await nextResize(grid);
});

it('should should have min-height of one row', () => {
verifyMinHeight();
});
});

describe('override', () => {
beforeEach(() => {
fixtureSync(`
<style>
vaadin-grid {
min-height: 200px;
}
</style>
`);
});

it('should allow overriding min-height through stylesheet', () => {
const height = grid.getBoundingClientRect().height;
expect(height).to.equal(200);
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1cc5372

Please sign in to comment.