Skip to content
Draft
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
6 changes: 4 additions & 2 deletions core-web/libs/block-editor/src/lib/block-editor.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import {
DotFieldRequiredDirective,
DotMessagePipe,
DotSpinnerComponent,
DotContentletStatusBadgeComponent
DotContentletStatusBadgeComponent,
DotContentThumbnailComponent
} from '@dotcms/ui';

//Editor
Expand Down Expand Up @@ -78,7 +79,8 @@ const initTranslations = (dotMessageService: DotMessageService) => {
DragHandleDirective,
DotContextMenuComponent,
DotAddButtonComponent,
DotContentletStatusBadgeComponent
DotContentletStatusBadgeComponent,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Angular module conflict

DotContentThumbnailComponent is added to both imports and declarations in BlockEditorModule. Angular requires components to be declared in exactly one module. Since it's imported from @dotcms/ui, it should only be in imports, not declarations. This duplication will cause Angular compiler errors.

DotContentThumbnailComponent
],
declarations: [
EditorDirective,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
<p-card class="flex! flex-row! items-center justify-start">
<ng-template #header>
<div class="box-border flex h-full items-center p-4! pr-0!">
<dot-contentlet-thumbnail
[width]="94"
[height]="94"
[iconSize]="'72px'"
<dot-content-thumbnail
[contentlet]="data"
class="relative block h-[94px] w-[94px]" />
iconSize="72px"
class="relative h-[94px] w-[94px]" />
</div>
</ng-template>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
width: 32px;
}

.icon-wrapper dot-contentlet-thumbnail {
align-items: center;
display: block;
.icon-wrapper dot-content-thumbnail {
position: relative;
width: 42px;
height: 42px;
Expand All @@ -43,7 +41,7 @@
}

.icon-wrapper.page,
.icon-wrapper.page dot-contentlet-thumbnail {
.icon-wrapper.page dot-content-thumbnail {
flex-basis: 64px;
height: 64px;
width: 64px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
<i class="material-symbols-outlined">{{ url }}</i>
} @else {
@if (data?.contentlet) {
<dot-contentlet-thumbnail
[contentlet]="data.contentlet"
[width]="42"
[height]="42"
[iconSize]="'42px'" />
<dot-content-thumbnail [contentlet]="data.contentlet" iconSize="42px" />
} @else {
<img [src]="url" alt="" />
}
Expand Down
5 changes: 3 additions & 2 deletions core-web/libs/block-editor/src/lib/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { TagModule } from 'primeng/tag';

import { DotContentletStatusBadgeComponent } from '@dotcms/ui';
import { DotContentletStatusBadgeComponent, DotContentThumbnailComponent } from '@dotcms/ui';

// Shared
import {
Expand All @@ -26,7 +26,8 @@ import { SuggestionsService } from './services';
ReactiveFormsModule,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Missing module export for DotContentThumbnailComponent

DotContentThumbnailComponent is declared in SharedModule's declarations array but not included in exports, preventing its use in templates of other modules that import SharedModule. This would cause Angular template compilation errors when attempting to use the component.

ButtonModule,
TagModule,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Incorrect Angular module configuration for DotContentThumbnailComponent

DotContentThumbnailComponent is imported directly in SharedModule's imports array. Angular modules should only import other modules, not individual components unless they are standalone. Since DotContentThumbnailComponent is not marked as standalone (based on its declaration in DotContentThumbnailModule), this configuration will cause Angular template errors. The correct approach is to import DotContentThumbnailModule which exports the component.

DotContentletStatusBadgeComponent
DotContentletStatusBadgeComponent,
DotContentThumbnailComponent

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Angular module configuration error

DotContentThumbnailComponent is declared in SharedModule's declarations array (core-web/libs/block-editor/src/lib/shared/shared.module.ts:30) despite being a standalone component. Standalone components should not be declared in NgModules - this will cause Angular template errors. The component should be removed from declarations array and instead imported directly in consuming components' imports.

],
declarations: [
SuggestionsComponent,
Expand Down
28 changes: 28 additions & 0 deletions core-web/libs/dotcms-webcomponents/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,13 @@ export namespace Components {
*/
"size": string;
}
/**
* @deprecated Angular code must use `DotContentThumbnailComponent`
* (`dot-content-thumbnail`) from `@dotcms/ui` instead — pass the contentlet
* via its `contentlet`/`options` inputs or a resolved model via `thumbnail`.
* This web component remains only for Stencil-internal use
* (`dot-card-contentlet`) and legacy JSP pages. See dotCMS/core#36295.
*/
interface DotContentletThumbnail {
/**
* @default ''
Expand Down Expand Up @@ -1840,6 +1847,13 @@ declare global {
prototype: HTMLDotContentletLockIconElement;
new (): HTMLDotContentletLockIconElement;
};
/**
* @deprecated Angular code must use `DotContentThumbnailComponent`
* (`dot-content-thumbnail`) from `@dotcms/ui` instead — pass the contentlet
* via its `contentlet`/`options` inputs or a resolved model via `thumbnail`.
* This web component remains only for Stencil-internal use
* (`dot-card-contentlet`) and legacy JSP pages. See dotCMS/core#36295.
*/
interface HTMLDotContentletThumbnailElement extends Components.DotContentletThumbnail, HTMLStencilElement {
}
var HTMLDotContentletThumbnailElement: {
Expand Down Expand Up @@ -2751,6 +2765,13 @@ declare namespace LocalJSX {
*/
"size"?: string;
}
/**
* @deprecated Angular code must use `DotContentThumbnailComponent`
* (`dot-content-thumbnail`) from `@dotcms/ui` instead — pass the contentlet
* via its `contentlet`/`options` inputs or a resolved model via `thumbnail`.
* This web component remains only for Stencil-internal use
* (`dot-card-contentlet`) and legacy JSP pages. See dotCMS/core#36295.
*/
interface DotContentletThumbnail {
/**
* @default ''
Expand Down Expand Up @@ -3891,6 +3912,13 @@ declare module "@stencil/core" {
*/
"dot-contentlet-icon": LocalJSX.DotContentletIcon & JSXBase.HTMLAttributes<HTMLDotContentletIconElement>;
"dot-contentlet-lock-icon": LocalJSX.DotContentletLockIcon & JSXBase.HTMLAttributes<HTMLDotContentletLockIconElement>;
/**
* @deprecated Angular code must use `DotContentThumbnailComponent`
* (`dot-content-thumbnail`) from `@dotcms/ui` instead — pass the contentlet
* via its `contentlet`/`options` inputs or a resolved model via `thumbnail`.
* This web component remains only for Stencil-internal use
* (`dot-card-contentlet`) and legacy JSP pages. See dotCMS/core#36295.
*/
"dot-contentlet-thumbnail": LocalJSX.DotContentletThumbnail & JSXBase.HTMLAttributes<HTMLDotContentletThumbnailElement>;
"dot-context-menu": LocalJSX.DotContextMenu & JSXBase.HTMLAttributes<HTMLDotContextMenuElement>;
"dot-data-view-button": LocalJSX.DotDataViewButton & JSXBase.HTMLAttributes<HTMLDotDataViewButtonElement>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { Component, h, Host, Prop, State } from '@stencil/core';

import { DotContentletItem } from '../../models/dot-contentlet-item.model';

/**
* @deprecated Angular code must use `DotContentThumbnailComponent`
* (`dot-content-thumbnail`) from `@dotcms/ui` instead — pass the contentlet
* via its `contentlet`/`options` inputs or a resolved model via `thumbnail`.
* This web component remains only for Stencil-internal use
* (`dot-card-contentlet`) and legacy JSP pages. See dotCMS/core#36295.
*/
@Component({
tag: 'dot-contentlet-thumbnail',
styleUrl: 'dot-contentlet-thumbnail.scss'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
<!-- Auto Generated Below -->


> **[DEPRECATED]** Angular code must use `DotContentThumbnailComponent`
(`dot-content-thumbnail`) from `@dotcms/ui` instead — pass the contentlet
via its `contentlet`/`options` inputs or a resolved model via `thumbnail`.
This web component remains only for Stencil-internal use
(`dot-card-contentlet`) and legacy JSP pages. See dotCMS/core#36295.

## Properties

| Property | Attribute | Description | Type | Default |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,10 @@
</div>
} @else {
<div class="dot-file-field-preview__thumbnail">
@if (fileInfo.source === 'temp') {
<dot-temp-file-thumbnail
[tempFile]="fileInfo.file"
[iconSize]="'48px'"
data-testId="temp-file-thumbnail" />
} @else {
<dot-contentlet-thumbnail
[fieldVariable]="fileInfo.fieldVariable"
[iconSize]="'48px'"
[contentlet]="fileInfo.file"
[playableVideo]="true"
data-testId="contentlet-thumbnail" />
}
<dot-content-thumbnail
[thumbnail]="fileInfo.thumbnail"
iconSize="48px"
data-testId="contentlet-thumbnail" />
</div>
<div class="dot-file-field-preview__metadata">
@if (metadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
overflow: hidden;
user-select: none;
cursor: pointer;
// Narrow layout (container height: auto): cap the card like the media
// thumbnails instead of growing with the whole text content. Ignored on
// the wide layout, where both axes are definite.
aspect-ratio: 16 / 9;
}

.dot-file-field-preview__code-content {
Expand All @@ -56,6 +60,10 @@
justify-content: center;
overflow: hidden;
background-color: #e5e7eb;
// Narrow layout (container height: auto): keep every card the same height
// regardless of the media's natural ratio. Ignored on the wide layout,
// where both width and height are definite.
aspect-ratio: 16 / 9;

@container preview (min-width: 500px) {
max-width: 17.5rem;
Expand Down Expand Up @@ -196,17 +204,6 @@
text-decoration: none;
}

// Third-party web-component internals – cannot be styled with Tailwind
:host {
dot-contentlet-thumbnail::ng-deep {
.thumbnail img,
.background-image:not(.svg-thumbnail) img {
object-fit: cover;
}
}
}

// Third-party web-component internals – cannot be styled with Tailwind
.preview-container--fade .preview-code_container::after {
content: "";
background: linear-gradient(0deg, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ describe('DotFileFieldPreviewComponent', () => {
expect(spectator.query(byTestId('download-btn'))).toBeTruthy();
});

it('should render the unified thumbnail with the temp file pdf src', () => {
spectator.detectChanges();

expect(spectator.query(byTestId('contentlet-thumbnail'))).toBeTruthy();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Test uses legacy test ID after component migration

The test queries for '[data-testId="contentlet-thumbnail"]' but the new component uses 'dot-content-thumbnail' selector. This would cause test failures as the element cannot be found after migration to the new component.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Incorrect test ID for content thumbnail component in test

The test uses '[data-testId="contentlet-thumbnail"]' but the new component's template uses 'dot-content-thumbnail' as the test ID. This mismatch causes the test to fail when it should pass, or pass when it should fail, leading to unreliable test results. The correct test ID should match the component's actual data-testId attribute.

expect(
spectator.query(byTestId('dot-content-thumbnail-image')).getAttribute('src')
).toBe(TEMP_FILE_MOCK.thumbnailUrl);
});

it('should call downloadAsset when click on the download btn', () => {
const downloadSpy = jest
.spyOn(spectator.component, 'downloadAsset')
Expand All @@ -80,6 +89,17 @@ describe('DotFileFieldPreviewComponent', () => {
expect(downloadSpy).toHaveBeenCalledWith(expectedUrl);
});

it('should render with fallback metadata when the temp file has none (image editor save)', () => {
spectator.setInput('previewFile', {
source: 'temp',
file: { ...TEMP_FILE_MOCK, metadata: undefined }
} as unknown);
spectator.detectChanges();

expect(spectator.component).toBeTruthy();
expect(spectator.query(byTestId('metadata-title'))).toHaveText(TEMP_FILE_MOCK.fileName);
});

it('should not show download button when referenceUrl is missing', () => {
spectator.setInput('previewFile', {
source: 'temp',
Expand Down Expand Up @@ -109,9 +129,15 @@ describe('DotFileFieldPreviewComponent', () => {
expect(spectator.component).toBeTruthy();
});

it('should be have a dot-contentlet-thumbnail', () => {
it('should render the unified thumbnail with the contentlet image src', () => {
spectator.detectChanges();
expect(spectator.query('dot-contentlet-thumbnail')).toBeTruthy();

const { inode, modDate } = NEW_FILE_MOCK.entity;

expect(spectator.query(byTestId('contentlet-thumbnail'))).toBeTruthy();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Test ID mismatch in contentlet thumbnail verification

The test checks for contentlet-thumbnail test ID but the new component uses dot-content-thumbnail without this ID. Test line 150 uses deprecated selector, causing false negatives.

expect(
spectator.query(byTestId('dot-content-thumbnail-image')).getAttribute('src')
).toBe(`/dA/${inode}/asset/500w/50q?r=${modDate}`);
});

it('should show proper metadata', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { of } from 'rxjs';

import {
CUSTOM_ELEMENTS_SCHEMA,
ChangeDetectionStrategy,
Component,
computed,
Expand All @@ -27,10 +26,14 @@ import {
DotFileMetadata
} from '@dotcms/dotcms-models';
import {
DotTempFileThumbnailComponent,
DotContentThumbnail,
DotContentThumbnailComponent,
DotFileSizeFormatPipe,
DotMessagePipe,
DotCopyButtonComponent
DotCopyButtonComponent,
buildFallbackTempFileMetadata,
contentletToThumbnailModel,
tempFileToThumbnailModel
} from '@dotcms/ui';
import { getFileMetadata } from '@dotcms/utils';

Expand All @@ -47,6 +50,8 @@ type FileInfo = UploadedFile & {
downloadLink: string | null;
content: string | null;
metadata: DotFileMetadata;
/** Resolved model for the thumbnail viewer, built from either origin. */
thumbnail: DotContentThumbnail;
};

const buildTempDownloadLink = (referenceUrl: string): string => {
Expand All @@ -58,7 +63,7 @@ const buildTempDownloadLink = (referenceUrl: string): string => {
@Component({
selector: 'dot-file-field-preview',
imports: [
DotTempFileThumbnailComponent,
DotContentThumbnailComponent,
DotFileSizeFormatPipe,
DotMessagePipe,
ButtonModule,
Expand All @@ -70,8 +75,7 @@ const buildTempDownloadLink = (referenceUrl: string): string => {
providers: [ConfirmationService],
templateUrl: './dot-file-field-preview.component.html',
styleUrls: ['./dot-file-field-preview.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
schemas: [CUSTOM_ELEMENTS_SCHEMA]
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DotFileFieldPreviewComponent implements OnInit {
readonly #dotResourceLinksService = inject(DotResourceLinksService);
Expand Down Expand Up @@ -149,7 +153,8 @@ export class DotFileFieldPreviewComponent implements OnInit {
contentType,
fieldVariable,
downloadLink: `/contentAsset/raw-data/${file.inode}/${fieldVariable}?byInode=true&force_download=true`,
metadata: getFileMetadata(file)
metadata: getFileMetadata(file),
thumbnail: contentletToThumbnailModel(file, { fieldVariable, playableVideo: true })
};
}

Expand All @@ -162,7 +167,9 @@ export class DotFileFieldPreviewComponent implements OnInit {
contentType: DEFAULT_CONTENT_TYPE,
fieldVariable: DEFAULT_CONTENT_TYPE,
downloadLink: file.referenceUrl ? buildTempDownloadLink(file.referenceUrl) : null,
metadata: file.metadata
// Temp files fresh from the image editor may lack the metadata block
metadata: file.metadata ?? buildFallbackTempFileMetadata(file),
thumbnail: tempFileToThumbnailModel(file)
};
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,9 @@
@if (showThumbnail) {
<div
class="h-14 w-20 shrink-0 overflow-hidden rounded-md bg-gray-100">
<dot-contentlet-thumbnail
[iconSize]="'24px'"
<dot-content-thumbnail
[contentlet]="item"
[playableVideo]="false"
iconSize="24px"
data-testId="contentlet-thumbnail" />
</div>
}
Expand Down Expand Up @@ -159,11 +158,12 @@
@case ('image') {
<td class="w-24 max-w-24 min-w-24" [class.cursor-not-allowed]="isDisabled">
<div class="flex items-center justify-center">
<dot-contentlet-thumbnail
[iconSize]="'30px'"
[contentlet]="item"
[playableVideo]="false"
data-testId="contentlet-thumbnail" />
<div class="h-14 w-20 shrink-0 overflow-hidden rounded-md">
<dot-content-thumbnail
[contentlet]="item"
iconSize="30px"
data-testId="contentlet-thumbnail" />
</div>
</div>
</td>
}
Expand Down
Loading
Loading