From 0a759514dfc9afb44c1b63a5f51c702a667b6a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Tue, 14 Mar 2023 16:29:43 +0100 Subject: [PATCH 1/7] fix Property Actions Menu: Make it go on top --- .../workspace-property-layout.element.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element.ts index 5fc679c887..f39fd6f368 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element.ts @@ -44,10 +44,12 @@ export class UmbWorkspacePropertyLayoutElement extends LitElement { p { margin-bottom: 0; } + #header { position: sticky; top: var(--uui-size-space-4); height: min-content; + z-index: 2; } `, ]; From 28b4ea55d868df56868e9d02f30be193f1899dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Mar 2023 20:17:52 +0100 Subject: [PATCH 2/7] make observer re-subscribe on hostConnected if unsubscribed by hostDisconnected --- .../libs/observable-api/observer.controller.ts | 4 ---- .../libs/observable-api/observer.ts | 13 +++++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts index 38f51421cc..cc223e1d48 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts @@ -22,8 +22,4 @@ export class UmbObserverController extends UmbObserver implement host.addController(this); } - - hostConnected() { - return; - } } diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts index bf7210c284..0ce9ce6fef 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts @@ -1,15 +1,20 @@ import { Observable, Subscription } from 'rxjs'; export class UmbObserver { + #source!: Observable; + #callback!: (_value: T) => void; #subscription!: Subscription; constructor(source: Observable, callback: (_value: T) => void) { - this.#subscription = source.subscribe((value) => { - callback(value); - }); + this.#source = source; + this.#subscription = source.subscribe(callback); } - // Notice controller class implements empty hostConnected(). + hostConnected() { + if (this.#subscription.closed) { + this.#subscription = this.#source.subscribe(this.#callback); + } + } hostDisconnected() { this.#subscription.unsubscribe(); From 3dfaa27d57d2bb62d01f5064216050227b9a895c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 16 Mar 2023 09:22:07 +0100 Subject: [PATCH 3/7] comment --- .../libs/observable-api/observer.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts index cc223e1d48..98b1575241 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts @@ -13,6 +13,7 @@ export class UmbObserverController extends UmbObserver implement this._alias = alias; // Lets check if controller is already here: + // No we don't want this, as multiple different controllers might be looking at the same source. /* if (this._subscriptions.has(source)) { const subscription = this._subscriptions.get(source); From 3c9aef06ad70d9e9b92ca72050297d5a770e17fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 16 Mar 2023 09:48:27 +0100 Subject: [PATCH 4/7] correct types --- .../shared/collection/collection.context.ts | 17 ++++---- .../input-document-picker.element.ts | 10 ++--- .../input-language-picker.element.ts | 2 +- .../input-media-picker.element.ts | 10 ++--- .../property-type-based-property.element.ts | 8 +++- .../section-views/section-views.element.ts | 39 ++++++++++++------- .../workspace-property.element.ts | 8 ++-- .../entity-manager-controller.ts | 2 +- .../src/backoffice/themes/theme.context.ts | 14 +++---- 9 files changed, 59 insertions(+), 51 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index 5335a14efe..ff2b2e6753 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -1,4 +1,4 @@ -import type { ContentTreeItemResponseModel, EntityTreeItemResponseModel } from '@umbraco-cms/backend-api'; +import type { EntityTreeItemResponseModel } from '@umbraco-cms/backend-api'; import type { UmbTreeStore } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/context-api'; @@ -6,10 +6,11 @@ import { ArrayState, UmbObserverController } from '@umbraco-cms/observable-api'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { createExtensionClass } from 'libs/extensions-api/create-extension-class.function'; import { UmbTreeRepository } from '@umbraco-cms/repository'; +import { Observable } from 'rxjs'; // TODO: Clean up the need for store as Media has switched to use Repositories(repository). export class UmbCollectionContext< - DataType extends ContentTreeItemResponseModel, + DataType extends EntityTreeItemResponseModel = EntityTreeItemResponseModel, StoreType extends UmbTreeStore = UmbTreeStore > { private _host: UmbControllerHostInterface; @@ -19,9 +20,9 @@ export class UmbCollectionContext< #repository?: UmbTreeRepository; private _store?: StoreType; - protected _dataObserver?: UmbObserverController; + protected _dataObserver?: UmbObserverController; - #data = new ArrayState(>[]); + #data = new ArrayState(>[]); public readonly data = this.#data.asObservable(); #selection = new ArrayState(>[]); @@ -123,7 +124,7 @@ export class UmbCollectionContext< const observable = (await this.#repository.requestTreeItemsOf(this._entityKey)).asObservable?.(); if (observable) { - this._dataObserver = new UmbObserverController(this._host, observable, (nodes) => { + this._dataObserver = new UmbObserverController(this._host, observable as Observable, (nodes) => { if (nodes) { this.#data.next(nodes); } @@ -133,7 +134,7 @@ export class UmbCollectionContext< const observable = (await this.#repository.requestRootTreeItems()).asObservable?.(); if (observable) { - this._dataObserver = new UmbObserverController(this._host, observable, (nodes) => { + this._dataObserver = new UmbObserverController(this._host, observable as Observable, (nodes) => { if (nodes) { this.#data.next(nodes); } @@ -174,6 +175,4 @@ export class UmbCollectionContext< } } -export const UMB_COLLECTION_CONTEXT_TOKEN = new UmbContextToken>( - 'UmbCollectionContext' -); +export const UMB_COLLECTION_CONTEXT_TOKEN = new UmbContextToken>('UmbCollectionContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts index a2a287ab6f..f51dc2af03 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts @@ -9,7 +9,7 @@ import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm'; import { UMB_DOCUMENT_PICKER_MODAL_TOKEN } from '../../../documents/documents/modals/document-picker'; import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/modal'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { DocumentTreeItemResponseModel, FolderTreeItemResponseModel } from '@umbraco-cms/backend-api'; +import type { DocumentTreeItemResponseModel, EntityTreeItemResponseModel } from '@umbraco-cms/backend-api'; import type { UmbObserverController } from '@umbraco-cms/observable-api'; @customElement('umb-input-document-picker') @@ -81,7 +81,7 @@ export class UmbInputDocumentPickerElement extends FormControlMixin(UmbLitElemen private _modalContext?: UmbModalContext; private _documentStore?: UmbDocumentTreeStore; - private _pickedItemsObserver?: UmbObserverController; + private _pickedItemsObserver?: UmbObserverController; constructor() { super(); @@ -133,7 +133,7 @@ export class UmbInputDocumentPickerElement extends FormControlMixin(UmbLitElemen }); } - private async _removeItem(item: FolderTreeItemResponseModel) { + private async _removeItem(item: EntityTreeItemResponseModel) { const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { color: 'danger', headline: `Remove ${item.name}?`, @@ -158,9 +158,9 @@ export class UmbInputDocumentPickerElement extends FormControlMixin(UmbLitElemen `; } - private _renderItem(item: FolderTreeItemResponseModel) { + private _renderItem(item: EntityTreeItemResponseModel) { // TODO: remove when we have a way to handle trashed items - const tempItem = item as FolderTreeItemResponseModel & { isTrashed: boolean }; + const tempItem = item as EntityTreeItemResponseModel & { isTrashed: boolean }; return html` diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts index 512cf9d4bd..7372d64a27 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts @@ -83,7 +83,7 @@ export class UmbInputLanguagePickerElement extends FormControlMixin(UmbLitElemen private _modalContext?: UmbModalContext; private _repository = new UmbLanguageRepository(this); - private _pickedItemsObserver?: UmbObserverController; + private _pickedItemsObserver?: UmbObserverController; constructor() { super(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts index 165e02f077..ef74cc83da 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts @@ -8,7 +8,7 @@ import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm'; import { UMB_MEDIA_PICKER_MODAL_TOKEN } from '../../../media/media/modals/media-picker'; import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/modal'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { EntityTreeItemResponseModel, FolderTreeItemResponseModel } from '@umbraco-cms/backend-api'; +import type { EntityTreeItemResponseModel } from '@umbraco-cms/backend-api'; import type { UmbObserverController } from '@umbraco-cms/observable-api'; @customElement('umb-input-media-picker') @@ -91,7 +91,7 @@ export class UmbInputMediaPickerElement extends FormControlMixin(UmbLitElement) private _items?: Array; private _modalContext?: UmbModalContext; - private _pickedItemsObserver?: UmbObserverController; + private _pickedItemsObserver?: UmbObserverController; private _repository = new UmbMediaRepository(this); constructor() { @@ -147,7 +147,7 @@ export class UmbInputMediaPickerElement extends FormControlMixin(UmbLitElement) }); } - private _removeItem(item: FolderTreeItemResponseModel) { + private _removeItem(item: EntityTreeItemResponseModel) { const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { color: 'danger', headline: `Remove ${item.name}?`, @@ -177,9 +177,9 @@ export class UmbInputMediaPickerElement extends FormControlMixin(UmbLitElement) `; } - private _renderItem(item: FolderTreeItemResponseModel) { + private _renderItem(item: EntityTreeItemResponseModel) { // TODO: remove when we have a way to handle trashed items - const tempItem = item as FolderTreeItemResponseModel & { isTrashed: boolean }; + const tempItem = item as EntityTreeItemResponseModel & { isTrashed: boolean }; return html` ; + private _dataTypeObserver?: UmbObserverController; @state() private _value?: unknown; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts index fe8129400f..ce66e3de28 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts @@ -39,7 +39,7 @@ export class UmbSectionViewsElement extends UmbLitElement { private _activeViewPathname?: string; private _sectionContext?: UmbSectionContext; - private _extensionsObserver?: UmbObserverController; + private _extensionsObserver?: UmbObserverController; constructor() { super(); @@ -60,34 +60,43 @@ export class UmbSectionViewsElement extends UmbLitElement { private _observeViews() { if (!this._sectionContext) return; - this.observe(this._sectionContext.alias, (sectionAlias) => { - this._observeExtensions(sectionAlias); - }, 'viewsObserver') + this.observe( + this._sectionContext.alias, + (sectionAlias) => { + this._observeExtensions(sectionAlias); + }, + 'viewsObserver' + ); } private _observeExtensions(sectionAlias?: string) { this._extensionsObserver?.destroy(); - if(sectionAlias) { + if (sectionAlias) { this._extensionsObserver = this.observe( - umbExtensionsRegistry?.extensionsOfType('sectionView').pipe( + umbExtensionsRegistry + ?.extensionsOfType('sectionView') + .pipe( map((views) => views .filter((view) => view.meta.sections.includes(sectionAlias)) .sort((a, b) => b.meta.weight - a.meta.weight) ) - ) ?? of([]) - , - (views) => { - this._views = views; - } + ) ?? of([]), + (views) => { + this._views = views; + } ); } } private _observeActiveView() { - if(this._sectionContext) { - this.observe(this._sectionContext?.activeViewPathname, (pathname) => { - this._activeViewPathname = pathname; - }, 'activeViewPathnameObserver'); + if (this._sectionContext) { + this.observe( + this._sectionContext?.activeViewPathname, + (pathname) => { + this._activeViewPathname = pathname; + }, + 'activeViewPathnameObserver' + ); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index e670c5d8e8..6574b32776 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -2,17 +2,17 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined.js'; +import { UmbPropertyEditorElement } from '@umbraco-cms/property-editor'; import { UmbVariantId } from '../../variants/variant-id.class'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; -import type { ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; +import type { ManifestPropertyEditorUI } from '@umbraco-cms/models'; import '../../property-actions/shared/property-action-menu/property-action-menu.element'; import '../../../../backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element'; import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbLitElement } from '@umbraco-cms/element'; import { DataTypePropertyPresentationModel } from '@umbraco-cms/backend-api'; -import { UmbPropertyEditorElement } from '@umbraco-cms/property-editor'; /** * @element umb-workspace-property @@ -151,10 +151,10 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { private _propertyContext = new UmbWorkspacePropertyContext(this); - private propertyEditorUIObserver?: UmbObserverController; + private propertyEditorUIObserver?: UmbObserverController; private _valueObserver?: UmbObserverController; - private _configObserver?: UmbObserverController; + private _configObserver?: UmbObserverController; constructor() { super(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts index 08137a4d6f..0fdd21a59e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts @@ -20,7 +20,7 @@ export class UmbEntityWorkspaceManager< state = new ObjectState(undefined); - protected _storeSubscription?: UmbObserverController; + protected _storeSubscription?: UmbObserverController; private _notificationContext?: UmbNotificationContext; private _store?: StoreType; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts index 5e246bc46e..9f5f0d36fb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts @@ -14,9 +14,9 @@ export class UmbThemeContext { #theme = new StringState('umb-light-theme'); public readonly theme = this.#theme.asObservable(); - private themeSubscription?: UmbObserverController; + private themeSubscription?: UmbObserverController; - #styleElement: HTMLLinkElement| HTMLStyleElement | null = null; + #styleElement: HTMLLinkElement | HTMLStyleElement | null = null; constructor(host: UmbControllerHostInterface) { this._host = host; @@ -43,25 +43,21 @@ export class UmbThemeContext { async (themes) => { this.#styleElement?.remove(); if (themes.length > 0) { - if(themes[0].loader) { - - const styleEl = this.#styleElement = document.createElement('style'); + if (themes[0].loader) { + const styleEl = (this.#styleElement = document.createElement('style')); styleEl.setAttribute('type', 'text/css'); document.head.appendChild(styleEl); const result = await themes[0].loader(); // Checking that this is still our styleElement, it has not been replaced with another theme in between. - if(styleEl === this.#styleElement) { + if (styleEl === this.#styleElement) { (styleEl as any).appendChild(document.createTextNode(result)); } - } else if (themes[0].css) { - this.#styleElement = document.createElement('link'); this.#styleElement.setAttribute('rel', 'stylesheet'); this.#styleElement.setAttribute('href', themes[0].css); document.head.appendChild(this.#styleElement); - } } else { localStorage.removeItem(LOCAL_STORAGE_KEY); From 8e8403fda96e9e8fa8c6b3607e9e2db1835771b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 16 Mar 2023 09:58:33 +0100 Subject: [PATCH 5/7] more tests --- .../libs/observable-api/array-state.test.ts | 110 +++++++++++------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts index 3ff159c362..6964402153 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts @@ -2,8 +2,7 @@ import { expect } from '@open-wc/testing'; import { ArrayState } from './array-state'; describe('ArrayState', () => { - - type ObjectType = {key: string, another: string}; + type ObjectType = { key: string; another: string }; type ArrayType = ObjectType[]; let subject: ArrayState; @@ -11,29 +10,30 @@ describe('ArrayState', () => { beforeEach(() => { initialData = [ - {key: '1', another: 'myValue1'}, - {key: '2', another: 'myValue2'}, - {key: '3', another: 'myValue3'} + { key: '1', another: 'myValue1' }, + { key: '2', another: 'myValue2' }, + { key: '3', another: 'myValue3' }, ]; - subject = new ArrayState(initialData, x => x.key); + subject = new ArrayState(initialData, (x) => x.key); }); - it('replays latests, no matter the amount of subscriptions.', (done) => { - + let amountOfCallbacks = 0; const observer = subject.asObservable(); observer.subscribe((value) => { + amountOfCallbacks++; expect(value).to.be.equal(initialData); }); observer.subscribe((value) => { + amountOfCallbacks++; expect(value).to.be.equal(initialData); - done(); + if (amountOfCallbacks === 2) { + done(); + } }); - }); it('remove method, removes the one with the key', (done) => { - const expectedData = [initialData[0], initialData[2]]; subject.remove(['2']); @@ -42,25 +42,21 @@ describe('ArrayState', () => { expect(JSON.stringify(value)).to.be.equal(JSON.stringify(expectedData)); done(); }); - }); it('filter method, removes anything that is not true of the given predicate method', (done) => { - const expectedData = [initialData[0], initialData[2]]; - subject.filter(x => x.key !== '2'); + subject.filter((x) => x.key !== '2'); const observer = subject.asObservable(); observer.subscribe((value) => { expect(JSON.stringify(value)).to.be.equal(JSON.stringify(expectedData)); done(); }); - }); it('add new item via appendOne method.', (done) => { - - const newItem = {key: '4', another: 'myValue4'}; + const newItem = { key: '4', another: 'myValue4' }; subject.appendOne(newItem); const expectedData = [...initialData, newItem]; @@ -71,37 +67,31 @@ describe('ArrayState', () => { expect(value[3].another).to.be.equal(expectedData[3].another); done(); }); - }); - it('getObservablePart for a specific entry of array', (done) => { - - const subObserver = subject.getObservablePart(data => data.find(x => x.key === '2')); + const subObserver = subject.getObservablePart((data) => data.find((x) => x.key === '2')); subObserver.subscribe((entry) => { - if(entry) { + if (entry) { expect(entry.another).to.be.equal(initialData[1].another); done(); } }); - }); - it('getObservablePart returns undefined if item does not exist', (done) => { - let amountOfCallbacks = 0; - const newItem = {key: '4', another: 'myValue4'}; + const newItem = { key: '4', another: 'myValue4' }; - const subObserver = subject.getObservablePart(data => data.find(x => x.key === newItem.key)); + const subObserver = subject.getObservablePart((data) => data.find((x) => x.key === newItem.key)); subObserver.subscribe((entry) => { amountOfCallbacks++; - if(amountOfCallbacks === 1) { - expect(entry).to.be.equal(undefined);// First callback should give null, cause we didn't have this entry when the subscription was made. + if (amountOfCallbacks === 1) { + expect(entry).to.be.equal(undefined); // First callback should give null, cause we didn't have this entry when the subscription was made. } - if(amountOfCallbacks === 2) { - expect(entry).to.be.equal(newItem);// Second callback should give us the right data: - if(entry) { + if (amountOfCallbacks === 2) { + expect(entry).to.be.equal(newItem); // Second callback should give us the right data: + if (entry) { expect(entry.another).to.be.equal(newItem.another); done(); } @@ -109,13 +99,10 @@ describe('ArrayState', () => { }); subject.appendOne(newItem); - }); - it('asObservable returns the replaced item', (done) => { - - const newItem = {key: '2', another: 'myValue4'}; + const newItem = { key: '2', another: 'myValue4' }; subject.appendOne(newItem); const expectedData = [initialData[0], newItem, initialData[2]]; @@ -126,24 +113,61 @@ describe('ArrayState', () => { expect(value[1].another).to.be.equal(newItem.another); done(); }); - }); it('getObservablePart returns the replaced item', (done) => { - - const newItem = {key: '2', another: 'myValue4'}; + const newItem = { key: '2', another: 'myValue4' }; subject.appendOne(newItem); - const subObserver = subject.getObservablePart(data => data.find(x => x.key === newItem.key)); + const subObserver = subject.getObservablePart((data) => data.find((x) => x.key === newItem.key)); subObserver.subscribe((entry) => { - expect(entry).to.be.equal(newItem);// Second callback should give us the right data: - if(entry) { + expect(entry).to.be.equal(newItem); // Second callback should give us the right data: + if (entry) { expect(entry.another).to.be.equal(newItem.another); done(); } }); - }); + it('getObservablePart replays existing data to any amount of subscribers.', (done) => { + let amountOfCallbacks = 0; + const subObserver = subject.getObservablePart((data) => data.find((x) => x.key === '2')); + subObserver.subscribe((entry) => { + if (entry) { + amountOfCallbacks++; + expect(entry.another).to.be.equal(initialData[1].another); + } + }); + subObserver.subscribe((entry) => { + if (entry) { + amountOfCallbacks++; + expect(entry.another).to.be.equal(initialData[1].another); + if (amountOfCallbacks === 2) { + done(); + } + } + }); + }); + + it('getObservablePart replays existing data to any amount of subscribers.', (done) => { + let amountOfCallbacks = 0; + + const subObserver = subject.getObservablePart((data) => data.find((x) => x.key === '2')); + subObserver.subscribe((entry) => { + if (entry) { + amountOfCallbacks++; + expect(entry.another).to.be.equal(initialData[1].another); + } + }); + subObserver.subscribe((entry) => { + if (entry) { + amountOfCallbacks++; + expect(entry.another).to.be.equal(initialData[1].another); + if (amountOfCallbacks === 2) { + done(); + } + } + }); + }); }); From e17d749385abf68f6edeef08cda8386048f0f598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 16 Mar 2023 09:59:11 +0100 Subject: [PATCH 6/7] lint fixes --- .../src/backoffice/shared/collection/collection.context.ts | 2 +- .../components/workspace-property/workspace-property.element.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index ff2b2e6753..164cbdee97 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -1,3 +1,4 @@ +import { Observable } from 'rxjs'; import type { EntityTreeItemResponseModel } from '@umbraco-cms/backend-api'; import type { UmbTreeStore } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -6,7 +7,6 @@ import { ArrayState, UmbObserverController } from '@umbraco-cms/observable-api'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { createExtensionClass } from 'libs/extensions-api/create-extension-class.function'; import { UmbTreeRepository } from '@umbraco-cms/repository'; -import { Observable } from 'rxjs'; // TODO: Clean up the need for store as Media has switched to use Repositories(repository). export class UmbCollectionContext< diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index 6574b32776..f1be387b72 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -2,9 +2,9 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined.js'; -import { UmbPropertyEditorElement } from '@umbraco-cms/property-editor'; import { UmbVariantId } from '../../variants/variant-id.class'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; +import { UmbPropertyEditorElement } from '@umbraco-cms/property-editor'; import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestPropertyEditorUI } from '@umbraco-cms/models'; From d4c7f6288c11fa5fc4e3e1b6dd02c34e4e91d298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 16 Mar 2023 09:59:29 +0100 Subject: [PATCH 7/7] fix import order --- .../components/workspace-property/workspace-property.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index f1be387b72..6574b32776 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -2,9 +2,9 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined.js'; +import { UmbPropertyEditorElement } from '@umbraco-cms/property-editor'; import { UmbVariantId } from '../../variants/variant-id.class'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; -import { UmbPropertyEditorElement } from '@umbraco-cms/property-editor'; import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestPropertyEditorUI } from '@umbraco-cms/models';