@dotcms/angular 0.0.1-alpha.38 → 0.0.1-alpha.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +18 -0
- package/jest.config.ts +22 -0
- package/ng-package.json +7 -0
- package/package.json +4 -21
- package/project.json +33 -0
- package/{index.d.ts → src/index.ts} +0 -1
- package/src/lib/components/dot-editable-text/dot-editable-text.component.css +4 -0
- package/src/lib/components/dot-editable-text/dot-editable-text.component.html +8 -0
- package/src/lib/components/dot-editable-text/dot-editable-text.component.spec.ts +424 -0
- package/src/lib/components/dot-editable-text/dot-editable-text.component.ts +269 -0
- package/src/lib/components/dot-editable-text/utils.ts +51 -0
- package/src/lib/components/no-component/no-component.component.css +3 -0
- package/src/lib/components/no-component/no-component.component.spec.ts +24 -0
- package/src/lib/components/no-component/no-component.component.ts +31 -0
- package/src/lib/layout/column/column.component.css +99 -0
- package/src/lib/layout/column/column.component.spec.ts +33 -0
- package/src/lib/layout/column/column.component.ts +49 -0
- package/src/lib/layout/container/container.component.css +9 -0
- package/src/lib/layout/container/container.component.html +26 -0
- package/src/lib/layout/container/container.component.spec.ts +205 -0
- package/src/lib/layout/container/container.component.ts +140 -0
- package/src/lib/layout/contentlet/contentlet.component.spec.ts +22 -0
- package/{lib/layout/contentlet/contentlet.component.d.ts → src/lib/layout/contentlet/contentlet.component.ts} +32 -17
- package/src/lib/layout/dotcms-layout/dotcms-layout.component.css +3 -0
- package/src/lib/layout/dotcms-layout/dotcms-layout.component.spec.ts +195 -0
- package/src/lib/layout/dotcms-layout/dotcms-layout.component.ts +150 -0
- package/src/lib/layout/row/row.component.css +6 -0
- package/src/lib/layout/row/row.component.spec.ts +28 -0
- package/src/lib/layout/row/row.component.ts +32 -0
- package/{lib/models/dotcms.model.d.ts → src/lib/models/dotcms.model.ts} +21 -3
- package/{lib/models/index.d.ts → src/lib/models/index.ts} +8 -1
- package/{lib/services/dotcms-context/page-context.service.d.ts → src/lib/services/dotcms-context/page-context.service.ts} +41 -12
- package/src/lib/services/dotcms-context/page-context.spec.ts +80 -0
- package/src/lib/utils/index.ts +92 -0
- package/src/lib/utils/testing.utils.ts +1019 -0
- package/src/test-setup.ts +8 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +11 -0
- package/dotcms-angular.d.ts.map +0 -1
- package/esm2022/dotcms-angular.mjs +0 -5
- package/esm2022/index.mjs +0 -5
- package/esm2022/lib/components/dot-editable-text/dot-editable-text.component.mjs +0 -225
- package/esm2022/lib/components/dot-editable-text/utils.mjs +0 -43
- package/esm2022/lib/components/no-component/no-component.component.mjs +0 -32
- package/esm2022/lib/layout/column/column.component.mjs +0 -45
- package/esm2022/lib/layout/container/container.component.mjs +0 -126
- package/esm2022/lib/layout/contentlet/contentlet.component.mjs +0 -120
- package/esm2022/lib/layout/dotcms-layout/dotcms-layout.component.mjs +0 -100
- package/esm2022/lib/layout/row/row.component.mjs +0 -29
- package/esm2022/lib/models/dotcms.model.mjs +0 -3
- package/esm2022/lib/models/index.mjs +0 -3
- package/esm2022/lib/services/dotcms-context/page-context.service.mjs +0 -75
- package/esm2022/lib/utils/index.mjs +0 -79
- package/fesm2022/dotcms-angular.mjs +0 -858
- package/fesm2022/dotcms-angular.mjs.map +0 -1
- package/index.d.ts.map +0 -1
- package/lib/components/dot-editable-text/dot-editable-text.component.d.ts +0 -129
- package/lib/components/dot-editable-text/dot-editable-text.component.d.ts.map +0 -1
- package/lib/components/dot-editable-text/utils.d.ts +0 -7
- package/lib/components/dot-editable-text/utils.d.ts.map +0 -1
- package/lib/components/no-component/no-component.component.d.ts +0 -22
- package/lib/components/no-component/no-component.component.d.ts.map +0 -1
- package/lib/layout/column/column.component.d.ts +0 -29
- package/lib/layout/column/column.component.d.ts.map +0 -1
- package/lib/layout/container/container.component.d.ts +0 -88
- package/lib/layout/container/container.component.d.ts.map +0 -1
- package/lib/layout/contentlet/contentlet.component.d.ts.map +0 -1
- package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts +0 -67
- package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts.map +0 -1
- package/lib/layout/row/row.component.d.ts +0 -20
- package/lib/layout/row/row.component.d.ts.map +0 -1
- package/lib/models/dotcms.model.d.ts.map +0 -1
- package/lib/models/index.d.ts.map +0 -1
- package/lib/services/dotcms-context/page-context.service.d.ts.map +0 -1
- package/lib/utils/index.d.ts +0 -63
- package/lib/utils/index.d.ts.map +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { Spectator, byTestId, byText, createComponentFactory } from '@ngneat/spectator';
|
|
2
|
+
import { of } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
import { Component, Input } from '@angular/core';
|
|
5
|
+
|
|
6
|
+
import { ContainerComponent } from './container.component';
|
|
7
|
+
|
|
8
|
+
import { NoComponent } from '../../components/no-component/no-component.component';
|
|
9
|
+
import { DotCMSContainer, DotCMSContentlet } from '../../models';
|
|
10
|
+
import { PageContextService } from '../../services/dotcms-context/page-context.service';
|
|
11
|
+
import { PageResponseMock } from '../../utils/testing.utils';
|
|
12
|
+
|
|
13
|
+
@Component({
|
|
14
|
+
selector: 'dotcms-mock-component',
|
|
15
|
+
standalone: true,
|
|
16
|
+
template: 'Hello world'
|
|
17
|
+
})
|
|
18
|
+
class DotcmsSDKMockComponent {
|
|
19
|
+
@Input() contentlet!: DotCMSContentlet;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
selector: 'dot-no-component',
|
|
24
|
+
template: 'no component yet - Custom'
|
|
25
|
+
})
|
|
26
|
+
class CustomNoComponent {
|
|
27
|
+
@Input() contentlet!: DotCMSContentlet;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('ContainerComponent', () => {
|
|
31
|
+
let spectator: Spectator<ContainerComponent>;
|
|
32
|
+
|
|
33
|
+
describe('inside editor', () => {
|
|
34
|
+
const createComponent = createComponentFactory({
|
|
35
|
+
component: ContainerComponent,
|
|
36
|
+
detectChanges: false,
|
|
37
|
+
providers: [
|
|
38
|
+
{
|
|
39
|
+
provide: PageContextService,
|
|
40
|
+
useValue: {
|
|
41
|
+
context: {
|
|
42
|
+
pageAsset: {
|
|
43
|
+
containers: PageResponseMock.containers
|
|
44
|
+
},
|
|
45
|
+
components: {
|
|
46
|
+
Banner: of(DotcmsSDKMockComponent)
|
|
47
|
+
},
|
|
48
|
+
isInsideEditor: true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
spectator = createComponent({
|
|
57
|
+
props: {
|
|
58
|
+
container: PageResponseMock.layout.body.rows[0].columns[0]
|
|
59
|
+
.containers[0] as DotCMSContainer
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should render MockContainerComponent', () => {
|
|
65
|
+
spectator.detectChanges();
|
|
66
|
+
expect(spectator.query(DotcmsSDKMockComponent)).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should container have data attributes', () => {
|
|
70
|
+
spectator.detectChanges();
|
|
71
|
+
const container = spectator.debugElement.nativeElement;
|
|
72
|
+
expect(container?.getAttribute('data-dot-accept-types')).toBeDefined();
|
|
73
|
+
expect(container?.getAttribute('data-dot-identifier')).toBeDefined();
|
|
74
|
+
expect(container?.getAttribute('data-max-contentlets')).toBeDefined();
|
|
75
|
+
expect(container?.getAttribute('ata-dot-uuid')).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should contentlets have data attributes', () => {
|
|
79
|
+
spectator.detectChanges();
|
|
80
|
+
const contentlets = spectator.queryAll(byTestId('dot-contentlet'));
|
|
81
|
+
contentlets.forEach((contentlet) => {
|
|
82
|
+
expect(contentlet.getAttribute('data-dot-identifier')).toBeDefined();
|
|
83
|
+
expect(contentlet.getAttribute('data-dot-basetype')).toBeDefined();
|
|
84
|
+
expect(contentlet.getAttribute('data-dot-title')).toBeDefined();
|
|
85
|
+
expect(contentlet.getAttribute('data-dot-inode')).toBeDefined();
|
|
86
|
+
expect(contentlet.getAttribute('data-dot-type')).toBeDefined();
|
|
87
|
+
expect(contentlet.getAttribute('data-dot-container')).toBeDefined();
|
|
88
|
+
expect(contentlet.getAttribute('data-dot-on-number-of-pages')).toBeDefined();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should render NoComponent component for unsetted content types', () => {
|
|
93
|
+
spectator.setInput(
|
|
94
|
+
'container',
|
|
95
|
+
PageResponseMock.layout.body.rows[1].columns[0].containers[0] as DotCMSContainer
|
|
96
|
+
);
|
|
97
|
+
spectator.detectChanges();
|
|
98
|
+
expect(spectator.query(NoComponent)).toBeTruthy();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should render message when container is empty', () => {
|
|
102
|
+
spectator.setInput(
|
|
103
|
+
'container',
|
|
104
|
+
PageResponseMock.layout.body.rows[2].columns[0].containers[0] as DotCMSContainer
|
|
105
|
+
);
|
|
106
|
+
spectator.detectChanges();
|
|
107
|
+
expect(spectator.query(byText('This container is empty.'))).toBeTruthy();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('with custom NoComponent component', () => {
|
|
112
|
+
const createComponent = createComponentFactory({
|
|
113
|
+
component: ContainerComponent,
|
|
114
|
+
detectChanges: false,
|
|
115
|
+
providers: [
|
|
116
|
+
{
|
|
117
|
+
provide: PageContextService,
|
|
118
|
+
useValue: {
|
|
119
|
+
context: {
|
|
120
|
+
pageAsset: {
|
|
121
|
+
containers: PageResponseMock.containers
|
|
122
|
+
},
|
|
123
|
+
components: {
|
|
124
|
+
Banner: of(DotcmsSDKMockComponent)
|
|
125
|
+
},
|
|
126
|
+
isInsideEditor: true
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
spectator = createComponent({
|
|
134
|
+
props: {
|
|
135
|
+
container: PageResponseMock.layout.body.rows[1].columns[0]
|
|
136
|
+
.containers[0] as DotCMSContainer
|
|
137
|
+
},
|
|
138
|
+
providers: [
|
|
139
|
+
{
|
|
140
|
+
provide: PageContextService,
|
|
141
|
+
useValue: {
|
|
142
|
+
context: {
|
|
143
|
+
pageAsset: {
|
|
144
|
+
containers: PageResponseMock.containers
|
|
145
|
+
},
|
|
146
|
+
components: {
|
|
147
|
+
CustomNoComponent: of(CustomNoComponent)
|
|
148
|
+
},
|
|
149
|
+
isInsideEditor: true
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should render custom NoComponent component for unsetted content types', () => {
|
|
158
|
+
spectator.detectChanges();
|
|
159
|
+
expect(spectator.query(CustomNoComponent)).toBeTruthy();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('outside editor', () => {
|
|
164
|
+
const createComponent = createComponentFactory({
|
|
165
|
+
component: ContainerComponent,
|
|
166
|
+
detectChanges: false,
|
|
167
|
+
providers: [
|
|
168
|
+
{
|
|
169
|
+
provide: PageContextService,
|
|
170
|
+
useValue: {
|
|
171
|
+
context: {
|
|
172
|
+
pageAsset: {
|
|
173
|
+
containers: PageResponseMock.containers
|
|
174
|
+
},
|
|
175
|
+
components: {
|
|
176
|
+
Banner: of(DotcmsSDKMockComponent)
|
|
177
|
+
},
|
|
178
|
+
isInsideEditor: false
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
beforeEach(() => {
|
|
186
|
+
spectator = createComponent({
|
|
187
|
+
props: {
|
|
188
|
+
container: PageResponseMock.layout.body.rows[0].columns[0]
|
|
189
|
+
.containers[0] as DotCMSContainer
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should not have data attributes', () => {
|
|
195
|
+
spectator.detectChanges();
|
|
196
|
+
const container = spectator.query(byTestId('dot-container'));
|
|
197
|
+
expect(container?.getAttribute('data-dot-accept-types')).toBeUndefined();
|
|
198
|
+
|
|
199
|
+
const contentlets = spectator.queryAll(byTestId('dot-contentlet'));
|
|
200
|
+
contentlets.forEach((contentlet) => {
|
|
201
|
+
expect(contentlet.getAttribute('data-dot-identifier')).toBeUndefined();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { AsyncPipe, NgComponentOutlet } from '@angular/common';
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
Component,
|
|
5
|
+
HostBinding,
|
|
6
|
+
Input,
|
|
7
|
+
OnChanges,
|
|
8
|
+
computed,
|
|
9
|
+
inject,
|
|
10
|
+
signal
|
|
11
|
+
} from '@angular/core';
|
|
12
|
+
|
|
13
|
+
import { NoComponent } from '../../components/no-component/no-component.component';
|
|
14
|
+
import { DynamicComponentEntity } from '../../models';
|
|
15
|
+
import { DotCMSContainer, DotCMSContentlet } from '../../models/dotcms.model';
|
|
16
|
+
import { PageContextService } from '../../services/dotcms-context/page-context.service';
|
|
17
|
+
import { getContainersData } from '../../utils';
|
|
18
|
+
import { ContentletComponent } from '../contentlet/contentlet.component';
|
|
19
|
+
|
|
20
|
+
interface DotContainer {
|
|
21
|
+
acceptTypes: string;
|
|
22
|
+
identifier: string;
|
|
23
|
+
maxContentlets: number;
|
|
24
|
+
uuid: string;
|
|
25
|
+
variantId?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* This component is responsible to display a container with contentlets.
|
|
30
|
+
*
|
|
31
|
+
* @export
|
|
32
|
+
* @class ContainerComponent
|
|
33
|
+
* @implements {OnChanges}
|
|
34
|
+
*/
|
|
35
|
+
@Component({
|
|
36
|
+
selector: 'dotcms-container',
|
|
37
|
+
standalone: true,
|
|
38
|
+
imports: [AsyncPipe, NgComponentOutlet, NoComponent, ContentletComponent],
|
|
39
|
+
templateUrl: './container.component.html',
|
|
40
|
+
styleUrl: './container.component.css',
|
|
41
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
42
|
+
})
|
|
43
|
+
export class ContainerComponent implements OnChanges {
|
|
44
|
+
/**
|
|
45
|
+
* The container object containing the contentlets.
|
|
46
|
+
*
|
|
47
|
+
* @type {DotCMSContainer}
|
|
48
|
+
* @memberof ContainerComponent
|
|
49
|
+
*/
|
|
50
|
+
@Input({ required: true }) container!: DotCMSContainer;
|
|
51
|
+
|
|
52
|
+
private readonly pageContextService: PageContextService = inject(PageContextService);
|
|
53
|
+
protected readonly NoComponent = NoComponent;
|
|
54
|
+
protected readonly $isInsideEditor = signal<boolean>(false);
|
|
55
|
+
|
|
56
|
+
protected componentsMap!: Record<string, DynamicComponentEntity>;
|
|
57
|
+
protected $contentlets = signal<DotCMSContentlet[]>([]);
|
|
58
|
+
protected $dotContainer = signal<DotContainer | null>(null);
|
|
59
|
+
protected $dotContainerAsString = computed(() => JSON.stringify(this.$dotContainer()));
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The accept types for the container component.
|
|
63
|
+
*
|
|
64
|
+
* @type {(string | null)}
|
|
65
|
+
* @memberof ContainerComponent
|
|
66
|
+
*/
|
|
67
|
+
@HostBinding('attr.data-dot-accept-types') acceptTypes: string | null = null;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The identifier for the container component.
|
|
71
|
+
*
|
|
72
|
+
* @type {(string | null)}
|
|
73
|
+
* @memberof ContainerComponent
|
|
74
|
+
*/
|
|
75
|
+
@HostBinding('attr.data-dot-identifier') identifier: string | null = null;
|
|
76
|
+
/**
|
|
77
|
+
* The max contentlets for the container component.
|
|
78
|
+
*
|
|
79
|
+
* @type {(number | null)}
|
|
80
|
+
* @memberof ContainerComponent
|
|
81
|
+
*/
|
|
82
|
+
@HostBinding('attr.data-max-contentlets') maxContentlets: number | null = null;
|
|
83
|
+
/**
|
|
84
|
+
* The uuid for the container component.
|
|
85
|
+
*
|
|
86
|
+
* @type {(string | null)}
|
|
87
|
+
* @memberof ContainerComponent
|
|
88
|
+
*/
|
|
89
|
+
@HostBinding('attr.data-dot-uuid') uuid: string | null = null;
|
|
90
|
+
/**
|
|
91
|
+
* The class for the container component.
|
|
92
|
+
*
|
|
93
|
+
* @type {(string | null)}
|
|
94
|
+
* @memberof ContainerComponent
|
|
95
|
+
*/
|
|
96
|
+
@HostBinding('class') class: string | null = null;
|
|
97
|
+
/**
|
|
98
|
+
* The dot object for the container component.
|
|
99
|
+
*
|
|
100
|
+
* @type {(string | null)}
|
|
101
|
+
* @memberof ContainerComponent
|
|
102
|
+
*/
|
|
103
|
+
@HostBinding('attr.data-dot-object') dotObject: string | null = null;
|
|
104
|
+
/**
|
|
105
|
+
* The data-testid attribute used for identifying the component during testing.
|
|
106
|
+
*
|
|
107
|
+
* @memberof ContainerComponent
|
|
108
|
+
*/
|
|
109
|
+
@HostBinding('attr.data-testid') testId = 'dot-container';
|
|
110
|
+
|
|
111
|
+
ngOnChanges() {
|
|
112
|
+
const { pageAsset, components, isInsideEditor } = this.pageContextService.context;
|
|
113
|
+
const { acceptTypes, maxContentlets, variantId, path, contentlets } = getContainersData(
|
|
114
|
+
pageAsset.containers,
|
|
115
|
+
this.container
|
|
116
|
+
);
|
|
117
|
+
const { identifier, uuid } = this.container;
|
|
118
|
+
|
|
119
|
+
this.componentsMap = components;
|
|
120
|
+
|
|
121
|
+
this.$isInsideEditor.set(isInsideEditor);
|
|
122
|
+
this.$contentlets.set(contentlets);
|
|
123
|
+
this.$dotContainer.set({
|
|
124
|
+
identifier: path ?? identifier,
|
|
125
|
+
acceptTypes,
|
|
126
|
+
maxContentlets,
|
|
127
|
+
variantId,
|
|
128
|
+
uuid
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (this.$isInsideEditor()) {
|
|
132
|
+
this.acceptTypes = acceptTypes;
|
|
133
|
+
this.identifier = identifier;
|
|
134
|
+
this.maxContentlets = maxContentlets;
|
|
135
|
+
this.uuid = uuid;
|
|
136
|
+
this.class = this.$contentlets().length ? null : 'empty-container';
|
|
137
|
+
this.dotObject = 'container';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { ContentletComponent } from './contentlet.component';
|
|
4
|
+
|
|
5
|
+
describe('ContentletComponent', () => {
|
|
6
|
+
let component: ContentletComponent;
|
|
7
|
+
let fixture: ComponentFixture<ContentletComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [ContentletComponent]
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
|
|
14
|
+
fixture = TestBed.createComponent(ContentletComponent);
|
|
15
|
+
component = fixture.componentInstance;
|
|
16
|
+
fixture.detectChanges();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should create', () => {
|
|
20
|
+
expect(component).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { OnChanges } from '@angular/core';
|
|
1
|
+
import { ChangeDetectionStrategy, Component, HostBinding, Input, OnChanges } from '@angular/core';
|
|
2
|
+
|
|
2
3
|
import { DotCMSContentlet } from '../../models';
|
|
3
|
-
|
|
4
|
+
|
|
4
5
|
/**
|
|
5
6
|
* This component is responsible to display a contentlet.
|
|
6
7
|
*
|
|
@@ -8,79 +9,93 @@ import * as i0 from "@angular/core";
|
|
|
8
9
|
* @class ContentletComponent
|
|
9
10
|
* @implements {OnChanges}
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
@Component({
|
|
13
|
+
selector: 'dotcms-contentlet-wrapper',
|
|
14
|
+
standalone: true,
|
|
15
|
+
template: '<ng-content></ng-content>',
|
|
16
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
17
|
+
})
|
|
18
|
+
export class ContentletComponent implements OnChanges {
|
|
12
19
|
/**
|
|
13
20
|
* The contentlet object containing content data.
|
|
14
21
|
*
|
|
15
22
|
* @type {DotCMSContentlet}
|
|
16
23
|
* @memberof ContentletComponent
|
|
17
24
|
*/
|
|
18
|
-
|
|
25
|
+
@Input({ required: true }) contentlet!: DotCMSContentlet;
|
|
19
26
|
/**
|
|
20
27
|
* The container data (as string) where the contentlet is located.
|
|
21
28
|
*
|
|
22
29
|
* @type {string}
|
|
23
30
|
* @memberof ContentletComponent
|
|
24
31
|
*/
|
|
25
|
-
container
|
|
32
|
+
@Input() container!: string;
|
|
33
|
+
|
|
26
34
|
/**
|
|
27
35
|
* The identifier of contentlet component.
|
|
28
36
|
*
|
|
29
37
|
* @type {(string | null)}
|
|
30
38
|
* @memberof ContentletComponent
|
|
31
39
|
*/
|
|
32
|
-
identifier: string | null;
|
|
40
|
+
@HostBinding('attr.data-dot-identifier') identifier: string | null = null;
|
|
33
41
|
/**
|
|
34
42
|
* The base type of contentlet component.
|
|
35
43
|
*
|
|
36
44
|
* @type {(string | null)}
|
|
37
45
|
* @memberof ContentletComponent
|
|
38
46
|
*/
|
|
39
|
-
baseType: string | null;
|
|
47
|
+
@HostBinding('attr.data-dot-basetype') baseType: string | null = null;
|
|
40
48
|
/**
|
|
41
49
|
* The title of contentlet component.
|
|
42
50
|
*
|
|
43
51
|
* @type {(string | null)}
|
|
44
52
|
* @memberof ContentletComponent
|
|
45
53
|
*/
|
|
46
|
-
title: string | null;
|
|
54
|
+
@HostBinding('attr.data-dot-title') title: string | null = null;
|
|
47
55
|
/**
|
|
48
56
|
* The inode of contentlet component.
|
|
49
57
|
*
|
|
50
58
|
* @type {(string | null)}
|
|
51
59
|
* @memberof ContentletComponent
|
|
52
60
|
*/
|
|
53
|
-
inode: string | null;
|
|
61
|
+
@HostBinding('attr.data-dot-inode') inode: string | null = null;
|
|
54
62
|
/**
|
|
55
63
|
* The type of contentlet component.
|
|
56
64
|
*
|
|
57
65
|
* @type {(string | null)}
|
|
58
66
|
* @memberof ContentletComponent
|
|
59
67
|
*/
|
|
60
|
-
dotType: string | null;
|
|
68
|
+
@HostBinding('attr.data-dot-type') dotType: string | null = null;
|
|
61
69
|
/**
|
|
62
70
|
* The container of contentlet component.
|
|
63
71
|
*
|
|
64
72
|
* @type {(string | null)}
|
|
65
73
|
* @memberof ContentletComponent
|
|
66
74
|
*/
|
|
67
|
-
dotContainer: string | null;
|
|
75
|
+
@HostBinding('attr.data-dot-container') dotContainer: string | null = null;
|
|
68
76
|
/**
|
|
69
77
|
* The number of pages where the contentlet appears
|
|
70
78
|
*
|
|
71
79
|
* @type {(string | null)}
|
|
72
80
|
* @memberof ContentletComponent
|
|
73
81
|
*/
|
|
74
|
-
numberOfPages: string | null;
|
|
82
|
+
@HostBinding('attr.data-dot-on-number-of-pages') numberOfPages: string | null = null;
|
|
75
83
|
/**
|
|
76
84
|
* The content of contentlet component.
|
|
77
85
|
*
|
|
78
86
|
* @type {(string | null)}
|
|
79
87
|
* @memberof ContentletComponent
|
|
80
88
|
*/
|
|
81
|
-
dotContent: string | null;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
@HostBinding('attr.data-dot-object') dotContent: string | null = null;
|
|
90
|
+
|
|
91
|
+
ngOnChanges() {
|
|
92
|
+
this.identifier = this.contentlet.identifier;
|
|
93
|
+
this.baseType = this.contentlet.baseType;
|
|
94
|
+
this.title = this.contentlet.title;
|
|
95
|
+
this.inode = this.contentlet.inode;
|
|
96
|
+
this.dotType = this.contentlet.contentType;
|
|
97
|
+
this.dotContainer = this.container;
|
|
98
|
+
this.numberOfPages = this.contentlet['onNumberOfPages'];
|
|
99
|
+
this.dotContent = 'contentlet';
|
|
100
|
+
}
|
|
85
101
|
}
|
|
86
|
-
//# sourceMappingURL=contentlet.component.d.ts.map
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { expect } from '@jest/globals';
|
|
2
|
+
import { Spectator, createRoutingFactory } from '@ngneat/spectator/jest';
|
|
3
|
+
import { MockComponent } from 'ng-mocks';
|
|
4
|
+
import { of } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
import { Component, Input } from '@angular/core';
|
|
7
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
8
|
+
|
|
9
|
+
import * as dotcmsClient from '@dotcms/client';
|
|
10
|
+
|
|
11
|
+
import { PageResponseMock, PageResponseOneRowMock } from './../../utils/testing.utils';
|
|
12
|
+
import { DotcmsLayoutComponent } from './dotcms-layout.component';
|
|
13
|
+
|
|
14
|
+
import { DotCMSContentlet, DotCMSPageAsset } from '../../models';
|
|
15
|
+
import { PageContextService } from '../../services/dotcms-context/page-context.service';
|
|
16
|
+
import { RowComponent } from '../row/row.component';
|
|
17
|
+
|
|
18
|
+
interface Callback {
|
|
19
|
+
[key: string]: (data: unknown) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface DotCmsClientMock extends dotcmsClient.DotCmsClient {
|
|
23
|
+
editor: {
|
|
24
|
+
on: (type: string, callbackFn: (data: unknown) => void) => void;
|
|
25
|
+
off: jest.Mock;
|
|
26
|
+
callbacks: Callback;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Component({
|
|
31
|
+
selector: 'dotcms-mock-component',
|
|
32
|
+
standalone: true,
|
|
33
|
+
template: 'Hello world'
|
|
34
|
+
})
|
|
35
|
+
class DotcmsSDKMockComponent {
|
|
36
|
+
@Input() contentlet!: DotCMSContentlet;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
jest.mock('@dotcms/client', () => ({
|
|
40
|
+
...jest.requireActual('@dotcms/client'),
|
|
41
|
+
isInsideEditor: jest.fn().mockReturnValue(true),
|
|
42
|
+
initEditor: jest.fn(),
|
|
43
|
+
updateNavigation: jest.fn(),
|
|
44
|
+
postMessageToEditor: jest.fn(),
|
|
45
|
+
DotCmsClient: {
|
|
46
|
+
instance: {
|
|
47
|
+
editor: {
|
|
48
|
+
on: function (type: string, callbackFn: (data: unknown) => void): void {
|
|
49
|
+
this.callbacks[type] = callbackFn;
|
|
50
|
+
},
|
|
51
|
+
off: jest.fn(),
|
|
52
|
+
callbacks: {} as Callback
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
CUSTOMER_ACTIONS: {
|
|
57
|
+
GET_PAGE_DATA: 'get-page-data'
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
const { DotCmsClient } = dotcmsClient as jest.Mocked<typeof dotcmsClient>;
|
|
62
|
+
|
|
63
|
+
describe('DotcmsLayoutComponent', () => {
|
|
64
|
+
let spectator: Spectator<DotcmsLayoutComponent>;
|
|
65
|
+
let pageContextService: PageContextService;
|
|
66
|
+
|
|
67
|
+
const createComponent = createRoutingFactory({
|
|
68
|
+
component: DotcmsLayoutComponent,
|
|
69
|
+
imports: [MockComponent(RowComponent)],
|
|
70
|
+
providers: [
|
|
71
|
+
PageContextService,
|
|
72
|
+
{ provide: ActivatedRoute, useValue: { url: of([]) } },
|
|
73
|
+
{ provide: Router, useValue: {} }
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
spectator = createComponent({
|
|
79
|
+
props: {
|
|
80
|
+
pageAsset: PageResponseMock as unknown as DotCMSPageAsset,
|
|
81
|
+
components: {
|
|
82
|
+
Banner: Promise.resolve(DotcmsSDKMockComponent)
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
detectChanges: false
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
pageContextService = spectator.inject(PageContextService, true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
jest.clearAllMocks();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should render rows', () => {
|
|
96
|
+
spectator.detectChanges();
|
|
97
|
+
expect(spectator.queryAll(RowComponent).length).toBe(3);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should save pageContext', () => {
|
|
101
|
+
const setContextSpy = jest.spyOn(pageContextService, 'setContext');
|
|
102
|
+
spectator.detectChanges();
|
|
103
|
+
expect(setContextSpy).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('inside editor', () => {
|
|
107
|
+
it('should call initEditor and updateNavigation from @dotcms/client', () => {
|
|
108
|
+
const initEditorSpy = jest.spyOn(dotcmsClient, 'initEditor');
|
|
109
|
+
const updateNavigationSpy = jest.spyOn(dotcmsClient, 'updateNavigation');
|
|
110
|
+
|
|
111
|
+
spectator.detectChanges();
|
|
112
|
+
expect(initEditorSpy).toHaveBeenCalled();
|
|
113
|
+
expect(updateNavigationSpy).toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('onReload', () => {
|
|
117
|
+
const client = DotCmsClient.instance;
|
|
118
|
+
let editorOnSpy: jest.SpyInstance;
|
|
119
|
+
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
editorOnSpy = jest.spyOn(client.editor, 'on');
|
|
122
|
+
spectator.setInput('onReload', () => {
|
|
123
|
+
/* do nothing */
|
|
124
|
+
});
|
|
125
|
+
spectator.detectChanges();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should subscribe to the `CHANGE` event', () => {
|
|
129
|
+
expect(editorOnSpy).toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should remove listener on unmount', () => {
|
|
133
|
+
spectator.component.ngOnDestroy();
|
|
134
|
+
spectator.detectChanges();
|
|
135
|
+
|
|
136
|
+
expect(client.editor.off).toHaveBeenCalledWith('changes');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('client is ready', () => {
|
|
141
|
+
const query = { query: 'query { ... }' };
|
|
142
|
+
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
spectator.setInput('editor', query);
|
|
145
|
+
spectator.detectChanges();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should post message to editor', () => {
|
|
149
|
+
spectator.detectChanges();
|
|
150
|
+
expect(dotcmsClient.postMessageToEditor).toHaveBeenCalledWith({
|
|
151
|
+
action: dotcmsClient.CUSTOMER_ACTIONS.CLIENT_READY,
|
|
152
|
+
payload: query
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('onChange', () => {
|
|
158
|
+
const client = DotCmsClient.instance;
|
|
159
|
+
beforeEach(() => spectator.detectChanges());
|
|
160
|
+
|
|
161
|
+
it('should update the page asset when changes are made in the editor', () => {
|
|
162
|
+
const editorOnSpy = jest.spyOn(client.editor, 'on');
|
|
163
|
+
expect(editorOnSpy).toHaveBeenCalledWith('changes', expect.any(Function));
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('template', () => {
|
|
169
|
+
beforeEach(() => spectator.detectChanges());
|
|
170
|
+
|
|
171
|
+
it('should render rows', () => {
|
|
172
|
+
expect(spectator.queryAll(RowComponent).length).toBe(3);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should pass the correct row to RowComponent', () => {
|
|
176
|
+
const rowComponents = spectator.queryAll(RowComponent);
|
|
177
|
+
const rows = PageResponseMock.layout.body.rows;
|
|
178
|
+
expect(rowComponents.length).toBe(rows.length);
|
|
179
|
+
|
|
180
|
+
rowComponents.forEach((component, index) => {
|
|
181
|
+
expect(component.row).toEqual(rows[index]);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should update the page asset when changes are made in the editor', () => {
|
|
186
|
+
const { editor } = DotCmsClient.instance as DotCmsClientMock;
|
|
187
|
+
editor.callbacks['changes'](PageResponseOneRowMock);
|
|
188
|
+
spectator.detectChanges();
|
|
189
|
+
const rowComponents = spectator.queryAll(RowComponent);
|
|
190
|
+
const rows = PageResponseMock.layout.body.rows;
|
|
191
|
+
expect(rowComponents.length).toBe(1);
|
|
192
|
+
expect(rowComponents[0].row).toEqual(rows[0]);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|