Skip to content

Commit 4389930

Browse files
authored
Merge branch 'main' into 33073-rxjs-from-v6-to-v7
2 parents d17382f + a3038b9 commit 4389930

74 files changed

Lines changed: 3763 additions & 1327 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core-web/apps/dotcms-ui/src/app/app.routes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,19 @@ const PORTLETS_ANGULAR: Route[] = [
157157
},
158158
{
159159
path: 'tags',
160+
canActivate: [MenuGuardService],
161+
canActivateChild: [MenuGuardService],
160162
data: { reuseRoute: false },
161163
loadChildren: () => import('@dotcms/portlets/dot-tags/portlet').then((m) => m.dotTagsRoutes)
162164
},
165+
{
166+
path: 'plugins',
167+
canActivate: [MenuGuardService],
168+
canActivateChild: [MenuGuardService],
169+
data: { reuseRoute: false },
170+
loadChildren: () =>
171+
import('@dotcms/portlets/dot-plugins/portlet').then((m) => m.dotPluginsRoutes)
172+
},
163173
{
164174
path: '',
165175
canActivate: [MenuGuardService],

core-web/apps/dotcms-ui/src/app/portlets/dot-apps/components/dot-apps-configuration-detail/components/dot-apps-configuration-detail-form/dot-apps-configuration-detail-form.component.html

Lines changed: 85 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,75 +16,96 @@
1616
</ng-template>
1717

1818
@for (field of $formFields(); track field) {
19-
<div [attr.data-testid]="field.name" class="field">
20-
@switch (field.type) {
21-
@case ('BUTTON') {
22-
<ng-container *ngTemplateOutlet="labelField; context: { field: field }" />
23-
<div>
24-
<button
25-
(click)="onIntegrate(field.value)"
19+
@if (field.type === 'HEADING') {
20+
<div
21+
class="dot-apps-configuration-detail__section-header"
22+
[attr.data-testid]="field.name">
23+
<h3>{{ field.label }}</h3>
24+
</div>
25+
} @else if (field.type === 'INFO') {
26+
<div
27+
class="dot-apps-configuration-detail__info-box"
28+
[attr.data-testid]="field.name">
29+
<markdown>{{ field.hint }}</markdown>
30+
</div>
31+
} @else {
32+
<div [attr.data-testid]="field.name" class="field">
33+
@switch (field.type) {
34+
@case ('BUTTON') {
35+
<ng-container
36+
*ngTemplateOutlet="labelField; context: { field: field }" />
37+
<div>
38+
<button
39+
(click)="onIntegrate(field.value)"
40+
[id]="field.name"
41+
[label]="field.label"
42+
[disabled]="!$appConfigured()"
43+
pButton
44+
type="button"></button>
45+
<ng-container
46+
*ngTemplateOutlet="warningIcon; context: { field: field }" />
47+
</div>
48+
<span class="form__group-hint">
49+
<markdown>{{ field.hint }}</markdown>
50+
</span>
51+
}
52+
@case ('STRING') {
53+
<ng-container
54+
*ngTemplateOutlet="labelField; context: { field: field }" />
55+
<ng-container
56+
*ngTemplateOutlet="warningIcon; context: { field: field }" />
57+
<textarea
58+
(click)="field.hidden ? $event.target.select() : null"
2659
[id]="field.name"
27-
[label]="field.label"
28-
[disabled]="!$appConfigured()"
29-
pButton
30-
type="button"></button>
60+
[formControlName]="field.name"
61+
#inputTextarea
62+
pTextarea
63+
[autoResize]="true"></textarea>
64+
<span class="p-field-hint">
65+
<markdown>{{ field.hint }}</markdown>
66+
</span>
67+
}
68+
@case ('GENERATED_STRING') {
69+
<ng-container
70+
*ngTemplateOutlet="labelField; context: { field: field }" />
71+
<dot-apps-configuration-detail-generated-string-field
72+
data-testid="generated-string-field"
73+
[formControlName]="field.name"
74+
[field]="field" />
75+
}
76+
@case ('BOOL') {
77+
<div class="flex items-center gap-2">
78+
<p-checkbox
79+
[class.required]="field.required"
80+
[inputId]="field.name"
81+
[formControlName]="field.name"
82+
[value]="field.value"
83+
[binary]="true" />
84+
<label [for]="field.name">{{ field.label }}</label>
85+
</div>
3186
<ng-container
3287
*ngTemplateOutlet="warningIcon; context: { field: field }" />
33-
</div>
34-
<span class="form__group-hint">
35-
<markdown>{{ field.hint }}</markdown>
36-
</span>
37-
}
38-
@case ('STRING') {
39-
<ng-container *ngTemplateOutlet="labelField; context: { field: field }" />
40-
<ng-container *ngTemplateOutlet="warningIcon; context: { field: field }" />
41-
<textarea
42-
(click)="field.hidden ? $event.target.select() : null"
43-
[id]="field.name"
44-
[formControlName]="field.name"
45-
#inputTextarea
46-
pTextarea
47-
[autoResize]="true"></textarea>
48-
<span class="p-field-hint">
49-
<markdown>{{ field.hint }}</markdown>
50-
</span>
51-
}
52-
@case ('GENERATED_STRING') {
53-
<ng-container *ngTemplateOutlet="labelField; context: { field: field }" />
54-
<dot-apps-configuration-detail-generated-string-field
55-
data-testid="generated-string-field"
56-
[formControlName]="field.name"
57-
[field]="field" />
58-
}
59-
@case ('BOOL') {
60-
<div class="flex items-center">
61-
<p-checkbox
62-
[class.required]="field.required"
63-
[inputId]="field.name"
88+
<span class="p-field-hint">
89+
<markdown>{{ field.hint }}</markdown>
90+
</span>
91+
}
92+
@case ('SELECT') {
93+
<ng-container
94+
*ngTemplateOutlet="labelField; context: { field: field }" />
95+
<ng-container
96+
*ngTemplateOutlet="warningIcon; context: { field: field }" />
97+
<p-select
98+
[id]="field.name"
6499
[formControlName]="field.name"
65-
[value]="field.value"
66-
[binary]="true" />
67-
<label [for]="field.name">{{ field.label }}</label>
68-
</div>
69-
<ng-container *ngTemplateOutlet="warningIcon; context: { field: field }" />
70-
<span class="p-field-hint">
71-
<markdown>{{ field.hint }}</markdown>
72-
</span>
73-
}
74-
@case ('SELECT') {
75-
<ng-container *ngTemplateOutlet="labelField; context: { field: field }" />
76-
<ng-container *ngTemplateOutlet="warningIcon; context: { field: field }" />
77-
<p-select
78-
[id]="field.name"
79-
[formControlName]="field.name"
80-
[class.required]="field.required"
81-
[options]="field.options" />
82-
<span class="p-field-hint">
83-
<markdown>{{ field.hint }}</markdown>
84-
</span>
100+
[class.required]="field.required"
101+
[options]="field.options" />
102+
<span class="p-field-hint">
103+
<markdown>{{ field.hint }}</markdown>
104+
</span>
105+
}
85106
}
86-
}
87-
</div>
107+
</div>
108+
}
88109
}
89110
</form>
90111
}

core-web/apps/dotcms-ui/src/app/portlets/dot-apps/components/dot-apps-configuration-detail/components/dot-apps-configuration-detail-form/dot-apps-configuration-detail-form.component.spec.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,34 @@ import { DotAppsConfigurationDetailFormComponent } from './dot-apps-configuratio
1818

1919
import { DotAppsConfigurationDetailGeneratedStringFieldComponent } from '../dot-apps-configuration-detail-generated-string-field/dot-apps-configuration-detail-generated-string-field.component';
2020

21+
const headingSecret = {
22+
dynamic: false,
23+
name: 'sectionHeader',
24+
hidden: false,
25+
hint: '',
26+
label: 'Server-Rendered Pages Configuration',
27+
required: false,
28+
type: 'HEADING',
29+
value: '',
30+
hasEnvVar: false,
31+
envShow: false,
32+
hasEnvVarValue: false
33+
};
34+
35+
const infoSecret = {
36+
dynamic: false,
37+
name: 'infoBox',
38+
hidden: false,
39+
hint: 'These settings apply ONLY to pages rendered by dotCMS servers.',
40+
label: '',
41+
required: false,
42+
type: 'INFO',
43+
value: '',
44+
hasEnvVar: false,
45+
envShow: false,
46+
hasEnvVarValue: false
47+
};
48+
2149
const secrets = [
2250
{
2351
dynamic: false,
@@ -315,6 +343,42 @@ describe('DotAppsConfigurationDetailFormComponent', () => {
315343
expect(spyValidOutput).toHaveBeenCalledTimes(3);
316344
});
317345

346+
it('should render HEADING field as section header with label text', () => {
347+
const spectatorWithHeading = createComponent({
348+
props: { formFields: [headingSecret, ...secrets] } as unknown
349+
});
350+
spectatorWithHeading.detectChanges();
351+
352+
const header = spectatorWithHeading.query('[data-testid="sectionHeader"]');
353+
expect(header).toBeTruthy();
354+
expect(header.classList).toContain('dot-apps-configuration-detail__section-header');
355+
expect(header.querySelector('h3').textContent.trim()).toBe(headingSecret.label);
356+
});
357+
358+
it('should render INFO field as info box with hint text', () => {
359+
const spectatorWithInfo = createComponent({
360+
props: { formFields: [infoSecret, ...secrets] } as unknown
361+
});
362+
spectatorWithInfo.detectChanges();
363+
364+
const infoBox = spectatorWithInfo.query('[data-testid="infoBox"]');
365+
expect(infoBox).toBeTruthy();
366+
expect(infoBox.classList).toContain('dot-apps-configuration-detail__info-box');
367+
expect(infoBox.querySelector('markdown')).toBeTruthy();
368+
});
369+
370+
it('should not add HEADING or INFO fields to the form group', () => {
371+
const spectatorWithExtra = createComponent({
372+
props: {
373+
formFields: [headingSecret, infoSecret, ...secrets]
374+
} as unknown
375+
});
376+
spectatorWithExtra.detectChanges();
377+
378+
expect(spectatorWithExtra.component.myFormGroup.contains('sectionHeader')).toBe(false);
379+
expect(spectatorWithExtra.component.myFormGroup.contains('infoBox')).toBe(false);
380+
});
381+
318382
it('should emit form state disabled when required field empty', () => {
319383
const spyValidOutput = jest.spyOn(spectator.component.valid, 'emit');
320384

core-web/apps/dotcms-ui/src/app/portlets/dot-templates/dot-template-create-edit/dot-template-create-edit.component.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ import {
3131
DotWorkflowActionsFireService,
3232
PaginatorService
3333
} from '@dotcms/data-access';
34-
import { SiteService } from '@dotcms/dotcms-js';
34+
import { DotcmsEventsService, SiteService } from '@dotcms/dotcms-js';
3535
import { DotSystemConfig } from '@dotcms/dotcms-models';
3636
import { DotFormDialogComponent, DotMessagePipe, DotApiLinkComponent } from '@dotcms/ui';
3737
import {
3838
DotCurrentUserServiceMock,
39+
DotcmsEventsServiceMock,
3940
MockDotMessageService,
4041
MockDotRouterService,
4142
mockDotThemes,
@@ -299,6 +300,10 @@ describe('DotTemplateCreateEditComponent', () => {
299300
get: jest.fn().mockReturnValue(of(mockDotThemes[1]))
300301
}
301302
},
303+
{
304+
provide: DotcmsEventsService,
305+
useValue: new DotcmsEventsServiceMock()
306+
},
302307
{ provide: DotSystemConfigService, useClass: MockDotSystemConfigService },
303308
{ provide: DotRouterService, useClass: MockDotRouterService }
304309
]

core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/layout/content-types-layout.component.html

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,13 @@
1111
{{ 'contenttypes.tab.style.editor.header' | dm }}
1212
</p-tab>
1313
}
14-
@if ($contentType()) {
15-
<p-tab [value]="2">
16-
{{ 'contenttypes.tab.relationship.header' | dm }}
17-
</p-tab>
18-
}
1914
@if ($contentType() && showPermissionsTab | async) {
20-
<p-tab [value]="3">
15+
<p-tab [value]="2">
2116
{{ 'contenttypes.tab.permissions.header' | dm }}
2217
</p-tab>
2318
}
2419
@if ($contentType()) {
25-
<p-tab [value]="$contentType() && (showPermissionsTab | async) ? 4 : 3">
20+
<p-tab [value]="$contentType() && (showPermissionsTab | async) ? 3 : 2">
2621
{{ 'contenttypes.tab.publisher.push.history.header' | dm }}
2722
</p-tab>
2823
}
@@ -70,17 +65,8 @@
7065
<dot-style-editor-builder [contentType]="$contentType()" />
7166
</p-tabpanel>
7267
}
73-
@if ($contentType()) {
74-
<p-tabpanel [value]="2" class="h-full">
75-
<div class="h-full">
76-
<dot-portlet-box class="h-full">
77-
<dot-iframe class="h-full" [src]="relationshipURL" />
78-
</dot-portlet-box>
79-
</div>
80-
</p-tabpanel>
81-
}
8268
@if ($contentType() && showPermissionsTab | async) {
83-
<p-tabpanel [value]="3" class="h-full">
69+
<p-tabpanel [value]="2" class="h-full">
8470
<div class="h-full">
8571
<dot-portlet-box class="h-full">
8672
<dot-iframe class="h-full" [src]="permissionURL" />
@@ -90,7 +76,7 @@
9076
}
9177
@if ($contentType()) {
9278
<p-tabpanel
93-
[value]="$contentType() && (showPermissionsTab | async) ? 4 : 3"
79+
[value]="$contentType() && (showPermissionsTab | async) ? 3 : 2"
9480
class="h-full">
9581
<div class="h-full">
9682
<dot-portlet-box class="h-full">

0 commit comments

Comments
 (0)