From acdc28c24a9e2624514ca0e6a4289186cdb859a4 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 10 Oct 2025 17:45:26 +0400 Subject: [PATCH 1/3] feat: add has-drawer and has-navbar attributes --- dev/aura/aura-view.css | 4 +- .../app-layout/src/vaadin-app-layout-mixin.js | 12 +++ .../app-layout/src/vaadin-app-layout.d.ts | 7 ++ packages/app-layout/src/vaadin-app-layout.js | 13 +++- .../dom/__snapshots__/app-layout.test.snap.js | 55 ++++++++++++-- .../app-layout/test/dom/app-layout.test.js | 76 ++++++++++++------- packages/aura/src/components/app-layout.css | 15 ++-- 7 files changed, 130 insertions(+), 52 deletions(-) diff --git a/dev/aura/aura-view.css b/dev/aura/aura-view.css index a609225f81a..b11b48a57fd 100644 --- a/dev/aura/aura-view.css +++ b/dev/aura/aura-view.css @@ -37,9 +37,7 @@ } } -:is(vaadin-app-layout[drawer-opened], vaadin-app-layout:has([slot='navbar'] vaadin-drawer-toggle)) - :is(.aura-view > header, .aura-view-header) - vaadin-drawer-toggle:not([theme~='permanent']), +vaadin-app-layout:is([drawer-opened], [has-navbar]) vaadin-drawer-toggle:not([theme~='permanent']), vaadin-master-detail-layout:not([drawer], [stack]) [slot='detail'].aura-view > header mdl-back-button, vaadin-master-detail-layout:not([drawer], [stack]) [slot='detail'] .aura-view > header mdl-back-button { display: none; diff --git a/packages/app-layout/src/vaadin-app-layout-mixin.js b/packages/app-layout/src/vaadin-app-layout-mixin.js index da1c9ceb3d8..cb365a7276c 100644 --- a/packages/app-layout/src/vaadin-app-layout-mixin.js +++ b/packages/app-layout/src/vaadin-app-layout-mixin.js @@ -208,6 +208,18 @@ export const AppLayoutMixin = (superclass) => window.removeEventListener('keydown', this.__onDrawerKeyDown); } + /** @private */ + __onNavbarSlotChange() { + this._updateTouchOptimizedMode(); + this.toggleAttribute('has-navbar', !!this.querySelector('[slot="navbar"]')); + } + + /** @private */ + __onDrawerSlotChange() { + this._updateDrawerSize(); + this.toggleAttribute('has-drawer', !!this.querySelector('[slot="drawer"]')); + } + /** * A callback for the `primarySection` property observer. * diff --git a/packages/app-layout/src/vaadin-app-layout.d.ts b/packages/app-layout/src/vaadin-app-layout.d.ts index d27edbc7010..656e3da7499 100644 --- a/packages/app-layout/src/vaadin-app-layout.d.ts +++ b/packages/app-layout/src/vaadin-app-layout.d.ts @@ -77,6 +77,13 @@ export type AppLayoutEventMap = AppLayoutCustomEventMap & HTMLElementEventMap; * `navbar` | Container for the navigation bar * `drawer` | Container for the drawer area * + * The following state attributes are available for styling: + * + * Attribute | Description + * ---------------|------------- + * `has-drawer` | Set when the element has a drawer. + * `has-navbar` | Set when the element has a navbar. + * * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation. * * ### Component's slots diff --git a/packages/app-layout/src/vaadin-app-layout.js b/packages/app-layout/src/vaadin-app-layout.js index d443168c3ea..7767a379046 100644 --- a/packages/app-layout/src/vaadin-app-layout.js +++ b/packages/app-layout/src/vaadin-app-layout.js @@ -59,6 +59,13 @@ import { AppLayoutMixin } from './vaadin-app-layout-mixin.js'; * * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation. * + * The following state attributes are available for styling: + * + * Attribute | Description + * ---------------|------------- + * `has-drawer` | Set when the element has a drawer. + * `has-navbar` | Set when the element has a navbar. + * * ### Component's slots * * The following slots are available to be set @@ -121,11 +128,11 @@ class AppLayout extends AppLayoutMixin(ElementMixin(ThemableMixin(PolylitMixin(L render() { return html`
- +
@@ -134,7 +141,7 @@ class AppLayout extends AppLayoutMixin(ElementMixin(ThemableMixin(PolylitMixin(L
`; } diff --git a/packages/app-layout/test/dom/__snapshots__/app-layout.test.snap.js b/packages/app-layout/test/dom/__snapshots__/app-layout.test.snap.js index b604049d93a..493168d7680 100644 --- a/packages/app-layout/test/dom/__snapshots__/app-layout.test.snap.js +++ b/packages/app-layout/test/dom/__snapshots__/app-layout.test.snap.js @@ -1,7 +1,46 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots["vaadin-app-layout desktop layout shadow default"] = +snapshots["vaadin-app-layout host default"] = +` + +`; +/* end snapshot vaadin-app-layout host default */ + +snapshots["vaadin-app-layout host with drawer"] = +` +
+ Drawer Content +
+
+`; +/* end snapshot vaadin-app-layout host with drawer */ + +snapshots["vaadin-app-layout host with navbar"] = +` +
+ Navbar Content +
+
+`; +/* end snapshot vaadin-app-layout host with navbar */ + +snapshots["vaadin-app-layout shadow desktop layout default"] = ` `; -/* end snapshot vaadin-app-layout desktop layout shadow default */ +/* end snapshot vaadin-app-layout shadow desktop layout default */ -snapshots["vaadin-app-layout desktop layout shadow drawer closed"] = +snapshots["vaadin-app-layout shadow desktop layout drawer closed"] = ` `; -/* end snapshot vaadin-app-layout desktop layout shadow drawer closed */ +/* end snapshot vaadin-app-layout shadow desktop layout drawer closed */ -snapshots["vaadin-app-layout mobile layout shadow default"] = +snapshots["vaadin-app-layout shadow mobile layout default"] = ` `; -/* end snapshot vaadin-app-layout mobile layout shadow default */ +/* end snapshot vaadin-app-layout shadow mobile layout default */ -snapshots["vaadin-app-layout mobile layout shadow drawer opened"] = +snapshots["vaadin-app-layout shadow mobile layout drawer opened"] = ` `; -/* end snapshot vaadin-app-layout mobile layout shadow drawer opened */ +/* end snapshot vaadin-app-layout shadow mobile layout drawer opened */ diff --git a/packages/app-layout/test/dom/app-layout.test.js b/packages/app-layout/test/dom/app-layout.test.js index db8ed0188d1..1d19824eae6 100644 --- a/packages/app-layout/test/dom/app-layout.test.js +++ b/packages/app-layout/test/dom/app-layout.test.js @@ -7,31 +7,55 @@ import '../../vaadin-drawer-toggle.js'; describe('vaadin-app-layout', () => { let layout; - async function fixtureLayout() { - layout = fixtureSync(` - - - Drawer Toggle - -
- Drawer Content -
-
Page Content
-
- `); - await nextRender(); - } + describe('host', () => { + beforeEach(() => { + layout = fixtureSync(''); + }); + + it('default', async () => { + await expect(layout).dom.to.equalSnapshot(); + }); - describe('desktop layout', () => { - before(async () => { - await setViewport({ width: 1000, height: 1000 }); + it('with drawer', async () => { + const drawer = document.createElement('div'); + drawer.setAttribute('slot', 'drawer'); + drawer.textContent = 'Drawer Content'; + layout.appendChild(drawer); + await nextRender(); + await expect(layout).dom.to.equalSnapshot(); }); + it('with navbar', async () => { + const navbar = document.createElement('div'); + navbar.setAttribute('slot', 'navbar'); + navbar.textContent = 'Navbar Content'; + layout.appendChild(navbar); + await nextRender(); + await expect(layout).dom.to.equalSnapshot(); + }); + }); + + describe('shadow', () => { beforeEach(async () => { - await fixtureLayout(); + layout = fixtureSync(` + + + Drawer Toggle + +
+ Drawer Content +
+
Page Content
+
+ `); + await nextRender(); }); - describe('shadow', () => { + describe('desktop layout', () => { + before(async () => { + await setViewport({ width: 1000, height: 1000 }); + }); + it('default', async () => { await expect(layout).shadowDom.to.equalSnapshot(); }); @@ -41,18 +65,12 @@ describe('vaadin-app-layout', () => { await expect(layout).shadowDom.to.equalSnapshot(); }); }); - }); - - describe('mobile layout', () => { - before(async () => { - await setViewport({ width: 500, height: 500 }); - }); - beforeEach(async () => { - await fixtureLayout(); - }); + describe('mobile layout', () => { + before(async () => { + await setViewport({ width: 500, height: 500 }); + }); - describe('shadow', () => { it('default', async () => { await expect(layout).shadowDom.to.equalSnapshot(); }); diff --git a/packages/aura/src/components/app-layout.css b/packages/aura/src/components/app-layout.css index 994141f2448..17355dc5b0a 100644 --- a/packages/aura/src/components/app-layout.css +++ b/packages/aura/src/components/app-layout.css @@ -94,7 +94,7 @@ vaadin-app-layout > vaadin-master-detail-layout:nth-child(1 of :not([slot])):nth } } -vaadin-app-layout:has([slot='navbar']):has([slot='drawer']) { +vaadin-app-layout[has-navbar][has-drawer] { padding-top: var(--_vaadin-app-layout-navbar-offset-size); &::part(drawer) { @@ -102,24 +102,21 @@ vaadin-app-layout:has([slot='navbar']):has([slot='drawer']) { } } -vaadin-app-layout:has([slot='drawer']):not([overlay]):not([drawer-opened]) +vaadin-app-layout[has-drawer]:not([overlay]):not([drawer-opened]) > :nth-child(1 of :not([slot])):nth-last-child(1 of :not([slot])) { border-inline-start-width: min(var(--aura-app-layout-inset), var(--_aura-mdl-border)); } -vaadin-app-layout:has([slot='navbar']):has([slot='drawer'])[drawer-opened]:not([overlay]) +vaadin-app-layout[has-navbar][has-drawer][drawer-opened]:not([overlay]) > :nth-child(1 of :not([slot])):nth-last-child(1 of :not([slot])) { border-start-start-radius: var(--aura-app-layout-radius); } -vaadin-app-layout:has([slot='navbar']):has([slot='drawer']) - > :nth-child(1 of :not([slot])):nth-last-child(1 of :not([slot])) { +vaadin-app-layout[has-navbar][has-drawer] > :nth-child(1 of :not([slot])):nth-last-child(1 of :not([slot])) { border-top-width: var(--_aura-mdl-border); } -vaadin-app-layout:has([slot='navbar']) - > :is(:not([slot]), [slot='drawer']) - vaadin-drawer-toggle:not([theme~='permanent']), -vaadin-app-layout:not(:has([slot='navbar'])) > [slot='drawer'] vaadin-drawer-toggle:not([theme~='permanent']) { +vaadin-app-layout[has-navbar] > :is(:not([slot]), [slot='drawer']) vaadin-drawer-toggle:not([theme~='permanent']), +vaadin-app-layout:not([has-navbar]) > [slot='drawer'] vaadin-drawer-toggle:not([theme~='permanent']) { display: none; } From 8de5487f1537b105ecc95b202e08012ee65a3983 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 13 Oct 2025 13:47:05 +0400 Subject: [PATCH 2/3] fix selector to match previous behavior --- dev/aura/aura-view.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/aura/aura-view.css b/dev/aura/aura-view.css index b11b48a57fd..487ca6bd714 100644 --- a/dev/aura/aura-view.css +++ b/dev/aura/aura-view.css @@ -37,7 +37,9 @@ } } -vaadin-app-layout:is([drawer-opened], [has-navbar]) vaadin-drawer-toggle:not([theme~='permanent']), +vaadin-app-layout:is([drawer-opened], :has(> [slot='navbar'] vaadin-drawer-toggle)) + :is(.aura-view > header, .aura-view-header) + vaadin-drawer-toggle:not([theme~='permanent']), vaadin-master-detail-layout:not([drawer], [stack]) [slot='detail'].aura-view > header mdl-back-button, vaadin-master-detail-layout:not([drawer], [stack]) [slot='detail'] .aura-view > header mdl-back-button { display: none; From 04c7b15d239b04566fcbe1a6a628c17508151444 Mon Sep 17 00:00:00 2001 From: Serhii Kulykov Date: Mon, 13 Oct 2025 13:54:04 +0300 Subject: [PATCH 3/3] Apply suggestions from code review --- packages/app-layout/src/vaadin-app-layout.d.ts | 4 ++-- packages/app-layout/src/vaadin-app-layout.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/app-layout/src/vaadin-app-layout.d.ts b/packages/app-layout/src/vaadin-app-layout.d.ts index 656e3da7499..376810be055 100644 --- a/packages/app-layout/src/vaadin-app-layout.d.ts +++ b/packages/app-layout/src/vaadin-app-layout.d.ts @@ -81,8 +81,8 @@ export type AppLayoutEventMap = AppLayoutCustomEventMap & HTMLElementEventMap; * * Attribute | Description * ---------------|------------- - * `has-drawer` | Set when the element has a drawer. - * `has-navbar` | Set when the element has a navbar. + * `has-drawer` | Set when the element has light DOM content in the drawer slot. + * `has-navbar` | Set when the element has light DOM content in the navbar slot. * * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation. * diff --git a/packages/app-layout/src/vaadin-app-layout.js b/packages/app-layout/src/vaadin-app-layout.js index 7767a379046..b170e7581d0 100644 --- a/packages/app-layout/src/vaadin-app-layout.js +++ b/packages/app-layout/src/vaadin-app-layout.js @@ -63,8 +63,8 @@ import { AppLayoutMixin } from './vaadin-app-layout-mixin.js'; * * Attribute | Description * ---------------|------------- - * `has-drawer` | Set when the element has a drawer. - * `has-navbar` | Set when the element has a navbar. + * `has-drawer` | Set when the element has light DOM content in the drawer slot. + * `has-navbar` | Set when the element has light DOM content in the navbar slot. * * ### Component's slots *