Skip to content

Commit 3c56c1e

Browse files
chore(uve): Added new UVEErrorPage (dotCMS#31401)
https://github.com/user-attachments/assets/87a5fd4f-426d-480b-8f36-375deeb7385f [Figma design](https://www.figma.com/design/trbwjLuf5F6KaU1rFucNeg/Edit-Page---UVE?node-id=4654-86803) This pull request introduces a new error handling component for the `edit-ema-editor` and updates related files to incorporate this feature. The changes include adding the new component, updating styles, modifying test cases, and updating the store and messages. ### New Error Handling Component: * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-error/dot-uve-error.component.html`](diffhunk://#diff-b91fff5957efb392d1a16ca9a1a15536986bc30bc097d0ea2fad2c82f970a423R1-R7): Added HTML template for the error component. * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-error/dot-uve-error.component.scss`](diffhunk://#diff-af9a2e98c6284f914139a320042cc33857d3a17d8fa4b5eb8435045362c9dd02R1-R29): Added styles for the error component. * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-error/dot-uve-error.component.ts`](diffhunk://#diff-425bfe244013749700f575e637567a9f71532d922d7372f7812429fa13efe073R1-R34): Implemented the error component with logic to display different messages based on error codes. * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-error/dot-uve-error.component.spec.ts`](diffhunk://#diff-533ec259908b88c86f448ae1bd5b82ee68bdf808c7f21a66e71b76cd16738218R1-R53): Added test cases for the error component. ### Store and Model Updates: * [`core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.ts`](diffhunk://#diff-42fdc01a4da73b6ecede38045c03fd631badeb73e299fbb4ed6442eb1f3ad963R151-R156): Added a computed property `$hasLiveVersion` to the store to track if a live version is available. * [`core-web/libs/portlets/edit-ema/portlet/src/lib/shared/models.ts`](diffhunk://#diff-4b111bf572c55ee766710e9b4ac256c014112a06ad7e08baa8302b290458338bR195): Updated the `DotPage` interface to include `hasLiveVersion` property. ### Integration with Existing Components: * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html`](diffhunk://#diff-7f93b3d5700edd531f6ee9f65ed695175ee686d7b1cfa63b46de3669e65fb1efR30-R32): Integrated the error component into the editor's template. [[1]](diffhunk://#diff-7f93b3d5700edd531f6ee9f65ed695175ee686d7b1cfa63b46de3669e65fb1efR30-R32) [[2]](diffhunk://#diff-7f93b3d5700edd531f6ee9f65ed695175ee686d7b1cfa63b46de3669e65fb1efR46) * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts`](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aL479-R500): Updated test cases to check for the error component. * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.ts`](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365R57): Imported and declared the error component. [[1]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365R57) [[2]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365L122-R124) [[3]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365R167-R168) ### Toolbar Updates: * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-editor-mode-selector/dot-editor-mode-selector.component.ts`](diffhunk://#diff-6f27ece37ce65b48ea79f5a4ffc71c4885dbb2edde6783e16d1810c42b5ae92dL33-R33): Updated to use the new `$hasLiveVersion` property. [[1]](diffhunk://#diff-6f27ece37ce65b48ea79f5a4ffc71c4885dbb2edde6783e16d1810c42b5ae92dL33-R33) [[2]](diffhunk://#diff-6f27ece37ce65b48ea79f5a4ffc71c4885dbb2edde6783e16d1810c42b5ae92dL71-R71) * [`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-editor-mode-selector/dot-editor-mode-selector.component.spec.ts`](diffhunk://#diff-15ea49d517da2919b00a29c3db20ded2e0e19d0a2b0ef65f65e63b36f252e2fdR34-R42): Added tests to verify behavior when there is no live version. [[1]](diffhunk://#diff-15ea49d517da2919b00a29c3db20ded2e0e19d0a2b0ef65f65e63b36f252e2fdR34-R42) [[2]](diffhunk://#diff-15ea49d517da2919b00a29c3db20ded2e0e19d0a2b0ef65f65e63b36f252e2fdR86) [[3]](diffhunk://#diff-15ea49d517da2919b00a29c3db20ded2e0e19d0a2b0ef65f65e63b36f252e2fdR178) ### Message Properties: * [`dotCMS/src/main/webapp/WEB-INF/messages/Language.properties`](diffhunk://#diff-21db9723fa4c52389731b7575f1e19bd364aa992b7e549e22c08a33456994e94L5945-R5955): Added new messages for the error component. --------- Co-authored-by: Kevin Davila <[email protected]>
1 parent d3267f4 commit 3c56c1e

File tree

12 files changed

+202
-54
lines changed

12 files changed

+202
-54
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<i [class]="'pi ' + $info().icon" class="icon" data-testId="icon"></i>
2+
3+
<h4 data-testId="title" class="title">{{ $info().title | dm }}</h4>
4+
5+
<p data-testId="description" class="description">
6+
{{ $info().description | dm }}
7+
</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
@use "variables" as *;
2+
3+
:host {
4+
display: flex;
5+
height: 100%;
6+
justify-content: center;
7+
align-items: center;
8+
flex-direction: column;
9+
}
10+
11+
.icon {
12+
font-size: 4.12rem;
13+
color: $color-palette-gray-700;
14+
}
15+
16+
.title {
17+
font-size: $font-size-xl;
18+
font-weight: $font-weight-medium-bold;
19+
color: $color-palette-gray-700;
20+
margin: 0;
21+
margin-top: $spacing-2;
22+
}
23+
24+
.description {
25+
font-size: $font-size-lmd;
26+
font-weight: $font-weight-regular-bold;
27+
color: $color-palette-gray-700;
28+
margin: 0;
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Spectator } from '@ngneat/spectator';
2+
import { createComponentFactory } from '@ngneat/spectator/jest';
3+
4+
import { signal } from '@angular/core';
5+
6+
import { DotMessageService } from '@dotcms/data-access';
7+
import { MockDotMessageService } from '@dotcms/utils-testing';
8+
9+
import { DotUvePageVersionNotFoundComponent } from './dot-uve-page-version-not-found.component';
10+
11+
import { UVEStore } from '../../../store/dot-uve.store';
12+
13+
const messagesMock = {
14+
'uve.editor.error.404.title': 'No Live Version Available',
15+
'uve.editor.error.404.description':
16+
"There's no live version of this content available for the selected date but you can explore future releases in the calendar. Navigate through the calendar to see what's schedules next."
17+
};
18+
19+
describe('DotUveErrorComponent', () => {
20+
let spectator: Spectator<DotUvePageVersionNotFoundComponent>;
21+
22+
const createComponent = createComponentFactory({
23+
component: DotUvePageVersionNotFoundComponent,
24+
providers: [
25+
{
26+
provide: UVEStore,
27+
useValue: {
28+
errorCode: signal(404)
29+
}
30+
},
31+
{
32+
provide: DotMessageService,
33+
useValue: new MockDotMessageService(messagesMock)
34+
}
35+
]
36+
});
37+
38+
beforeEach(() => {
39+
spectator = createComponent();
40+
});
41+
42+
it('should render the error icon, title and description', () => {
43+
const icon = spectator.query('[data-testId="icon"]');
44+
const title = spectator.query('[data-testId="title"]');
45+
const description = spectator.query('[data-testId="description"]');
46+
47+
expect(icon).toBeDefined();
48+
expect(title).toContainText('No Live Version Available');
49+
expect(description).toContainText(
50+
"There's no live version of this content available for the selected date but you can explore future releases in the calendar. Navigate through the calendar to see what's schedules next."
51+
);
52+
});
53+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ChangeDetectionStrategy, Component, inject, computed } from '@angular/core';
2+
3+
import { DotMessagePipe } from '@dotcms/ui';
4+
5+
import { UVEStore } from '../../../store/dot-uve.store';
6+
@Component({
7+
selector: 'dot-uve-page-version-not-found',
8+
standalone: true,
9+
imports: [DotMessagePipe],
10+
templateUrl: './dot-uve-page-version-not-found.component.html',
11+
styleUrl: './dot-uve-page-version-not-found.component.scss',
12+
changeDetection: ChangeDetectionStrategy.OnPush
13+
})
14+
export class DotUvePageVersionNotFoundComponent {
15+
readonly #store = inject(UVEStore);
16+
17+
readonly $info = computed(() => {
18+
const errorCode = this.#store.errorCode();
19+
20+
if (errorCode === 404) {
21+
return {
22+
icon: 'pi-stopwatch',
23+
title: 'uve.editor.error.404.title',
24+
description: 'uve.editor.error.404.description'
25+
};
26+
}
27+
28+
return {
29+
icon: 'pi-exclamation-triangle',
30+
title: 'uve.editor.error.default.title',
31+
description: 'uve.editor.error.default.description'
32+
};
33+
});
34+
}

core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-editor-mode-selector/dot-editor-mode-selector.component.spec.ts

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ describe('DotEditorModeSelectorComponent', () => {
3131
live: true
3232
}
3333
},
34+
hasLiveVersion: true,
3435
pageParams
3536
};
3637

3738
const mockStore = {
3839
canEditPage: signal(mockStoreState.canEditPage),
3940
pageAPIResponse: signal(mockStoreState.pageAPIResponse),
4041
pageParams: signal(mockStoreState.pageParams),
42+
$hasLiveVersion: signal(mockStoreState.hasLiveVersion),
4143
clearDeviceAndSocialMedia: jest.fn(),
4244
loadPageAsset: jest.fn()
4345
};
@@ -79,22 +81,6 @@ describe('DotEditorModeSelectorComponent', () => {
7981
expect(menuItems).toHaveLength(2);
8082
expect(menuItems.map((item) => item.id)).not.toContain(UVE_MODE.EDIT);
8183
});
82-
83-
it('should exclude LIVE mode when page has no live version', () => {
84-
mockStore.pageAPIResponse.set({
85-
...MOCK_RESPONSE_HEADLESS,
86-
page: {
87-
...MOCK_RESPONSE_HEADLESS.page,
88-
live: false
89-
}
90-
});
91-
mockStore.canEditPage.set(true);
92-
93-
spectator.detectChanges();
94-
const menuItems = component.$menuItems();
95-
expect(menuItems).toHaveLength(2);
96-
expect(menuItems.map((item) => item.id)).not.toContain(UVE_MODE.LIVE);
97-
});
9884
});
9985

10086
describe('$currentModeLabel', () => {
@@ -135,27 +121,16 @@ describe('DotEditorModeSelectorComponent', () => {
135121
});
136122

137123
describe('$modeGuardEffect', () => {
138-
it('should switch to PREVIEW mode when in EDIT mode without edit permission', () => {
139-
mockStore.pageParams.set({ ...pageParams, mode: UVE_MODE.EDIT });
140-
mockStore.canEditPage.set(false);
141-
142-
spectator.detectChanges();
143-
144-
expect(mockStore.loadPageAsset).toHaveBeenCalledWith({
145-
mode: UVE_MODE.PREVIEW,
146-
publishDate: undefined
147-
});
124+
beforeEach(() => {
125+
// Reset mock store to initial state
126+
mockStore.canEditPage.set(true);
127+
mockStore.pageParams.set(pageParams);
128+
jest.clearAllMocks();
148129
});
149130

150-
it('should switch to PREVIEW mode when in LIVE mode without live version', () => {
151-
mockStore.pageParams.set({ ...pageParams, mode: UVE_MODE.LIVE });
152-
mockStore.pageAPIResponse.set({
153-
...MOCK_RESPONSE_HEADLESS,
154-
page: {
155-
...MOCK_RESPONSE_HEADLESS.page,
156-
live: false
157-
}
158-
});
131+
it('should switch to PREVIEW mode when in EDIT mode without edit permission', () => {
132+
mockStore.canEditPage.set(false);
133+
mockStore.pageParams.set({ ...pageParams, mode: UVE_MODE.EDIT });
159134

160135
spectator.detectChanges();
161136

@@ -172,6 +147,7 @@ describe('DotEditorModeSelectorComponent', () => {
172147
mockStore.canEditPage.set(true);
173148
mockStore.pageAPIResponse.set(MOCK_RESPONSE_HEADLESS);
174149
mockStore.pageParams.set(pageParams);
150+
mockStore.$hasLiveVersion.set(true);
175151
});
176152

177153
it('should show menu when clicking the button', () => {

core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-editor-mode-selector/dot-editor-mode-selector.component.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export class DotEditorModeSelectorComponent {
3030

3131
readonly $menuItems = computed(() => {
3232
const canEditPage = this.#store.canEditPage();
33-
const hasLiveVersion = this.#store.pageAPIResponse().page.live;
3433
const menu = [];
3534

3635
if (canEditPage) {
@@ -47,13 +46,11 @@ export class DotEditorModeSelectorComponent {
4746
id: UVE_MODE.PREVIEW
4847
});
4948

50-
if (hasLiveVersion) {
51-
menu.push({
52-
label: 'uve.editor.mode.published',
53-
description: 'uve.editor.mode.published.description',
54-
id: UVE_MODE.LIVE
55-
});
56-
}
49+
menu.push({
50+
label: 'uve.editor.mode.published',
51+
description: 'uve.editor.mode.published.description',
52+
id: UVE_MODE.LIVE
53+
});
5754

5855
return menu;
5956
});
@@ -68,17 +65,11 @@ export class DotEditorModeSelectorComponent {
6865
() => {
6966
const currentMode = untracked(() => this.$currentMode());
7067
const canEditPage = this.#store.canEditPage();
71-
const hasLiveVersion = this.#store.pageAPIResponse().page.live;
7268

7369
// If the user is in edit mode and does not have edit permission, change to preview mode
7470
if (currentMode === UVE_MODE.EDIT && !canEditPage) {
7571
this.onModeChange(UVE_MODE.PREVIEW);
7672
}
77-
78-
// If the user is in live mode and does not have a live version, change to preview mode
79-
if (currentMode === UVE_MODE.LIVE && !hasLiveVersion) {
80-
this.onModeChange(UVE_MODE.PREVIEW);
81-
}
8273
},
8374
{
8475
allowSignalWrites: true

core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
height: $editorProps().iframe?.wrapper?.height
2828
}"
2929
class="iframe-wrapper">
30+
@if (uveStore.status() === UVE_STATUS.ERROR) {
31+
<dot-uve-page-version-not-found />
32+
}
3033
<iframe
3134
(load)="onIframePageLoad()"
3235
[src]="uveStore.$iframeURL() | safeUrl"
@@ -40,6 +43,7 @@
4043
data-testId="iframe"
4144
width="100%"
4245
height="100%"></iframe>
46+
4347
@if ($editorProps().progressBar) {
4448
<p-progressBar
4549
[ngStyle]="{ position: 'absolute', top: '0', left: '0', width: '100%' }"

core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
MockDotHttpErrorManagerService
7777
} from '@dotcms/utils-testing';
7878

79+
import { DotUvePageVersionNotFoundComponent } from './components/dot-uve-page-version-not-found/dot-uve-page-version-not-found.component';
7980
import { DotEmaRunningExperimentComponent } from './components/dot-uve-toolbar/components/dot-ema-running-experiment/dot-ema-running-experiment.component';
8081
import { DotUveWorkflowActionsComponent } from './components/dot-uve-toolbar/components/dot-uve-workflow-actions/dot-uve-workflow-actions.component';
8182
import { DotUveToolbarComponent } from './components/dot-uve-toolbar/dot-uve-toolbar.component';
@@ -476,12 +477,27 @@ describe('EditEmaEditorComponent', () => {
476477
});
477478
});
478479

479-
it('should relaod when Block editor is saved', () => {
480+
it('should reload when Block editor is saved', () => {
480481
const blockEditorSidebar = spectator.query(DotBlockEditorSidebarComponent);
481482
const spy = jest.spyOn(store, 'reloadCurrentPage');
482483
blockEditorSidebar.onSaved.emit();
483484
expect(spy).toHaveBeenCalled();
484485
});
486+
487+
it('should show the error component when there is no live version', () => {
488+
const errorComponent = spectator.query(DotUvePageVersionNotFoundComponent);
489+
490+
spectator.detectChanges();
491+
492+
store.loadPageAsset({
493+
url: 'index',
494+
language_id: '9'
495+
});
496+
497+
spectator.detectChanges();
498+
499+
expect(errorComponent).toBeDefined();
500+
});
485501
});
486502

487503
describe('customer actions', () => {

core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { DotResultsSeoToolComponent } from '@dotcms/portlets/dot-ema/ui';
5454
import { SafeUrlPipe, DotSpinnerModule, DotCopyContentModalService } from '@dotcms/ui';
5555
import { isEqual } from '@dotcms/utils';
5656

57+
import { DotUvePageVersionNotFoundComponent } from './components/dot-uve-page-version-not-found/dot-uve-page-version-not-found.component';
5758
import { DotUveToolbarComponent } from './components/dot-uve-toolbar/dot-uve-toolbar.component';
5859
import { EditEmaPaletteComponent } from './components/edit-ema-palette/edit-ema-palette.component';
5960
import { EmaContentletToolsComponent } from './components/ema-contentlet-tools/ema-contentlet-tools.component';
@@ -119,7 +120,8 @@ import {
119120
ProgressBarModule,
120121
DotResultsSeoToolComponent,
121122
DotUveToolbarComponent,
122-
DotBlockEditorSidebarComponent
123+
DotBlockEditorSidebarComponent,
124+
DotUvePageVersionNotFoundComponent
123125
],
124126
providers: [
125127
DotCopyContentModalService,
@@ -162,6 +164,8 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
162164
readonly $editorContentStyles = this.uveStore.$editorContentStyles;
163165
readonly ogTagsResults$ = toObservable(this.uveStore.ogTagsResults);
164166

167+
readonly UVE_STATUS = UVE_STATUS;
168+
165169
get contentWindow(): Window {
166170
return this.iframe.nativeElement.contentWindow;
167171
}
@@ -1082,8 +1086,6 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
10821086
const { contentlet, container } = payload;
10831087
const { onNumberOfPages = '1', title } = contentlet;
10841088

1085-
// console.log("LLAMADO", this.$editorProps().showDialogs);
1086-
10871089
if (Number(onNumberOfPages) <= 1) {
10881090
this.dialog?.editContentlet(contentlet);
10891091

core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,35 @@ export const BASE_SHELL_PROPS_RESPONSE = {
757757
};
758758

759759
export const UVE_PAGE_RESPONSE_MAP = {
760+
// URLContentMap with hasLiveVersion false
761+
9: of({
762+
page: {
763+
title: 'hello world',
764+
inode: PAGE_INODE_MOCK,
765+
identifier: '123',
766+
canRead: true,
767+
canSeeRules: true,
768+
pageURI: 'page-one',
769+
rendered: '<div>New Content - Hello World</div>',
770+
canEdit: true,
771+
hasLiveVersion: false
772+
},
773+
site: {
774+
identifier: '123'
775+
},
776+
viewAs: {
777+
language: {
778+
id: 4,
779+
language: 'German',
780+
countryCode: 'DE',
781+
languageCode: 'de',
782+
country: 'Germany'
783+
},
784+
persona: DEFAULT_PERSONA
785+
},
786+
urlContentMap: { ...URL_CONTENT_MAP_MOCK, hasLiveVersion: false },
787+
containers: dotPageContainerStructureMock
788+
}),
760789
// Locked without unlock permission
761790
8: of({
762791
page: {

0 commit comments

Comments
 (0)