Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7f3dfc8
GAUD-9165 - Implement the d2l-page skeleton
svanherk May 1, 2026
5994bb7
Linting fixes
svanherk May 1, 2026
4c09b8b
Stacking context adjustments
svanherk May 4, 2026
67d42fd
GAUD-9165 - Flesh out the demo page to be more realistic and include …
svanherk May 4, 2026
2c5444a
Add dialog demonstrating stacking context issue, since dialogs don't …
svanherk May 4, 2026
3d988db
Move background to nav
svanherk May 5, 2026
afe40b1
Remove stacking contexts and any z-index values not proven to be need…
svanherk May 5, 2026
cb0fe91
Unset overflow for main
svanherk May 5, 2026
b1eb358
Better min behaviour for now
svanherk May 5, 2026
398112c
Add demo title
svanherk May 5, 2026
7bb5ab3
Cleanup demo controls
svanherk May 5, 2026
59495f8
Set --d2l-page-header-height to 0 when header is not sticky
svanherk May 5, 2026
d04f7d6
requestUpdate not needed since we're replacing the object
svanherk May 5, 2026
551d5c7
Move observe to connectedCallback
svanherk May 5, 2026
93af9b4
Pull render pieces out into their own functions
svanherk May 5, 2026
e66ae71
Revert "Move observe to connectedCallback"
svanherk May 5, 2026
919577e
Save padding for the d2l-page-footer component
svanherk May 5, 2026
d609d92
Adjust description
svanherk May 5, 2026
20a6859
Re-arrange demo to make cleaner next PR
svanherk May 6, 2026
44e581b
Merge branch 'GAUD-9165-Implement-the-d2l-page-skeleton' into GAUD-91…
svanherk May 6, 2026
c86d890
Footer needs z-index
svanherk May 6, 2026
fa6a9d6
Merge branch 'GAUD-9165-Implement-the-d2l-page-skeleton' into GAUD-91…
svanherk May 6, 2026
05ddf5d
Remove broken dialog example
svanherk May 6, 2026
9509470
Add back end content
svanherk May 6, 2026
5ad1548
Better nav demo
svanherk May 6, 2026
ef89a70
Move around again
svanherk May 6, 2026
36e6b57
GAUD-9843 - Skeleton of the panel mixin and the three components
svanherk May 6, 2026
69efd70
Merge branch 'main' into GAUD-9843-Add-page-panel-components
svanherk May 6, 2026
4425eac
Save panel height properties for next PR
svanherk May 6, 2026
e8d5fb4
Linting fixes
svanherk May 6, 2026
a35de39
No dom changes outside of render
svanherk May 6, 2026
16df33e
Make function private
svanherk May 6, 2026
c1adbb5
Merge branch 'main' into GAUD-9843-Add-page-panel-components
svanherk May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 86 additions & 8 deletions components/page/demo/page-component.js

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the stuff added in the headers for these demos could be a bit better, but I've run out of time 😅

Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import '../../button/button-icon.js';
import '../../button/button-subtle.js';
import '../../button/button.js';
import '../../collapsible-panel/collapsible-panel.js';
import '../../collapsible-panel/collapsible-panel-group.js';
import '../../collapsible-panel/collapsible-panel-summary-item.js';
import '../../demo/demo-page-settings.js';
import '../../dialog/dialog.js';
import '../../filter/filter.js';
import '../../filter/filter-dimension-set.js';
import '../../filter/filter-dimension-set-value.js';
import '../../icons/icon.js';
import '../../inputs/input-checkbox.js';
import '../../inputs/input-date.js';
Expand All @@ -14,8 +20,13 @@ import '../../list/list-item.js';
import '../../list/list-item-content.js';
import '../../list/list-item-nav.js';
import '../../selection/selection-action.js';
import '../../switch/switch-visibility.js';
import '../../switch/switch.js';
import '../../table/table-controls.js';
import '../page.js';
import '../page-main.js';
import '../page-side-nav.js';
import '../page-supporting.js';
import { css, html, LitElement, nothing } from 'lit';
import { navStyles } from './temp-nav-styles.js';
import { selectStyles } from '../../inputs/input-select-styles.js';
Expand All @@ -29,11 +40,15 @@ class PageDemo extends LitElement {
static properties = {
demoMode: { type: Boolean, attribute: 'demo-mode' },
hasFooter: { type: Boolean, attribute: 'has-footer' },
hasMainHeader: { type: Boolean, attribute: 'has-main-header' },
hasSideNavHeader: { type: Boolean, attribute: 'has-side-nav-header' },
hasSideNavPanel: { type: Boolean, attribute: 'has-side-nav-panel' },
hasSupportingHeader: { type: Boolean, attribute: 'has-supporting-header' },
hasSupportingPanel: { type: Boolean, attribute: 'has-supporting-panel' },
navType: { type: String, attribute: 'nav-type' },
widthType: { type: String, attribute: 'width-type' },
_allowThreePanels: { state: true }
_allowThreePanels: { state: true },
_demoDialogOpened: { state: true }
};

static styles = [navStyles, selectStyles, tableStyles, css`
Expand All @@ -42,18 +57,25 @@ class PageDemo extends LitElement {
flex-wrap: wrap;
gap: 0.75rem;
}
d2l-demo-page-settings {
margin-block: 0.6rem;
}
`];

constructor() {
super();
this._allowThreePanels = false; // Temp for dev/testing
this.demoMode = false;
this.hasFooter = false;
this.hasMainHeader = false;
this.hasSideNavHeader = false;
this.hasSideNavPanel = false;
this.hasSupportingHeader = false;
this.hasSupportingPanel = false;
this.navType = 'full';
/** @type {'normal'|'wide'|'fullscreen'} */
this.widthType = 'normal';
this._demoDialogOpened = false;
}

render() {
Expand All @@ -75,6 +97,14 @@ class PageDemo extends LitElement {
}
}

#handleDialogClose() {
this._demoDialogOpened = false;
}

#handleDialogOpen() {
this._demoDialogOpened = true;
}

#handleNavTypeChange(e) {
this.navType = e.target.on ? 'immersive' : 'full';
}
Expand Down Expand Up @@ -107,13 +137,27 @@ class PageDemo extends LitElement {
<d2l-switch id="switch-nav-type" text="Immersive Nav" @change="${this.#handleNavTypeChange}"></d2l-switch>
<d2l-switch id="switch-side-nav-panel" text="Side Nav Panel" data-key="hasSideNavPanel" @change="${this.#handleVisibilityChange}" ?on="${this.hasSideNavPanel}"></d2l-switch>
<d2l-switch id="switch-supporting-panel" text="Supporting Panel" data-key="hasSupportingPanel" @change="${this.#handleVisibilityChange}" ?on="${this.hasSupportingPanel}"></d2l-switch>
<d2l-switch id="switch-main-header" text="Main Header" data-key="hasMainHeader" @change="${this.#handleVisibilityChange}" ?on="${this.hasMainHeader}"></d2l-switch>
<d2l-switch id="switch-footer" text="Footer" data-key="hasFooter" @change="${this.#handleVisibilityChange}"></d2l-switch>
<d2l-switch id="switch-allow-three-panels" text="Allow Three Panels" @change="${this.#handleAllowThreePanelsChange}"></d2l-switch>
</div>
</d2l-collapsible-panel>
` : nothing;
}

#renderDemoSideNavControls() {
return this.demoMode ? html`
<d2l-switch id="switch-side-nav-header" text="Side Nav Header" data-key="hasSideNavHeader" @change="${this.#handleVisibilityChange}" ?on="${this.hasSideNavHeader}"></d2l-switch>
` : nothing;
}

#renderDemoSupportingControls() {
return this.demoMode ? html`
<d2l-switch id="switch-supporting-header" text="Supporting Header" data-key="hasSupportingHeader" @change="${this.#handleVisibilityChange}" ?on="${this.hasSupportingHeader}"></d2l-switch>
<d2l-demo-page-settings panel-title="d2l-page"></d2l-demo-page-settings>
` : nothing;
}

#renderFooter() {
return this.hasFooter ? html`
<div slot="footer">
Expand Down Expand Up @@ -181,7 +225,20 @@ class PageDemo extends LitElement {

#renderMainPanel() {
return html`
<div>
<d2l-page-main>
${this.hasMainHeader ? html`
<d2l-switch-visibility slot="header-start"></d2l-switch-visibility>
<d2l-filter slot="header-end">
<d2l-filter-dimension-set key="type" text="Activity Type" select-all>
<d2l-filter-dimension-set-value key="assignments" text="Assignments" selected></d2l-filter-dimension-set-value>
<d2l-filter-dimension-set-value key="quizzes" text="Quizzes" selected></d2l-filter-dimension-set-value>
<d2l-filter-dimension-set-value key="discussions" text="Discussions"></d2l-filter-dimension-set-value>
<d2l-filter-dimension-set-value key="content" text="Content Topics"></d2l-filter-dimension-set-value>
</d2l-filter-dimension-set>
</d2l-filter>
<d2l-button slot="header-end" primary @click="${this.#handleDialogOpen}">New Assignment</d2l-button>
` : nothing }

${this.#renderDemoMainControls()}
<p>I'm in the <b>default</b> slot of the <b>d2l-page</b> component!</p>

Expand Down Expand Up @@ -333,13 +390,29 @@ class PageDemo extends LitElement {
</table>
</d2l-table-wrapper>
<div style="align-items: end; display: flex; height: 500px;">End of Content</div>
</div>
<d2l-dialog id="demo-dialog" title-text="New Assignment" ?opened="${this._demoDialogOpened}" @d2l-dialog-close="${this.#handleDialogClose}">
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
<d2l-input-text label="Assignment Name" value=""></d2l-input-text>
<d2l-input-number label="Points" value="100"></d2l-input-number>
<d2l-input-date label="Due Date"></d2l-input-date>
<d2l-input-checkbox>Allow late submissions</d2l-input-checkbox>
</div>
<d2l-button slot="footer" primary data-dialog-action="create">Create</d2l-button>
<d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
</d2l-dialog>
</d2l-page-main>
`;
}

#renderSideNavPanel() {
return this.hasSideNavPanel ? html`
<div slot="side-nav">
<d2l-page-side-nav slot="side-nav">
${this.hasSideNavHeader ? html`
<d2l-button-subtle slot="header-start" text="Add Topic" icon="tier1:plus-default"></d2l-button-subtle>
<d2l-button-icon slot="header-end" text="Collapse All" icon="tier1:arrow-collapse"></d2l-button-icon>
<d2l-button-icon slot="header-end" text="Reorder" icon="tier1:dragger"></d2l-button-icon>
` : nothing}
${this.#renderDemoSideNavControls()}
<p>I'm in the <b>side-nav</b> slot of the <b>d2l-page</b> component!</p>
<d2l-list grid drag-multiple style="width: 100%;">
<d2l-list-item-nav key="nav-1" label="Course Overview" color="#006fbf" draggable drag-handle-text="Course Overview" drop-nested action-href="javascript:void(0)" prevent-navigation>
Expand Down Expand Up @@ -433,14 +506,19 @@ class PageDemo extends LitElement {
</d2l-list-item-nav>
</d2l-list>
<div style="align-items: end; display: flex; height: 150px;">End of Content</div>
</div>
</d2l-page-side-nav>
` : nothing;
}

#renderSupportingPanel() {
return this.hasSupportingPanel ? html`
<div slot="supporting">
<d2l-demo-page-settings panel-title="d2l-page"></d2l-demo-page-settings>
<d2l-page-supporting slot="supporting">
${this.hasSupportingHeader ? html`
<d2l-button-subtle slot="header-start" text="Preview" icon="tier1:preview"></d2l-button-subtle>
<d2l-button-icon slot="header-end" text="Full Screen" icon="tier1:fullscreen"></d2l-button-icon>
<d2l-button-icon slot="header-end" text="Dismiss" icon="tier1:close-small"></d2l-button-icon>
` : nothing}
${this.#renderDemoSupportingControls()}
<p>I'm in the <b>supporting</b> slot of the <b>d2l-page</b> component!</p>
<d2l-collapsible-panel-group>
<d2l-collapsible-panel panel-title="Availability Dates and Conditions" expanded>
Expand Down Expand Up @@ -489,7 +567,7 @@ class PageDemo extends LitElement {
</d2l-collapsible-panel>
</d2l-collapsible-panel-group>
<div style="align-items: end; display: flex; height: 150px;">End of Content</div>
</div>
</d2l-page-supporting>
` : nothing;
}
}
Expand Down
24 changes: 24 additions & 0 deletions components/page/page-main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { css, html, LitElement } from 'lit';
import { PagePanelMixin, pagePanelStyles } from './page-panel-mixin.js';

/**
* Component to be placed in the main default slot of d2l-page, providing a panel with optional header
* @slot - The main content of the main page panel
* @slot header-start - Optional start content of the main page header
* @slot header-end - Optional end content of the main page header
*/
class PageMain extends PagePanelMixin(LitElement) {

static styles = [pagePanelStyles, css`
.panel-header {
top: var(--d2l-page-header-height, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also set a variable that the mixin uses to avoid these subclasses knowing much about the internals of the base class.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean? Like set a different css property with this value like --d2l-page-panel-top-offset?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, or just --d2l-page-panel-top, and this would set that on its :host. Not a big deal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to adjust this/add a few more uses and variables to fix the sticky stuff (next draft PR going up), so we can figure it out there. It's a bit confusing and I don't love what I have

}
`];

render() {
return this._renderPanel(html`<slot></slot>`);
}

}

customElements.define('d2l-page-main', PageMain);
74 changes: 74 additions & 0 deletions components/page/page-panel-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import '../colors/colors.js';
import { css, html } from 'lit';
import { classMap } from 'lit/directives/class-map.js';

export const pagePanelStyles = css`
.panel-header:not([hidden]) {
align-items: center;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.panel-header {
background-color: white;
border-bottom: 1px solid var(--d2l-color-mica);
height: 70px;
overflow: hidden;
padding: 0 30px;
position: sticky;
top: 0;
z-index: 14; /* To be over sticky content of our core components, but not over the header shadow */
}

.header-start,
.header-end {
display: flex;
gap: 6px;
}

.panel {
padding: 30px;
}
`;

export const PagePanelMixin = superclass => class extends superclass {

static properties = {
_hasHeader: { state: true },
_slotVisibility: { state: true }
};

constructor() {
super();
this._hasHeader = false;
this._slotVisibility = {};
}

_renderPanel(content) {
const panelClasses = {
'panel': true,
'header-sticky': this._hasHeader
};
const header = html`
<div ?hidden="${!this._hasHeader}" class="panel-header">
<div class="header-start"><slot name="header-start" @slotchange="${this.#handleSlotVisibilityChange}"></slot></div>
<div class="header-end"><slot name="header-end" @slotchange="${this.#handleSlotVisibilityChange}"></slot></div>
</div>`;

return html`
<div>
${header}
<div class="${classMap(panelClasses)}">${content}</div>
</div>
`;
}

#handleSlotVisibilityChange(e) {
const key = e.target.name;
const nodes = e.target.assignedNodes();
this._slotVisibility = { ...this._slotVisibility, [key]: nodes.length !== 0 };

this._hasHeader = this._slotVisibility['header-start'] || this._slotVisibility['header-end'];
}

};
19 changes: 19 additions & 0 deletions components/page/page-side-nav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { html, LitElement } from 'lit';
import { PagePanelMixin, pagePanelStyles } from './page-panel-mixin.js';

/**
* Component to be placed in the side-nav slot of d2l-page, providing a panel with optional header
* @slot - The main content of the side-nav page panel
* @slot header-start - Optional start content of the side-nav page header
* @slot header-end - Optional end content of the side-nav page header
*/
class PageSideNav extends PagePanelMixin(LitElement) {

static styles = [pagePanelStyles];

render() {
return this._renderPanel(html`<slot></slot>`);
}
}

customElements.define('d2l-page-side-nav', PageSideNav);
19 changes: 19 additions & 0 deletions components/page/page-supporting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { html, LitElement } from 'lit';
import { PagePanelMixin, pagePanelStyles } from './page-panel-mixin.js';

/**
* Component to be placed in the supporting slot of d2l-page, providing a panel with optional header
* @slot - The main content of the supporting page panel
* @slot header-start - Optional start content of the supporting page header
* @slot header-end - Optional end content of the supporting page header
*/
class PageSupporting extends PagePanelMixin(LitElement) {

static styles = [pagePanelStyles];

render() {
return this._renderPanel(html`<slot></slot>`);
}
}

customElements.define('d2l-page-supporting', PageSupporting);
6 changes: 5 additions & 1 deletion components/page/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ class Page extends LocalizeCoreElement(LitElement) {
--d2l-page-footer-max-width: 100%;
}

.header {
position: relative;
z-index: 15; /* To be over sticky content of our core components */
}

.page.header-sticky .header {
position: sticky;
top: 0;
z-index: 15; /* To be over sticky content of our core components */
}
Comment on lines +47 to 55

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, we could implement one of the strategies @dbatiste describes in this comment, making the panel headers only apply sticky once the navbar has scrolled way. I'm not sure if it's "worth it" to add all that until we run into a problem with this, but also flexible with what others think.


.content {
Expand Down
Loading