Skip to content

Commit bf13d19

Browse files
authored
fix(binary-field): rollback migration regressions and fix custom element builder styling #33882 (#34897)
- Rolls back dot-edit-content-binary-field lib components to their pre-migration state (SCSS-based styles) due to multiple regressions from the Angular 20→21 / PrimeNG 17→21 migration. Restores all deleted SCSS files and HTML templates. - Fixes the dotcms-binary-field-builder custom element app by adding Tailwind CSS setup, PrimeNG 21 Lara theme provider (provideDotCMSTheme), dialog backdrop styling, and a button icon alignment override for the global .pi rule conflict. https://github.com/user-attachments/assets/7cc2b957-601f-4b89-9d56-3ceebf38fd53 This PR fixes: #33882
1 parent 027b3b8 commit bf13d19

27 files changed

Lines changed: 843 additions & 490 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": {
3+
"@tailwindcss/postcss": {}
4+
}
5+
}

core-web/apps/dotcms-binary-field-builder/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"apps/dotcms-binary-field-builder/src/assets"
2222
],
2323
"styles": [
24+
"apps/dotcms-binary-field-builder/src/style.css",
2425
"node_modules/primeicons/primeicons.css",
2526
"libs/dotcms-scss/angular/dotcms-theme/_misc.scss",
2627
"libs/dotcms-scss/angular/dotcms-theme/components/form/_inputtext.scss",

core-web/apps/dotcms-binary-field-builder/src/app/app.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
88

99
import { DotMessageService, DotUploadService } from '@dotcms/data-access';
1010
import { DotEditContentBinaryFieldComponent } from '@dotcms/edit-content';
11+
import { provideDotCMSTheme } from '@dotcms/ui';
1112

1213
import { AppComponent } from './app.component';
1314

@@ -32,7 +33,7 @@ const CONTENTTYPE_FIELDS: ContenttypeFieldElement[] = [
3233
DotEditContentBinaryFieldComponent,
3334
MonacoEditorModule
3435
],
35-
providers: [DotMessageService, DotUploadService]
36+
providers: [DotMessageService, DotUploadService, provideDotCMSTheme()]
3637
})
3738
export class AppModule implements DoBootstrap {
3839
constructor(private readonly injector: Injector) {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@import 'tailwindcss';
2+
@import 'tailwindcss-primeui';
3+
4+
/* Binary field dialogs use a white transparent backdrop (mirrors pre-migration p-component-overlay rule).
5+
PrimeNG 21 renamed the mask class from p-component-overlay to p-overlay-mask. */
6+
.p-dialog-mask-transparent {
7+
background-color: rgba(255, 255, 255, 0.8);
8+
backdrop-filter: blur(4px);
9+
}
10+
11+
/* The global .pi rule in _misc.scss sets display:flex + width:20px + aspect-ratio:1/1 to create
12+
fixed-size icon boxes. Inside a PrimeNG 21 p-button this breaks the icon/label flex alignment
13+
because the icon span becomes an oversized block, pushing the label out of place.
14+
Reset those properties so the button's own flex layout controls icon sizing. */
15+
.p-button .p-button-icon.pi {
16+
display: inline-flex;
17+
width: auto;
18+
aspect-ratio: unset;
19+
}

core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,43 @@
1-
@let allowFileNameEdit = $allowFileNameEdit();
2-
@let cancel = $cancel;
3-
4-
<form
5-
(ngSubmit)="onSubmit()"
6-
[formGroup]="form"
7-
class="flex h-full w-full flex-col items-start justify-center gap-4">
1+
<form (ngSubmit)="onSubmit()" [formGroup]="form" class="editor-mode__form">
82
@if (allowFileNameEdit) {
9-
<div class="flex w-full flex-col gap-2">
10-
<label class="p-label-input-required" data-testId="editor-label" for="file-name">
11-
{{ 'dot.binary.field.action.create.new.file.label' | dm }}
12-
</label>
13-
<input
14-
id="file-name"
15-
autocomplete="off"
16-
formControlName="name"
17-
pInputText
18-
class="w-full"
19-
placeholder="Ex. template.html"
20-
type="text"
21-
data-testId="editor-file-name" />
22-
<div class="flex min-h-6 items-start">
3+
<div class="editor-mode__input-container">
4+
<div class="editor-mode__input">
5+
<label class="p-label-input-required" data-testId="editor-label" for="file-name">
6+
{{ 'dot.binary.field.action.create.new.file.label' | dm }}
7+
</label>
8+
<input
9+
id="file-name"
10+
autocomplete="off"
11+
formControlName="name"
12+
pInputText
13+
placeholder="Ex. template.html"
14+
type="text"
15+
data-testId="editor-file-name" />
16+
</div>
17+
<div class="error-message">
2318
<dot-field-validation-message
2419
[field]="form.get('name')"
2520
[patternErrorMessage]="'dot.binary.field.error.type.file.not.extension'"
2621
data-testId="error-message" />
2722
</div>
2823
</div>
2924
}
30-
<div class="flex w-full flex-1 flex-col items-start gap-2">
25+
<div class="binary-field__editor-container">
3126
<ngx-monaco-editor
3227
(init)="onEditorInit()"
33-
[class.bg-gray-200]="form.disabled"
34-
[class.opacity-50]="form.disabled"
35-
[class.cursor-not-allowed]="form.disabled"
28+
[ngClass]="{ 'binary-field__code-editor--disabled': form.disabled }"
3629
[options]="monacoOptions()"
37-
class="block w-full min-h-80 flex-1 overflow-auto rounded-md border border-gray-400"
30+
class="binary-field__code-editor"
3831
#editorRef
3932
data-testId="code-editor"
4033
formControlName="content" />
4134

42-
<div
43-
[class.visible]="!!mimeType"
44-
class="invisible flex items-center gap-2 text-sm text-gray-700">
35+
<div [ngClass]="{ 'editor-mode__helper--visible': mimeType }" class="editor-mode__helper">
4536
<i class="pi pi-info-circle"></i>
4637
<small>Mime Type: {{ mimeType }}</small>
4738
</div>
4839
</div>
49-
<div class="flex w-full items-center justify-end gap-2">
40+
<div class="editor-mode__actions">
5041
<p-button
5142
(click)="cancel.emit()"
5243
[label]="'dot.common.cancel' | dm"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
@use "variables" as *;
2+
3+
.binary-field__editor-container {
4+
display: flex;
5+
justify-content: center;
6+
align-items: flex-start;
7+
flex-direction: column;
8+
flex: 1;
9+
width: 100%;
10+
gap: $spacing-1;
11+
}
12+
13+
.binary-field__code-editor {
14+
border: 1px solid $color-palette-gray-400; // Input
15+
display: block;
16+
flex-grow: 1;
17+
width: 100%;
18+
min-height: 20rem;
19+
border-radius: $border-radius-md;
20+
overflow: auto;
21+
}
22+
23+
.binary-field__code-editor--disabled {
24+
background-color: $color-palette-gray-200;
25+
opacity: 0.5;
26+
27+
&::ng-deep {
28+
.monaco-mouse-cursor-text,
29+
.overflow-guard {
30+
cursor: not-allowed;
31+
}
32+
}
33+
}
34+
35+
.editor-mode__form {
36+
height: 100%;
37+
display: flex;
38+
flex-direction: column;
39+
justify-content: center;
40+
align-items: flex-start;
41+
}
42+
43+
.editor-mode__input-container {
44+
width: 100%;
45+
display: flex;
46+
gap: $spacing-1;
47+
flex-direction: column;
48+
}
49+
50+
.editor-mode__input {
51+
width: 100%;
52+
display: flex;
53+
flex-direction: column;
54+
}
55+
56+
.editor-mode__actions {
57+
width: 100%;
58+
display: flex;
59+
gap: $spacing-1;
60+
align-items: center;
61+
justify-content: flex-end;
62+
}
63+
64+
.editor-mode__helper {
65+
display: flex;
66+
justify-content: flex-start;
67+
align-items: center;
68+
gap: $spacing-1;
69+
color: $color-palette-gray-700;
70+
font-weight: $font-size-sm;
71+
visibility: hidden;
72+
}
73+
74+
.editor-mode__helper--visible {
75+
visibility: visible;
76+
}
77+
78+
.error-message {
79+
min-height: $spacing-4; // Fix height to avoid jumping
80+
justify-content: flex-start;
81+
display: flex;
82+
}

core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe('DotBinaryFieldEditorComponent', () => {
9393
detectChanges: false,
9494
props: {
9595
allowFileNameEdit: true
96-
} as unknown
96+
}
9797
});
9898

9999
component = spectator.component;
@@ -108,7 +108,7 @@ describe('DotBinaryFieldEditorComponent', () => {
108108
it('should emit cancel event on escape keydown', () => {
109109
const event = new KeyboardEvent('keydown', { key: 'Escape' });
110110

111-
const cancelSpy = jest.spyOn(spectator.component.$cancel, 'emit');
111+
const cancelSpy = jest.spyOn(spectator.component.cancel, 'emit');
112112
const preventDefaultSpy = jest.spyOn(event, 'preventDefault');
113113
const stopPropagationSpy = jest.spyOn(event, 'stopPropagation');
114114

@@ -205,7 +205,7 @@ describe('DotBinaryFieldEditorComponent', () => {
205205
expect(component.mimeType).toBe('plain/text');
206206
}));
207207
it('should emit cancel event when cancel button is clicked', () => {
208-
const spy = jest.spyOn(component.$cancel, 'emit');
208+
const spy = jest.spyOn(component.cancel, 'emit');
209209
const cancelBtn = spectator.query(byTestId('cancel-button'));
210210

211211
spectator.click(cancelBtn);
@@ -214,7 +214,7 @@ describe('DotBinaryFieldEditorComponent', () => {
214214
});
215215

216216
it('should emit tempFileUploaded event when import button is clicked if form is valid', () => {
217-
const spy = jest.spyOn(component.$tempFileUploaded, 'emit');
217+
const spy = jest.spyOn(component.tempFileUploaded, 'emit');
218218
const spyFormDisabled = jest.spyOn(component.form, 'disable');
219219
const spyFormEnabled = jest.spyOn(component.form, 'enable');
220220
const spyFileUpload = jest
@@ -238,7 +238,7 @@ describe('DotBinaryFieldEditorComponent', () => {
238238
});
239239

240240
it('should not emit tempFileUploaded event when import button is clicked if form is invalid', () => {
241-
const spy = jest.spyOn(component.$tempFileUploaded, 'emit');
241+
const spy = jest.spyOn(component.tempFileUploaded, 'emit');
242242
const spyFormDisabled = jest.spyOn(component.form, 'disable');
243243
const spyFormEnabled = jest.spyOn(component.form, 'enable');
244244
const spyFileUpload = jest

core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.ts

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import {
1111
ChangeDetectorRef,
1212
Component,
1313
computed,
14-
effect,
14+
EventEmitter,
1515
HostListener,
1616
inject,
17-
input,
17+
Input,
18+
OnChanges,
1819
OnInit,
19-
output,
20+
Output,
2021
signal,
2122
ViewChild
2223
} from '@angular/core';
@@ -55,17 +56,16 @@ const DEFAULT_FILE_TYPE = 'text';
5556
DotFieldValidationMessageComponent
5657
],
5758
templateUrl: './dot-binary-field-editor.component.html',
59+
styleUrls: ['./dot-binary-field-editor.component.scss'],
5860
changeDetection: ChangeDetectionStrategy.OnPush
5961
})
60-
export class DotBinaryFieldEditorComponent implements OnInit {
61-
$fileName = input<string>('', { alias: 'fileName' });
62-
$fileContent = input<string>('', { alias: 'fileContent' });
63-
$allowFileNameEdit = input<boolean>(true, { alias: 'allowFileNameEdit' });
64-
$userMonacoOptions = input<MonacoEditorConstructionOptions>({}, { alias: 'userMonacoOptions' });
65-
66-
$tempFileUploaded = output<DotCMSTempFile>();
67-
$cancel = output<void>();
62+
export class DotBinaryFieldEditorComponent implements OnInit, OnChanges {
63+
@Input() fileName = '';
64+
@Input() fileContent = '';
65+
@Input() allowFileNameEdit = true;
6866

67+
@Output() readonly tempFileUploaded = new EventEmitter<DotCMSTempFile>();
68+
@Output() readonly cancel = new EventEmitter<void>();
6969
@ViewChild('editorRef', { static: true }) editorRef!: MonacoEditorComponent;
7070
readonly form = new FormGroup({
7171
name: new FormControl('', [Validators.required]),
@@ -93,6 +93,11 @@ export class DotBinaryFieldEditorComponent implements OnInit {
9393
};
9494
});
9595

96+
@Input()
97+
set userMonacoOptions(customMonacoOptions: MonacoEditorConstructionOptions) {
98+
this._userMonacoOptions.set(customMonacoOptions);
99+
}
100+
96101
get name(): FormControl {
97102
return this.form.get('name') as FormControl;
98103
}
@@ -110,30 +115,13 @@ export class DotBinaryFieldEditorComponent implements OnInit {
110115
*/
111116
@HostListener('document:keydown.escape', ['$event']) onEscape(event) {
112117
// TODO: The 'emit' function requires a mandatory void argument
113-
this.$cancel.emit();
118+
this.cancel.emit();
114119
event.preventDefault();
115120
event.stopPropagation();
116121
}
117122

118-
constructor() {
119-
// Effects must be called in constructor (injection context)
120-
effect(() => {
121-
this._userMonacoOptions.set(this.$userMonacoOptions() ?? {});
122-
});
123-
124-
effect(() => {
125-
this.setFormValues();
126-
const fileName = this.$fileName();
127-
if (window.monaco) {
128-
this.setEditorLanguage(fileName);
129-
}
130-
});
131-
}
132-
133123
ngOnInit(): void {
134-
// Initialize form values with initial input values
135124
this.setFormValues();
136-
137125
this.name.valueChanges
138126
.pipe(debounceTime(350))
139127
.subscribe((name) => this.setEditorLanguage(name));
@@ -143,11 +131,17 @@ export class DotBinaryFieldEditorComponent implements OnInit {
143131
);
144132
}
145133

134+
ngOnChanges(): void {
135+
this.setFormValues();
136+
if (window.monaco) {
137+
this.setEditorLanguage(this.fileName);
138+
}
139+
}
140+
146141
onEditorInit() {
147142
this.editor = this.editorRef.editor;
148-
const fileName = this.$fileName();
149-
if (fileName) {
150-
this.setEditorLanguage(fileName);
143+
if (this.fileName) {
144+
this.setEditorLanguage(this.fileName);
151145
}
152146

153147
window.monaco.languages.register({
@@ -173,8 +167,8 @@ export class DotBinaryFieldEditorComponent implements OnInit {
173167
}
174168

175169
private setFormValues(): void {
176-
this.name.setValue(this.$fileName());
177-
this.content.setValue(this.$fileContent());
170+
this.name.setValue(this.fileName);
171+
this.content.setValue(this.fileContent);
178172
}
179173

180174
private markControlInvalid(control: FormControl): void {
@@ -188,7 +182,7 @@ export class DotBinaryFieldEditorComponent implements OnInit {
188182
this.disableEditor();
189183
obs$.subscribe((tempFile) => {
190184
this.enableEditor();
191-
this.$tempFileUploaded.emit({
185+
this.tempFileUploaded.emit({
192186
...tempFile,
193187
content: this.content.value
194188
});

0 commit comments

Comments
 (0)