Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import {
ChangeDetectionStrategy,
Component,
effect,
inject,
input,
model,
effect,
output
} from '@angular/core';

import { ButtonModule } from 'primeng/button';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { DialogService } from 'primeng/dynamicdialog';
import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog';
import { MessagesModule } from 'primeng/messages';
import { ToastModule } from 'primeng/toast';

Expand Down Expand Up @@ -84,6 +84,7 @@ import { DotEditContentSidebarComponent } from '../dot-edit-content-sidebar/dot-
ButtonModule,
ToastModule,
MessagesModule,
DynamicDialogModule,
DotEditContentFormComponent,
DotEditContentSidebarComponent,
ConfirmDialogModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@let src = iframeSrc();

@if (src) {
<iframe
[src]="src"
class="block w-full border-none"
[style.min-height]="'60vh'"
title="Content permissions"
data-testId="permissions-iframe"></iframe>
} @else {
<p class="p-3 m-0 text-500" data-testId="permissions-empty">
No content selected. Permissions require a content item.
</p>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { byTestId, createComponentFactory, Spectator } from '@ngneat/spectator/jest';

import { DynamicDialogConfig } from 'primeng/dynamicdialog';

import {
DotPermissionsDialogComponent,
PERMISSIONS_IFRAME_PATH,
PermissionsDialogData
} from './permissions-dialog.component';

describe('DotPermissionsDialogComponent', () => {
let spectator: Spectator<DotPermissionsDialogComponent>;

const defaultData: PermissionsDialogData = {
identifier: 'contentlet-123',
languageId: 1
};

const configRef: { data: PermissionsDialogData | null | undefined } = { data: defaultData };

const createComponent = createComponentFactory({
component: DotPermissionsDialogComponent,
providers: [
{
provide: DynamicDialogConfig,
useValue: configRef
}
]
});

beforeEach(() => {
configRef.data = defaultData;
spectator = createComponent();
});

it('should create', () => {
expect(spectator.component).toBeTruthy();
});

describe('Elements by data-testId - Success', () => {
it('should render permissions-iframe when identifier and languageId are valid', () => {
expect(spectator.query(byTestId('permissions-iframe'))).toBeTruthy();
});

it('should NOT render permissions-empty when data is valid', () => {
expect(spectator.query(byTestId('permissions-empty'))).toBeFalsy();
});

it('should set iframe src to permissions JSP with query params when data is valid', () => {
const iframe = spectator.query(byTestId('permissions-iframe')) as HTMLIFrameElement;
expect(iframe).toBeTruthy();
expect(iframe.src).toContain(PERMISSIONS_IFRAME_PATH);
expect(iframe.src).toContain('contentletId=contentlet-123');
expect(iframe.src).toContain('languageId=1');
expect(iframe.src).toContain('popup=true');
expect(iframe.src).toContain('in_frame=true');
expect(iframe.src).toContain('frame=detailFrame');
expect(iframe.src).toContain('container=true');
expect(iframe.src).toContain('angularCurrentPortlet=edit-content');
});

it('should have iframe with title "Content permissions" and min-height 60vh', () => {
const iframe = spectator.query(byTestId('permissions-iframe')) as HTMLIFrameElement;
expect(iframe?.getAttribute('title')).toBe('Content permissions');
expect(iframe?.style.minHeight).toBe('60vh');
});
});

describe('Elements by data-testId - Failure and Edge Cases', () => {
it('should render permissions-empty when data is undefined', () => {
configRef.data = undefined;
spectator = createComponent();
spectator.detectChanges();

expect(spectator.query(byTestId('permissions-empty'))).toBeTruthy();
expect(spectator.query(byTestId('permissions-empty'))?.textContent?.trim()).toContain(
'No content selected'
);
expect(spectator.query(byTestId('permissions-iframe'))).toBeFalsy();
});

it('should render permissions-empty when data is null', () => {
configRef.data = null;
spectator = createComponent();
spectator.detectChanges();

expect(spectator.query(byTestId('permissions-empty'))).toBeTruthy();
expect(spectator.query(byTestId('permissions-iframe'))).toBeFalsy();
});

it('should render permissions-empty when identifier is empty string', () => {
configRef.data = { identifier: '', languageId: 1 };
spectator = createComponent();
spectator.detectChanges();

expect(spectator.query(byTestId('permissions-empty'))).toBeTruthy();
expect(spectator.query(byTestId('permissions-iframe'))).toBeFalsy();
});

it('should render permissions-empty when languageId is 0', () => {
configRef.data = { identifier: 'id-ok', languageId: 0 };
spectator = createComponent();
spectator.detectChanges();

expect(spectator.query(byTestId('permissions-empty'))).toBeTruthy();
expect(spectator.query(byTestId('permissions-iframe'))).toBeFalsy();
});

it('should render permissions-empty when data is empty object', () => {
configRef.data = {} as PermissionsDialogData;
spectator = createComponent();
spectator.detectChanges();

expect(spectator.query(byTestId('permissions-empty'))).toBeTruthy();
expect(spectator.query(byTestId('permissions-iframe'))).toBeFalsy();
});
});

describe('iframeSrc computed - Edge Cases', () => {
it('should include correct languageId in URL for different values', () => {
configRef.data = { identifier: 'x', languageId: 99 };
spectator = createComponent();
spectator.detectChanges();

const iframe = spectator.query(byTestId('permissions-iframe')) as HTMLIFrameElement;
expect(iframe?.src).toContain('languageId=99');
});

it('should include correct identifier in URL for special characters', () => {
configRef.data = { identifier: 'abc-xyz-789', languageId: 2 };
spectator = createComponent();
spectator.detectChanges();

const iframe = spectator.query(byTestId('permissions-iframe')) as HTMLIFrameElement;
expect(iframe?.src).toContain('contentletId=abc-xyz-789');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';

import { DynamicDialogConfig } from 'primeng/dynamicdialog';

export const PERMISSIONS_IFRAME_PATH = '/html/portlet/ext/contentlet/permissions.jsp';

export interface PermissionsDialogData {
identifier: string;
languageId: number;
}

function buildPermissionsIframeUrl(identifier: string, languageId: number): string {
const params = new URLSearchParams({
contentletId: identifier,
languageId: String(languageId),
popup: 'true',
in_frame: 'true',
frame: 'detailFrame',
container: 'true',
angularCurrentPortlet: 'edit-content'
});
return `${PERMISSIONS_IFRAME_PATH}?${params.toString()}`;
}

/**
* Dialog component that displays contentlet permissions in an iframe.
* Used from the Permissions tab in the edit content sidebar.
*/
@Component({
selector: 'dot-permissions-dialog',
templateUrl: './permissions-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DotPermissionsDialogComponent {
readonly #config = inject(DynamicDialogConfig<PermissionsDialogData>);
readonly #sanitizer = inject(DomSanitizer);

readonly #identifier = this.#config.data?.identifier ?? '';
readonly #languageId = this.#config.data?.languageId ?? 0;

readonly iframeSrc = computed<SafeResourceUrl | null>(() => {
const identifier = this.#identifier;
const languageId = this.#languageId;
if (!identifier || !languageId) return null;
return this.#sanitizer.bypassSecurityTrustResourceUrl(
buildPermissionsIframeUrl(identifier, languageId)
);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<section class="flex justify-content-center p-4" role="region" aria-label="Content Permissions">
<div
class="w-full cursor-pointer"
role="button"
tabindex="0"
(click)="openPermissionsDialog()"
(keydown.enter)="openPermissionsDialog()"
(keydown.space)="$event.preventDefault(); openPermissionsDialog()"
[attr.aria-label]="'edit.content.sidebar.permissions.title' | dm"
data-testId="permissions-card">
<p-card [header]="'edit.content.sidebar.permissions.title' | dm">
<p class="m-0 text-600">
{{ 'edit.content.sidebar.permissions.setup' | dm }}
</p>
</p-card>
</div>
</section>
Loading
Loading