From d834374d45323f768466e858f97e061f2a8218ce Mon Sep 17 00:00:00 2001 From: Rafael Velazco Date: Tue, 18 Jun 2024 13:49:23 -0400 Subject: [PATCH] fix(UVE): Hide add forms option for Non-Licensed Users in UVE Editor (#28903) ### Proposed Changes * Hide add forms option for Non-Licensed Users in UVE Editor ### Video https://github.com/dotCMS/core/assets/72418962/631e2dd0-be2c-4034-9098-890ac4f1eae8 --- .../ema-contentlet-tools.component.html | 14 +-- .../ema-contentlet-tools.component.spec.ts | 101 +++++++++++++----- .../ema-contentlet-tools.component.ts | 44 +++++--- .../edit-ema-editor.component.html | 33 +++--- .../edit-ema-editor.component.spec.ts | 29 ++++- .../edit-ema/portlet/src/lib/shared/consts.ts | 39 +++++++ 6 files changed, 192 insertions(+), 68 deletions(-) diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.html index 0dc2d484ce1f..50b395c77bf3 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.html +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.html @@ -9,16 +9,16 @@ [ngStyle]="styles.bottomButton" data-testId="add-bottom-button" icon="pi pi-plus"> - +
@if (contentletArea.payload.vtlFiles?.length) { - - + + } { () => (spectator = createComponent({ props: { - contentletArea: contentletAreaMock + contentletArea: contentletAreaMock, + isEnterprise: false } })) ); @@ -77,7 +78,7 @@ describe('EmaContentletToolsComponent', () => { const hideMenu = jest.spyOn(spectator.component.menu, 'hide'); // Open menu - spectator.click('[data-testId="menu-add"]'); + spectator.click(byTestId('menu-add')); //Change contentlet hover spectator.setInput('contentletArea', { @@ -98,13 +99,13 @@ describe('EmaContentletToolsComponent', () => { describe('events', () => { it('should emit delete on delete button click', () => { const deleteSpy = jest.spyOn(spectator.component.delete, 'emit'); - spectator.click('[data-testId="delete-button"]'); + spectator.click(byTestId('delete-button')); expect(deleteSpy).toHaveBeenCalledWith(contentletAreaMock.payload); }); it('should emit edit on edit button click', () => { const deleteSpy = jest.spyOn(spectator.component.edit, 'emit'); - spectator.click('[data-testId="edit-button"]'); + spectator.click(byTestId('edit-button')); expect(deleteSpy).toHaveBeenCalledWith(contentletAreaMock.payload); }); @@ -126,13 +127,13 @@ describe('EmaContentletToolsComponent', () => { describe('top button', () => { it('should open menu on add button click', () => { - spectator.click('[data-testId="add-top-button"]'); + spectator.click(byTestId('add-top-button')); expect(spectator.query('.p-menu-overlay')).not.toBeNull(); }); it('should call addContent on Content option click', () => { const addSpy = jest.spyOn(spectator.component.addContent, 'emit'); - spectator.click('[data-testId="add-top-button"]'); + spectator.click(byTestId('add-top-button')); spectator.click(byText('Content')); expect(addSpy).toHaveBeenCalledWith({ ...contentletAreaMock.payload, @@ -140,60 +141,108 @@ describe('EmaContentletToolsComponent', () => { } as ActionPayload); }); - it('should call addForm on Form option click', () => { - const addSpy = jest.spyOn(spectator.component.addForm, 'emit'); - spectator.click('[data-testId="add-top-button"]'); - spectator.click(byText('Form')); - expect(addSpy).toHaveBeenCalledWith({ - ...contentletAreaMock.payload, - position: 'before' - } as ActionPayload); + it('should not call addForm on Form option click', () => { + spectator.click(byTestId('add-bottom-button')); + const formOption = spectator.query(byText('Form')); + expect(formOption).toBeNull(); }); it('should call addWidget on Widget option click', () => { const addSpy = jest.spyOn(spectator.component.addWidget, 'emit'); - spectator.click('[data-testId="add-top-button"]'); + spectator.click(byTestId('add-top-button')); spectator.click(byText('Widget')); expect(addSpy).toHaveBeenCalledWith({ ...contentletAreaMock.payload, position: 'before' } as ActionPayload); }); + + describe('isEnterprise', () => { + beforeEach( + () => + (spectator = createComponent({ + props: { + contentletArea: contentletAreaMock, + isEnterprise: true + } + })) + ); + + it('should render form option', () => { + spectator.click(byTestId('add-top-button')); + expect(spectator.query(byText('Form'))).toBeDefined(); + }); + + it('should call addForm on Form option click', () => { + const addSpy = jest.spyOn(spectator.component.addForm, 'emit'); + spectator.click(byTestId('add-top-button')); + spectator.click(byText('Form')); + expect(addSpy).toHaveBeenCalledWith({ + ...contentletAreaMock.payload, + position: 'before' + } as ActionPayload); + }); + }); }); describe('bottom button', () => { it('should open menu on button click', () => { - spectator.click('[data-testId="add-bottom-button"]'); + spectator.click(byTestId('add-bottom-button')); expect(spectator.query('.p-menu-overlay')).not.toBeNull(); }); it('should call addContent on Content option click', () => { const addSpy = jest.spyOn(spectator.component.addContent, 'emit'); - spectator.click('[data-testId="add-bottom-button"]'); + spectator.click(byTestId('add-bottom-button')); spectator.click(byText('Content')); expect(addSpy).toHaveBeenCalledWith({ ...contentletAreaMock.payload, position: 'after' } as ActionPayload); }); - it('should call addForm on Form option click', () => { - const addSpy = jest.spyOn(spectator.component.addForm, 'emit'); - spectator.click('[data-testId="add-bottom-button"]'); - spectator.click(byText('Form')); - expect(addSpy).toHaveBeenCalledWith({ - ...contentletAreaMock.payload, - position: 'after' - } as ActionPayload); + + it('should not call addForm on Form option click', () => { + spectator.click(byTestId('add-bottom-button')); + const formOption = spectator.query(byText('Form')); + expect(formOption).toBeNull(); }); + it('should call addWidget on Widget option click', () => { const addSpy = jest.spyOn(spectator.component.addWidget, 'emit'); - spectator.click('[data-testId="add-bottom-button"]'); + spectator.click(byTestId('add-bottom-button')); spectator.click(byText('Widget')); expect(addSpy).toHaveBeenCalledWith({ ...contentletAreaMock.payload, position: 'after' } as ActionPayload); }); + + describe('isEnterprise', () => { + beforeEach( + () => + (spectator = createComponent({ + props: { + contentletArea: contentletAreaMock, + isEnterprise: true + } + })) + ); + + it('should render form option', () => { + spectator.click(byTestId('add-bottom-button')); + expect(spectator.query(byText('Form'))).toBeDefined(); + }); + + it('should call addForm on Form option click', () => { + const addSpy = jest.spyOn(spectator.component.addForm, 'emit'); + spectator.click(byTestId('add-bottom-button')); + spectator.click(byText('Form')); + expect(addSpy).toHaveBeenCalledWith({ + ...contentletAreaMock.payload, + position: 'after' + } as ActionPayload); + }); + }); }); }); diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.ts index 650ecf85508f..d21894ac0bea 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/ema-contentlet-tools/ema-contentlet-tools.component.ts @@ -10,7 +10,8 @@ import { Output, SimpleChanges, ViewChild, - inject + inject, + signal } from '@angular/core'; import { MenuItem } from 'primeng/api'; @@ -41,12 +42,12 @@ export class EmaContentletToolsComponent implements OnChanges { @ViewChild('menu') menu: Menu; @ViewChild('menuVTL') menuVTL: Menu; @ViewChild('dragImage') dragImage: ElementRef; - private dotMessageService = inject(DotMessageService); - private buttonPosition: 'after' | 'before' = 'after'; + @HostBinding('class.hide') @Input() hide = false; @Input() contentletArea: ContentletArea; - @HostBinding('class.hide') @Input() hide = false; + @Input() isEnterprise: boolean; + @Output() addContent = new EventEmitter(); @Output() addForm = new EventEmitter(); @Output() addWidget = new EventEmitter(); @@ -54,40 +55,44 @@ export class EmaContentletToolsComponent implements OnChanges { @Output() editVTL = new EventEmitter(); @Output() delete = new EventEmitter(); - items: MenuItem[] = [ + #dotMessageService = inject(DotMessageService); + ACTIONS_CONTAINER_WIDTH = INITIAL_ACTIONS_CONTAINER_WIDTH; // Now is dynamic based on the page type (Headless - VTL) + vtlFiles: MenuItem[] = []; + #buttonPosition: 'after' | 'before' = 'after'; + + readonly #comunityItems: MenuItem[] = [ { - label: this.dotMessageService.get('content'), + label: this.#dotMessageService.get('content'), command: () => { this.addContent.emit({ ...this.contentletArea.payload, - position: this.buttonPosition + position: this.#buttonPosition }); } }, { - label: this.dotMessageService.get('Widget'), + label: this.#dotMessageService.get('Widget'), command: () => { this.addWidget.emit({ ...this.contentletArea.payload, - position: this.buttonPosition + position: this.#buttonPosition }); } - }, + } + ]; + readonly #enterpriseItems: MenuItem[] = [ { - label: this.dotMessageService.get('form'), + label: this.#dotMessageService.get('form'), command: () => { this.addForm.emit({ ...this.contentletArea.payload, - position: this.buttonPosition + position: this.#buttonPosition }); } } ]; - vtlFiles: MenuItem[] = []; - - ACTIONS_CONTAINER_WIDTH = INITIAL_ACTIONS_CONTAINER_WIDTH; // Now is dynamic based on the page type (Headless - VTL) - + readonly items = signal(this.#comunityItems); protected styles: Record = {}; ngOnChanges(changes: SimpleChanges): void { @@ -95,6 +100,11 @@ export class EmaContentletToolsComponent implements OnChanges { return; } + // If the contentlet is enterprise, we need to add the form option + if (changes.isEnterprise?.currentValue) { + this.items.update((items) => [...items, ...this.#enterpriseItems]); + } + this.hideMenus(); // We need to hide the menu if the contentlet changes this.setVtlFiles(); // Set the VTL files for the component @@ -136,7 +146,7 @@ export class EmaContentletToolsComponent implements OnChanges { * @memberof EmaContentletToolsComponent */ setPositionFlag(position: 'before' | 'after'): void { - this.buttonPosition = position; + this.#buttonPosition = position; } /** diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html index 9e1afac5deaa..a23c3ffc1e8f 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html @@ -64,6 +64,7 @@ (addContent)="dialog.addContentlet($event)" [hide]="es.state === editorState.DRAGGING" [contentletArea]="es.contentletArea" + [isEnterprise]="es.isEnterpriseLicense" data-testId="contentlet-tools" /> - @if ( - es.editorData.canEditVariant && - (es.editorData.mode === editorMode.EDIT || es.editorData.mode === editorMode.EDIT_VARIANT) - ) { - - + @if ( es.editorData.canEditVariant && (es.editorData.mode === editorMode.EDIT || + es.editorData.mode === editorMode.EDIT_VARIANT) ) { + + } diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts index c3ba14117177..3553b883b3f2 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts @@ -94,7 +94,8 @@ import { TREE_NODE_MOCK, URL_CONTENT_MAP_MOCK, newContentlet, - dotPageContainerStructureMock + dotPageContainerStructureMock, + SHOW_CONTENTLET_TOOLS_PATCH_MOCK } from '../shared/consts'; import { EDITOR_MODE, EDITOR_STATE, NG_CUSTOM_EVENTS } from '../shared/enums'; import { ActionPayload, ContentTypeDragPayload } from '../shared/models'; @@ -524,6 +525,7 @@ const createRouting = (permissions: { canEdit: boolean; canRead: boolean }) => } ] }); + describe('EditEmaEditorComponent', () => { describe('with queryParams and permission', () => { let spectator: SpectatorRouting; @@ -3187,5 +3189,30 @@ describe('EditEmaEditorComponent', () => { }); }); }); + + describe('Components Inputs', () => { + it('should set right inputs for the dot-ema-contentlet-tools tag', () => { + store.load({ + url: 'index', + language_id: '5', + 'com.dotmarketing.persona.id': DEFAULT_PERSONA.identifier, + variantName: 'hello-there', + experimentId: 'i-have-a-running-experiment' + }); + + spectator.detectChanges(); + + store.patchState(SHOW_CONTENTLET_TOOLS_PATCH_MOCK); + + spectator.detectChanges(); + const contentletTool = spectator.query(EmaContentletToolsComponent); + + expect(contentletTool).not.toBeNull(); + expect(contentletTool.contentletArea).toEqual( + SHOW_CONTENTLET_TOOLS_PATCH_MOCK.contentletArea + ); + expect(contentletTool.isEnterprise).toBeTruthy(); + }); + }); }); }); diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts index 3abfa32316fa..7635273bd463 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts @@ -14,6 +14,7 @@ import { dotcmsContentletMock } from '@dotcms/utils-testing'; +import { EDITOR_MODE, EDITOR_STATE } from './enums'; import { ActionPayload } from './models'; import { DotPageApiResponse } from '../services/dot-page-api.service'; @@ -382,3 +383,41 @@ export const URL_CONTENT_MAP_MOCK = { inode: '1234', title: 'hello world' }; + +export const SHOW_CONTENTLET_TOOLS_PATCH_MOCK = { + editorState: EDITOR_STATE.IDLE, + editorData: { + mode: EDITOR_MODE.EDIT, + canEditVariant: true, + device: null, + page: { + lockedByUser: '', + canLock: true, + isLocked: false + } + }, + contentletArea: { + x: 0, + y: 0, + width: 100, + height: 100, + payload: { + language_id: '', + pageContainers: [], + pageId: '', + container: { + acceptTypes: '', + identifier: '', + maxContentlets: 0, + variantId: '', + uuid: '' + }, + contentlet: { + identifier: '123', + inode: '', + title: '', + contentType: '' + } + } + } +};