Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(UVE): UrlContentMap Redirection to Correct Page After Save or Publish #29896 #30334

Merged
merged 6 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ import { DotActionUrlService } from '../services/dot-action-url/dot-action-url.s
import { DotPageApiService } from '../services/dot-page-api.service';
import { DEFAULT_PERSONA, WINDOW } from '../shared/consts';
import { FormStatus, NG_CUSTOM_EVENTS } from '../shared/enums';
import { PAGE_RESPONSE_BY_LANGUAGE_ID, PAYLOAD_MOCK } from '../shared/mocks';
import {
PAGE_RESPONSE_BY_LANGUAGE_ID,
PAGE_RESPONSE_URL_CONTENT_MAP,
PAYLOAD_MOCK
} from '../shared/mocks';
import { UVEStore } from '../store/dot-uve.store';

describe('DotEmaShellComponent', () => {
Expand Down Expand Up @@ -961,7 +965,7 @@ describe('DotEmaShellComponent', () => {

expect(navigate).toHaveBeenCalledWith([], {
queryParams: {
url: 'my-awesome-page'
url: '/my-awesome-page'
},
queryParamsHandling: 'merge'
});
Expand Down Expand Up @@ -1000,6 +1004,40 @@ describe('DotEmaShellComponent', () => {

expect(reloadSpy).toHaveBeenCalled();
});

it('should trigger a store reload if the URL from urlContentMap is the same as the current URL', () => {
const reloadSpy = jest.spyOn(store, 'reload');
// Mocking the uveStore to return a urlContentMap with the same URL as the current one
jest.spyOn(store, 'pageAPIResponse').mockReturnValue(PAGE_RESPONSE_URL_CONTENT_MAP);
jest.spyOn(store, 'params').mockReturnValue({
url: '/test-url',
language_id: '1',
'com.dotmarketing.persona.id': '1'
});

spectator.detectChanges();

spectator.triggerEventHandler(DotEmaDialogComponent, 'action', {
event: new CustomEvent('ng-event', {
detail: {
name: NG_CUSTOM_EVENTS.SAVE_PAGE,
payload: {
htmlPageReferer: '/test-url'
}
}
}),
payload: PAYLOAD_MOCK,
form: {
status: FormStatus.SAVED,
isTranslation: false
}
});

spectator.detectChanges();

expect(reloadSpy).toHaveBeenCalled();
expect(router.navigate).not.toHaveBeenCalled();
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { WINDOW } from '../shared/consts';
import { FormStatus, NG_CUSTOM_EVENTS } from '../shared/enums';
import { DialogAction, DotPage } from '../shared/models';
import { UVEStore } from '../store/dot-uve.store';
import { compareUrlPaths } from '../utils';

@Component({
selector: 'dot-ema-shell',
Expand Down Expand Up @@ -166,23 +167,68 @@ export class DotEmaShellComponent implements OnInit, OnDestroy {
}

case NG_CUSTOM_EVENTS.SAVE_PAGE: {
// Maybe this can be out of the switch but we should evaluate it if it's needed
// This can be undefined
const url = event.detail.payload?.htmlPageReferer?.split('?')[0].replace('/', '');

if (url && this.uveStore.params().url !== url) {
this.navigate({
url
});
this.handleSavePageEvent(event);
break;
}
}
}

return;
}
/**
* Handles the save page event triggered from the dialog.
*
* @param {CustomEvent} event - The event object containing details about the save action.
* @return {void}
*/
private handleSavePageEvent(event: CustomEvent): void {
const url = this.extractPageRefererUrl(event);
const targetUrl = this.getTargetUrl(url);

this.uveStore.reload();
if (this.shouldNavigate(targetUrl)) {
// Navigate to the new URL if it's different from the current one
this.navigate({ url: targetUrl });

break;
}
return;
}

this.uveStore.reload();
}
/**
* Extracts the htmlPageReferer url from the event payload.
*
* @param {CustomEvent} event - The event object containing the payload with the URL.
* @return {string | undefined} - The extracted URL or undefined if not found.
*/
private extractPageRefererUrl(event: CustomEvent): string | undefined {
return event.detail.payload?.htmlPageReferer;
}

/**
* Determines the target URL for navigation.
*
* If `urlContentMap` is present and contains a `URL_MAP_FOR_CONTENT`, it will be used.
* Otherwise, it falls back to the URL extracted from the event.
*
* @param {string | undefined} url - The URL extracted from the event.
* @returns {string | undefined} - The final target URL for navigation, or undefined if none.
*/
private getTargetUrl(url: string | undefined): string | undefined {
const urlContentMap = this.uveStore.pageAPIResponse().urlContentMap;

// Return URL from content map or fallback to the provided URL
return urlContentMap?.URL_MAP_FOR_CONTENT || url;
}

/**
* Determines whether navigation to a new URL is necessary.
*
* @param {string | undefined} targetUrl - The target URL for navigation.
* @returns {boolean} - True if the current URL differs from the target URL and navigation is required.
*/
private shouldNavigate(targetUrl: string | undefined): boolean {
const currentUrl = this.uveStore.params().url;

// Navigate if the target URL is defined and different from the current URL
return targetUrl !== undefined && !compareUrlPaths(targetUrl, currentUrl);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ import { DEFAULT_PERSONA } from '../../../shared/consts';
import {
HEADLESS_BASE_QUERY_PARAMS,
MOCK_RESPONSE_HEADLESS,
MOCK_RESPONSE_VTL,
PAGE_RESPONSE_BY_LANGUAGE_ID,
PAGE_RESPONSE_URL_CONTENT_MAP,
URL_CONTENT_MAP_MOCK
} from '../../../shared/mocks';
import { UVEStore } from '../../../store/dot-uve.store';
Expand Down Expand Up @@ -179,7 +181,9 @@ describe('EditEmaToolbarComponent', () => {
}),
setDevice: jest.fn(),
setSocialMedia: jest.fn(),
params: signal(params)
params: signal(params),
pageAPIResponse: signal(MOCK_RESPONSE_VTL),
reload: jest.fn()
})
]
});
Expand Down Expand Up @@ -446,6 +450,21 @@ describe('EditEmaToolbarComponent', () => {
queryParamsHandling: 'merge'
});
});

it('should trigger a store reload if the URL from urlContentMap is the same as the current URL', () => {
jest.spyOn(store, 'pageAPIResponse').mockReturnValue(PAGE_RESPONSE_URL_CONTENT_MAP);

spectator.triggerEventHandler(DotEditEmaWorkflowActionsComponent, 'newPage', {
pageURI: '/test-url',
url: '/test-url',
languageId: 1
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any);

spectator.detectChanges();
expect(store.reload).toHaveBeenCalled();
expect(router.navigate).not.toHaveBeenCalled();
});
});

describe('dot-ema-info-display', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,21 @@ export class EditEmaToolbarComponent {
});
}

/**
* Determines whether navigation to a new page is necessary based on URL and language changes.
*
* @param {Params} params - The incoming parameters, including a new URL and language ID.
* @returns {boolean} - True if navigation to a new page is needed.
*/
private shouldNavigateToNewPage(params: Params): boolean {
const { url: newUrl, language_id: newLanguageId } = params;
const { url, language_id } = this.uveStore.params();
const { url: currentUrl, language_id: currentLanguageId } = this.uveStore.params();

// Determine the target URL, prioritizing the content map URL if available
const urlContentMap = this.uveStore.pageAPIResponse().urlContentMap;
const targetUrl = urlContentMap?.URL_MAP_FOR_CONTENT || newUrl;

return !compareUrlPaths(newUrl, url) || newLanguageId != language_id;
// Return true if the URL paths are different or the language has changed
return !compareUrlPaths(currentUrl, targetUrl) || newLanguageId != currentLanguageId;
}
}
38 changes: 31 additions & 7 deletions core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,32 @@ export const MOCK_RESPONSE_HEADLESS: DotPageApiResponse = {
containers: mockDotContainers()
};

export const URL_CONTENT_MAP_MOCK = {
contentType: 'Blog',
identifier: '123',
inode: '1234',
title: 'hello world',
baseType: 'CONTENT',
folder: 'SYSTEM_FOLDER',
host: '123',
languageId: 1,
live: true,
modDate: '1722992210315',
modUser: 'dotcms.org.1',
owner: 'dotcms.org.1',
url: '/content.ec123',
working: true,
archived: false,
hasTitleImage: true,
hostName: 'demo.dotcms.com',
locked: false,
modUserName: 'Admin User',
sortOrder: 0,
stInode: '799f176a-d32e-4844-a07c-1b5fcd107578',
titleImage: 'image',
URL_MAP_FOR_CONTENT: '/test-url'
};

export const MOCK_RESPONSE_VTL: DotPageApiResponse = {
page: {
pageURI: 'test-url',
Expand Down Expand Up @@ -214,6 +240,11 @@ export const MOCK_RESPONSE_VTL: DotPageApiResponse = {
containers: mockDotContainers()
};

export const PAGE_RESPONSE_URL_CONTENT_MAP = {
...MOCK_RESPONSE_VTL,
urlContentMap: URL_CONTENT_MAP_MOCK
};

export const dotPageContainerStructureMock: DotPageContainerStructure = {
'123': {
container: {
Expand Down Expand Up @@ -387,13 +418,6 @@ export const EDIT_ACTION_PAYLOAD_MOCK: ActionPayload = {
position: 'before'
};

export const URL_CONTENT_MAP_MOCK = {
contentType: 'Blog',
identifier: '123',
inode: '1234',
title: 'hello world'
};

export const PAGE_RESPONSE_BY_LANGUAGE_ID = {
1: of({
page: {
Expand Down