From a9a5bcdcd09f1df51aa37ab65bc0f1b759974f8e Mon Sep 17 00:00:00 2001 From: Duncan Uszkay Date: Mon, 13 Apr 2026 15:04:00 -0400 Subject: [PATCH 1/4] Move to multi-state prop --- components/backdrop/backdrop-loading.js | 49 ++++++++++++------- .../backdrop/test/backdrop-loading.vdiff.js | 12 +++-- components/table/demo/table-test.js | 20 +++++--- components/table/table-wrapper.js | 12 ++--- 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/components/backdrop/backdrop-loading.js b/components/backdrop/backdrop-loading.js index fe781e79b9d..5d92a594d59 100644 --- a/components/backdrop/backdrop-loading.js +++ b/components/backdrop/backdrop-loading.js @@ -25,10 +25,13 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { static get properties() { return { /** - * Used to control whether the loading backdrop is shown - * @type {boolean} + * The state of data in the element being overlaid. Set to 'clean' when the data represents the user's latest selections, 'dirty' when the data does not represent the user's latest selections, and 'loading' if the data is being actively refreshed + * @type {'clean'|'dirty'|'loading'} */ - shown: { type: Boolean }, + dataState: { + reflect: true, + type: String + }, /** * Used to identify content that the backdrop should make inert * @type {boolean} @@ -95,7 +98,7 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { constructor() { super(); - this.shown = false; + this.dataState = 'clean'; this._state = 'hidden'; this._spinnerTop = 0; this._ariaContent = ''; @@ -113,28 +116,40 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { `; } updated(changedProperties) { + if (changedProperties.get('_state') && changedProperties.get('_state') === 'hidden') + { + this.#centerLoadingSpinner(); + } + if (changedProperties.has('_state')) { if (this._state === 'showing') { - setTimeout(() => { - if (this._state === 'showing') this._state = 'shown'; - }, BACKDROP_DELAY_MS); + if (this.dataState === 'loading') { + setTimeout(() => { + if (this._state === 'showing') this._state = 'shown'; + }, BACKDROP_DELAY_MS); + } else { + this._state = 'shown'; + } } } - - if (changedProperties.has('shown') && ( - (reduceMotion && this._state === 'shown') || (!reduceMotion && this._state === 'showing') - )) { - this.#centerLoadingSpinner(); - } } willUpdate(changedProperties) { - if (changedProperties.has('shown')) { + if (changedProperties.has('dataState') && changedProperties.get('dataState') !== undefined) { this.#clearLiveArea(); - if (this.shown) { + + const oldState = changedProperties.get('dataState'); + const newState = this.dataState; + + // Calculate announcements + if (newState === 'loading') { this.#setLiveArea(this.localize('components.backdrop-loading.loadingAnnouncement'), { delay: LOADING_ANNOUNCEMENT_DELAY }); - this.#show(); - } else if (changedProperties.get('shown') !== undefined) { + } else if (oldState === 'loading' && newState === 'clean') { this.#setLiveArea(this.localize('components.backdrop-loading.loadingCompleteAnnouncement')); + } + // Update backdrop + if (oldState === 'clean') { + this.#show(); + } else if (newState === 'clean') { this.#fade(); } } diff --git a/components/backdrop/test/backdrop-loading.vdiff.js b/components/backdrop/test/backdrop-loading.vdiff.js index 597cdc1ec92..6e7075493ec 100644 --- a/components/backdrop/test/backdrop-loading.vdiff.js +++ b/components/backdrop/test/backdrop-loading.vdiff.js @@ -11,14 +11,20 @@ const template = html` `; describe('backdrop-loading', () => { - it('not shown', async() => { + it('clean', async() => { const elem = await fixture(template); await expect(elem).to.be.golden(); }); - it('shown', async() => { + it('dirty', async() => { const elem = await fixture(template); - elem.querySelector('d2l-backdrop-loading').shown = true; + elem.querySelector('d2l-backdrop-loading').dataState = 'dirty'; + await expect(elem).to.be.golden(); + }); + + it('loading', async() => { + const elem = await fixture(template); + elem.querySelector('d2l-backdrop-loading').dataState = 'loading'; await expect(elem).to.be.golden(); }); }); diff --git a/components/table/demo/table-test.js b/components/table/demo/table-test.js index 7014884e4d3..acd122540ba 100644 --- a/components/table/demo/table-test.js +++ b/components/table/demo/table-test.js @@ -14,6 +14,8 @@ import '../../selection/selection-action.js'; import '../../selection/selection-action-dropdown.js'; import '../../selection/selection-action-menu-item.js'; import '../../selection/selection-input.js'; +import '../../inputs/input-radio.js'; +import '../../inputs/input-radio-group.js'; import { css, html, nothing } from 'lit'; import { tableStyles, TableWrapper } from '../table-wrapper.js'; @@ -81,6 +83,7 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper') this._data = data(); this._sortField = undefined; this._sortDesc = false; + this.dataState = 'clean'; } render() { @@ -97,11 +100,11 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper') icon="tier1:${this.stickyHeaders ? 'check' : 'close-default'}" @d2l-selection-action-click="${this._toggleStickyHeaders}" > - + + + + + @@ -167,6 +170,10 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper') `; } + _handleDataStateChange(e) { + this.dataState = e.detail.value; + } + async _handlePagerLoadMore(e) { const pageSize = e.target.pageSize; await new Promise(resolve => setTimeout(resolve, 1000)); @@ -256,9 +263,6 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper') this.requestUpdate(); } - _toggleLoading() { - this.loading = !this.loading; - } _toggleStickyControls() { this.stickyControls = !this.stickyControls; } diff --git a/components/table/table-wrapper.js b/components/table/table-wrapper.js index e227b989f36..7f80efa7266 100644 --- a/components/table/table-wrapper.js +++ b/components/table/table-wrapper.js @@ -311,12 +311,12 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) { type: Boolean, }, /** - * Whether or not to display a loading backdrop. Set this property when the content in the table is being refreshed. - * @type {boolean} + * The state of data in the table. Set to 'clean' when the data represents the user's latest selections, 'dirty' when the data does not represent the user's latest selections, and 'loading' if the data is being actively refreshed + * @type {'clean'|'dirty'|'loading'} */ - loading: { + dataState: { reflect: true, - type: Boolean + type: String }, }; } @@ -388,7 +388,7 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) { this._tableIntersectionObserver = null; this._tableMutationObserver = null; this._tableScrollers = {}; - this.loading = false; + this.dataState = 'clean'; this._excludeStickyColumnsFromScrollCalculations = getFlag('GAUD-9530-exclude-sticky-columns-from-scroll-calculations', false); } @@ -422,7 +422,7 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) { const slot = html`
- +
`; const useScrollWrapper = this.stickyHeadersScrollWrapper || !this.stickyHeaders; From d164e017d085475f37bb4deb78bd77da5a57f5d3 Mon Sep 17 00:00:00 2001 From: Duncan Uszkay Date: Mon, 13 Apr 2026 15:06:40 -0400 Subject: [PATCH 2/4] Add dirty state dialog --- components/backdrop/backdrop-loading.js | 53 +++++++++++++++++++++++-- lang/ar.js | 5 ++- lang/cy.js | 3 ++ lang/da.js | 3 ++ lang/de.js | 3 ++ lang/en-gb.js | 3 ++ lang/en.js | 3 ++ lang/es-es.js | 3 ++ lang/es.js | 3 ++ lang/fr-fr.js | 3 ++ lang/fr.js | 3 ++ lang/haw.js | 3 ++ lang/hi.js | 3 ++ lang/ja.js | 3 ++ lang/ko.js | 3 ++ lang/mi.js | 3 ++ lang/nl.js | 3 ++ lang/pt.js | 3 ++ lang/sv.js | 3 ++ lang/th.js | 3 ++ lang/tr.js | 3 ++ lang/vi.js | 3 ++ lang/zh-cn.js | 3 ++ lang/zh-tw.js | 3 ++ 24 files changed, 119 insertions(+), 5 deletions(-) diff --git a/components/backdrop/backdrop-loading.js b/components/backdrop/backdrop-loading.js index 5d92a594d59..87afd0a1639 100644 --- a/components/backdrop/backdrop-loading.js +++ b/components/backdrop/backdrop-loading.js @@ -12,6 +12,7 @@ const BACKDROP_DELAY_MS = 800; const FADE_DURATION_MS = 500; const SPINNER_DELAY_MS = FADE_DURATION_MS; const LOADING_ANNOUNCEMENT_DELAY = 1000; +const DIRTY_ANNOUNCEMENT_DELAY = 1000; const LOADING_SPINNER_SIZE = 50; @@ -84,9 +85,28 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { opacity: 1; transition: opacity ${FADE_DURATION_MS}ms ease-in ${SPINNER_DELAY_MS}ms; } - - :host([_state="hiding"]) .d2l-backdrop, + :host([_state="shown"][dataState="dirty"]) d2l-loading-spinner, :host([_state="hiding"]) d2l-loading-spinner { + opacity: 0; + transition: opacity ${FADE_DURATION_MS}ms ease-out; + } + + d2l-empty-state-simple { + background-color: var(--d2l-table-controls-background-color, white); + top: 0; + opacity: 0; + height: fit-content; + justify-content: center; + position: relative; + z-index: 1000; + } + :host([_state="shown"]) d2l-empty-state-simple { + opacity: 1; + transition: opacity ${FADE_DURATION_MS}ms ease-in; + } + :host([_state="shown"][dataState="loading"]) d2l-empty-state-simple, + :host([_state="hiding"]) d2l-empty-state-simple { + opacity: 0; transition: opacity ${FADE_DURATION_MS}ms ease-out; } @@ -101,6 +121,7 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { this.dataState = 'clean'; this._state = 'hidden'; this._spinnerTop = 0; + this._dirtyDialogTop = 0; this._ariaContent = ''; } @@ -110,6 +131,9 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { html`
+ + +
` } ${this._ariaContent} @@ -145,17 +169,24 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { this.#setLiveArea(this.localize('components.backdrop-loading.loadingAnnouncement'), { delay: LOADING_ANNOUNCEMENT_DELAY }); } else if (oldState === 'loading' && newState === 'clean') { this.#setLiveArea(this.localize('components.backdrop-loading.loadingCompleteAnnouncement')); + } else if (newState === 'dirty') { + this.#setLiveArea(this.localize('components.backdrop-loading.dirtyAnnouncement'), { delay: DIRTY_ANNOUNCEMENT_DELAY }); } + // Update backdrop if (oldState === 'clean') { this.#show(); } else if (newState === 'clean') { this.#fade(); + } else if (oldState === 'loading' && newState === 'dirty') { + setTimeout(() => { + if (this._state === 'showing') this._state = 'shown'; + }, BACKDROP_DELAY_MS); } } } - #centerLoadingSpinner() { + async #centerLoadingSpinner() { if (this._state === 'hidden') { return; } const loadingSpinner = this.shadowRoot.querySelector('d2l-loading-spinner'); @@ -175,7 +206,13 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { // Adjust for the size of the spinner const spinnerSizeOffset = LOADING_SPINNER_SIZE / 2; + // Adjust for the size of the dirty dialog + await this.shadowRoot.querySelector('d2l-empty-state-simple').getUpdateComplete(); + await this.shadowRoot.querySelector('d2l-empty-state-action-button')?.getUpdateComplete(); + const dirtyDialogSizeOffset = this.shadowRoot.querySelector('d2l-empty-state-simple').getBoundingClientRect().height / 2; + this._spinnerTop = centeringOffset + topOffset - spinnerSizeOffset; + this._dirtyDialogTop = centeringOffset + topOffset - dirtyDialogSizeOffset; } #clearLiveArea() { @@ -201,6 +238,7 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { this._state = 'hiding'; } } + #getBackdropTarget() { const parent = getComposedParent(this); @@ -214,11 +252,17 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { return targetedChildren.length === 0 ? parent : targetedChildren[0]; } + + #handleApplyButton() { + this.dispatchEvent(new CustomEvent('d2l-apply-button-click', { bubbles: true, composed: true })); + } + #handleTransitionEnd() { if (this._state === 'hiding') { this.#hide(); } } + #hide() { this._state = 'hidden'; @@ -226,9 +270,11 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { if (containingBlock.dataset.initiallyInert !== '1') containingBlock.removeAttribute('inert'); } + #setLiveArea(content, { delay } = {}) { this.announcementTimeout = setTimeout(() => this._ariaContent = content, delay || 0); } + #show() { this._state = reduceMotion ? 'shown' : 'showing'; @@ -238,7 +284,6 @@ class LoadingBackdrop extends LocalizeCoreElement(LitElement) { containingBlock.setAttribute('inert', 'inert'); } - } customElements.define('d2l-backdrop-loading', LoadingBackdrop); diff --git a/lang/ar.js b/lang/ar.js index 3e2a23a2f46..6679434c5c9 100644 --- a/lang/ar.js +++ b/lang/ar.js @@ -1,8 +1,11 @@ export default { "components.alert.close": "إغلاق التنبيه", - "components.breadcrumbs.breadcrumb": "مسار التنقل", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", + "components.breadcrumbs.breadcrumb": "مسار التنقل", "components.button-add.addItem": "إضافة عنصر", "components.button-copy.copied": "تم النسخ!", "components.button-copy.error": "فشل النسخ. حاول مرة أخرى، أو حاول النسخ يدويًا.", diff --git a/lang/cy.js b/lang/cy.js index c59c9e705ca..ff2fbc5ad09 100644 --- a/lang/cy.js +++ b/lang/cy.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Cau Hysbysiad", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Briwsionyn Bara", "components.button-add.addItem": "Ychwanegu Eitem", "components.button-copy.copied": "Wedi’i gopïo!", diff --git a/lang/da.js b/lang/da.js index 5956001743d..3c555f16c30 100644 --- a/lang/da.js +++ b/lang/da.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Luk besked", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Brødkrumme", "components.button-add.addItem": "Tilføj element", "components.button-copy.copied": "Kopieret!", diff --git a/lang/de.js b/lang/de.js index 0980a2205e1..f9e8a79f605 100644 --- a/lang/de.js +++ b/lang/de.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Benachrichtigung schließen", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Brotkrümelnavigation", "components.button-add.addItem": "Element hinzufügen", "components.button-copy.copied": "Kopiert.", diff --git a/lang/en-gb.js b/lang/en-gb.js index 4830e2e5336..d38b06373db 100644 --- a/lang/en-gb.js +++ b/lang/en-gb.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Close Alert", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Breadcrumb", "components.button-add.addItem": "Add Item", "components.button-copy.copied": "Copied!", diff --git a/lang/en.js b/lang/en.js index 569d4558a69..f209edaea13 100644 --- a/lang/en.js +++ b/lang/en.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Close Alert", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Breadcrumb", "components.button-add.addItem": "Add Item", "components.button-copy.copied": "Copied!", diff --git a/lang/es-es.js b/lang/es-es.js index 75e15de3b3b..be40f0ab0ea 100644 --- a/lang/es-es.js +++ b/lang/es-es.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Cerrar alerta", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Ruta de navegación", "components.button-add.addItem": "Agregar elemento", "components.button-copy.copied": "¡Copiado!", diff --git a/lang/es.js b/lang/es.js index 909dd1f3040..4ed69eb1190 100644 --- a/lang/es.js +++ b/lang/es.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Cerrar alerta", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Ruta de navegación", "components.button-add.addItem": "Agregar elemento", "components.button-copy.copied": "Copiado.", diff --git a/lang/fr-fr.js b/lang/fr-fr.js index dd1da26104a..32be8feb5b0 100644 --- a/lang/fr-fr.js +++ b/lang/fr-fr.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Fermer l’alerte", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Chemin de navigation", "components.button-add.addItem": "Ajouter un élément", "components.button-copy.copied": "Copie effectuée !", diff --git a/lang/fr.js b/lang/fr.js index c9bf39303af..e7632f8aff2 100644 --- a/lang/fr.js +++ b/lang/fr.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Fermer l’alerte", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Chemin de navigation", "components.button-add.addItem": "Ajouter un élément", "components.button-copy.copied": "Copié!", diff --git a/lang/haw.js b/lang/haw.js index 741deddd8d6..f59d4a62e76 100644 --- a/lang/haw.js +++ b/lang/haw.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Pani i ka makaʻala", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Palapalapala", "components.button-add.addItem": "Pākuʻi Mea", "components.button-copy.copied": "Kope ʻia!", diff --git a/lang/hi.js b/lang/hi.js index 92690d820f5..8300bd6fa5e 100644 --- a/lang/hi.js +++ b/lang/hi.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "अलर्ट बंद करें", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "ब्रेडक्रंब", "components.button-add.addItem": "आइटम जोड़ें", "components.button-copy.copied": "कॉपी किया गया!", diff --git a/lang/ja.js b/lang/ja.js index f3d4bdf0c09..3ac8701c2c7 100644 --- a/lang/ja.js +++ b/lang/ja.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "アラートを閉じる", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "階層", "components.button-add.addItem": "項目の追加", "components.button-copy.copied": "コピーできました。", diff --git a/lang/ko.js b/lang/ko.js index 40d5c0207a4..68460d17bdc 100644 --- a/lang/ko.js +++ b/lang/ko.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "경보 닫기", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "이동 경로", "components.button-add.addItem": "항목 추가", "components.button-copy.copied": "복사 완료!", diff --git a/lang/mi.js b/lang/mi.js index b0e165ac128..d2be6912f5f 100644 --- a/lang/mi.js +++ b/lang/mi.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Kati Matohi", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Pānui", "components.button-add.addItem": "Tāpiri Tūemi", "components.button-copy.copied": "Kua tāruatia!", diff --git a/lang/nl.js b/lang/nl.js index 2e9f9be2de6..97058116170 100644 --- a/lang/nl.js +++ b/lang/nl.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Waarschuwing sluiten", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Kruimelpad", "components.button-add.addItem": "Item toevoegen", "components.button-copy.copied": "Gekopieerd!", diff --git a/lang/pt.js b/lang/pt.js index 6d48a08cc68..5f460ed241d 100644 --- a/lang/pt.js +++ b/lang/pt.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Fechar alerta", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Auxiliar de navegação", "components.button-add.addItem": "Adicionar item", "components.button-copy.copied": "Copiado!", diff --git a/lang/sv.js b/lang/sv.js index e3dd955ce54..b9424f12e38 100644 --- a/lang/sv.js +++ b/lang/sv.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Stängningsvarning", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Sökväg", "components.button-add.addItem": "Lägg till objekt", "components.button-copy.copied": "Kopierat!", diff --git a/lang/th.js b/lang/th.js index 9c910bb8ae1..6881434871a 100644 --- a/lang/th.js +++ b/lang/th.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "ปิดการแจ้งเตือน", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "แถบนำทาง", "components.button-add.addItem": "เพิ่มรายการ", "components.button-copy.copied": "คัดลอกแล้ว!", diff --git a/lang/tr.js b/lang/tr.js index 7f9838396e8..73d77f0c662 100644 --- a/lang/tr.js +++ b/lang/tr.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Kapatma Uyarısı", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "İçerik Haritası", "components.button-add.addItem": "Öğe Ekle", "components.button-copy.copied": "Kopyalandı!", diff --git a/lang/vi.js b/lang/vi.js index 7cd30d8172a..785b2b26d2e 100644 --- a/lang/vi.js +++ b/lang/vi.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "Đóng Cảnh Báo", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "Đường dẫn", "components.button-add.addItem": "Thêm mục", "components.button-copy.copied": "Đã sao chép!", diff --git a/lang/zh-cn.js b/lang/zh-cn.js index d5dd23c630a..e12d8e60887 100644 --- a/lang/zh-cn.js +++ b/lang/zh-cn.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "关闭提醒", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "痕迹导航", "components.button-add.addItem": "添加项目", "components.button-copy.copied": "已复制!", diff --git a/lang/zh-tw.js b/lang/zh-tw.js index 16037be73f3..cf488a39f2e 100644 --- a/lang/zh-tw.js +++ b/lang/zh-tw.js @@ -2,6 +2,9 @@ export default { "components.alert.close": "關閉警示", "components.backdrop-loading.loadingAnnouncement": "Loading.", "components.backdrop-loading.loadingCompleteAnnouncement": "Loading Complete.", + "components.backdrop-loading.dirtyAnnouncement": "Filters have been changed. Click to apply filters", + "components.backdrop-loading.dirtyDialogDescription": "Filters have been changed.", + "components.backdrop-loading.dirtyDialogAction": "Apply Filters.", "components.breadcrumbs.breadcrumb": "導覽路徑", "components.button-add.addItem": "新增項目", "components.button-copy.copied": "複製成功!", From 85bca16369115c4b23880d3b4d7dc7a499c3a29a Mon Sep 17 00:00:00 2001 From: Duncan Uszkay Date: Tue, 14 Apr 2026 11:40:36 -0400 Subject: [PATCH 3/4] Add loading backdrop to list --- components/list/demo/demo-list.js | 34 +++++++++++++++++++++++++++++-- components/list/list.js | 17 +++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/components/list/demo/demo-list.js b/components/list/demo/demo-list.js index 813570c13d2..3b4a5f0274b 100644 --- a/components/list/demo/demo-list.js +++ b/components/list/demo/demo-list.js @@ -13,6 +13,8 @@ import '../list-controls.js'; import '../list-item-content.js'; import '../list-item.js'; import '../list.js'; +import '../../inputs/input-radio.js'; +import '../../inputs/input-radio-group.js'; import { css, html, LitElement } from 'lit'; import { getUniqueId } from '../../../helpers/uniqueId.js'; import { ifDefined } from 'lit/directives/if-defined.js'; @@ -116,10 +118,12 @@ class DemoList extends LitElement { static get properties() { return { + dataState: { type: String }, addButton: { type: Boolean, attribute: 'add-button' }, grid: { type: Boolean }, extendSeparators: { type: Boolean, attribute: 'extend-separators' }, - _lastItemLoadedIndex: { state: true } + _lastItemLoadedIndex: { state: true }, + _selectAllDisabled: { state: true } }; } @@ -144,6 +148,7 @@ class DemoList extends LitElement { this.items = JSON.parse(JSON.stringify(items)); this._lastItemLoadedIndex = 2; this._pageSize = 2; + this.dataState = 'clean'; } render() { @@ -157,7 +162,12 @@ class DemoList extends LitElement { ?extend-separators="${this.extendSeparators}" ?add-button="${this.addButton}" add-button-text="${ifDefined(addButtonText)}"> - + + + + + + @@ -216,6 +226,21 @@ class DemoList extends LitElement { `; } + updated(changedProperties) { + if (changedProperties.has('dataState')) { + switch (this.dataState) { + case 'clean': + this._selectAllDisabled = false; + break; + case 'dirty': + this._selectAllDisabled = true; + break; + case 'loading': + setTimeout(() => { this._selectAllDisabled = this.dataState !== 'clean'; }, 800); + } + } + } + _handleAddItem() { const newKey = getUniqueId(); this.items.push({ @@ -228,6 +253,11 @@ class DemoList extends LitElement { this.requestUpdate(); } + _handleDataStateChange(e) { + this.shadowRoot.querySelector('d2l-list').dataState = e.detail.value; + this.dataState = e.detail.value; + } + _handlePagerLoadMore(e) { // mock delay consumers might have setTimeout(() => { diff --git a/components/list/list.js b/components/list/list.js index 6ffc020f129..d489000bbbc 100644 --- a/components/list/list.js +++ b/components/list/list.js @@ -1,3 +1,4 @@ +import '../backdrop/backdrop-loading.js'; import { css, html, LitElement } from 'lit'; import { getNextFocusable, getPreviousFocusable } from '../../helpers/focus.js'; import { SelectionInfo, SelectionMixin } from '../selection/selection-mixin.js'; @@ -98,6 +99,14 @@ class List extends PageableMixin(SelectionMixin(LitElement)) { * Show selection only on hover, focus or if at least one item is selected. Exclusive for the tile layout * @type {boolean} */ + /** + * The state of data in the table. Set to 'clean' when the data represents the user's latest selections, 'dirty' when the data does not represent the user's latest selections, and 'loading' if the data is being actively refreshed + * @type {'clean'|'dirty'|'loading'} + */ + dataState: { + reflect: true, + type: String + }, selectionWhenInteracted: { type: Boolean, attribute: 'selection-when-interacted', reflect: true }, _breakpoint: { type: Number, reflect: true }, _slimColor: { type: Boolean, reflect: true, attribute: '_slim-color' } @@ -175,6 +184,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) { this._listItemChanges = []; this._childHasColor = false; this._childHasExpandCollapseToggle = false; + this.dataState = 'clean'; this._breakpoint = 0; this._slimColor = false; @@ -247,15 +257,20 @@ class List extends PageableMixin(SelectionMixin(LitElement)) { render() { const role = !this.grid ? 'list' : 'application'; // not using grid role due to Safari+VO: https://bugs.webkit.org/show_bug.cgi?id=291591 const ariaLabel = this.slot !== 'nested' ? this.label : undefined; + console.log(this.dataState); return html`
- +
+ + +
${this._renderPagerContainer()} `; } + willUpdate(changedProperties) { super.willUpdate(changedProperties); if (changedProperties.has('breakpoints') && changedProperties.get('breakpoints') !== undefined) { From bdef83d962dd15db9403fc33ba7bb7e3ba6839e5 Mon Sep 17 00:00:00 2001 From: Duncan Uszkay Date: Tue, 21 Apr 2026 13:50:23 -0400 Subject: [PATCH 4/4] Add disabled state to selection controls --- components/list/list.js | 6 +++++- components/selection/selection-controls.js | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/components/list/list.js b/components/list/list.js index d489000bbbc..317858ed4fa 100644 --- a/components/list/list.js +++ b/components/list/list.js @@ -5,6 +5,8 @@ import { SelectionInfo, SelectionMixin } from '../selection/selection-mixin.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { PageableMixin } from '../paging/pageable-mixin.js'; import { SubscriberRegistryController } from '../../controllers/subscriber/subscriberControllers.js'; +import { querySelectorComposed } from '../../helpers/dom.js'; +import { query } from 'lit/decorators.js'; const keyCodes = { TAB: 9 @@ -257,7 +259,6 @@ class List extends PageableMixin(SelectionMixin(LitElement)) { render() { const role = !this.grid ? 'list' : 'application'; // not using grid role due to Safari+VO: https://bugs.webkit.org/show_bug.cgi?id=291591 const ariaLabel = this.slot !== 'nested' ? this.label : undefined; - console.log(this.dataState); return html` @@ -396,6 +397,9 @@ class List extends PageableMixin(SelectionMixin(LitElement)) { return items.length > 0 ? items[0]._getFlattenedListItems().lazyLoadListItems : new Map(); } + _getLazyLoadItemsFoo() { + } + _handleKeyDown(e) { if (!this.grid || this.slot === 'nested' || e.keyCode !== keyCodes.TAB) return; e.preventDefault(); diff --git a/components/selection/selection-controls.js b/components/selection/selection-controls.js index a491b3e8b83..5fbc5416f85 100644 --- a/components/selection/selection-controls.js +++ b/components/selection/selection-controls.js @@ -39,6 +39,11 @@ export class SelectionControls extends PageableSubscriberMixin(SelectionObserver * @type {boolean} */ selectAllPagesAllowed: { type: Boolean, attribute: 'select-all-pages-allowed' }, + /** + * Disables the select all checkbox + * @type {boolean} + */ + disabled: { type: Boolean, attribute: 'disabled' }, _hasActions: { state: true }, _noSelectionText: { state: true }, _scrolled: { type: Boolean, reflect: true } @@ -118,6 +123,7 @@ export class SelectionControls extends PageableSubscriberMixin(SelectionObserver constructor() { super(); this.noSelection = false; + this.disabled = false; this.noSticky = false; this.selectAllPagesAllowed = false; this._scrolled = false; @@ -183,7 +189,7 @@ export class SelectionControls extends PageableSubscriberMixin(SelectionObserver _renderSelection() { return html` - ${this._provider && !this._noSelectAll ? html`` : nothing} + ${this._provider && !this._noSelectAll ? html`` : nothing} ${this.selectAllPagesAllowed ? html`` : nothing} `;