Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue-29621: Excluding Hidden content types from the Widgets displayed #30517

Merged
merged 8 commits into from
Dec 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import {
DotContentTypeService,
DotESContentService,
DotPropertiesService,
DotSessionStorageService,
PaginatorService
} from '@dotcms/data-access';
Expand Down Expand Up @@ -110,18 +111,29 @@ class MockContentTypeService {
}
}

const SORTED_CONTENT_TYPE_MOCK = contentTypeDataMock.sort((a, b) =>
a.name.localeCompare(b.name)
) as DotCMSContentType[];

describe('DotPaletteStore', () => {
let dotPaletteStore: DotPaletteStore;
let paginatorService: PaginatorService;
let dotContentTypeService: DotContentTypeService;
let dotESContentService: DotESContentService;
let dotSessionStorageService: DotSessionStorageService;
let dotPropertiesService: DotPropertiesService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
DotPaletteStore,
DotSessionStorageService,
{
provide: DotPropertiesService,
useValue: {
getKeyAsList: () => of([])
}
},
{ provide: PaginatorService, useClass: MockPaginatorService },
{ provide: DotContentTypeService, useClass: MockContentTypeService },
{ provide: DotESContentService, useClass: MockESPaginatorService }
Expand All @@ -132,6 +144,7 @@ describe('DotPaletteStore', () => {
dotContentTypeService = TestBed.inject(DotContentTypeService);
dotESContentService = TestBed.inject(DotESContentService);
dotSessionStorageService = TestBed.inject(DotSessionStorageService);
dotPropertiesService = TestBed.inject(DotPropertiesService);
});

// Updaters
Expand Down Expand Up @@ -182,27 +195,42 @@ describe('DotPaletteStore', () => {

// Effects
it('should load contentTypes to store', (done) => {
const sortedDataMock = contentTypeDataMock.sort((a, b) => a.name.localeCompare(b.name));
spyOn(dotContentTypeService, 'filterContentTypes').and.returnValues(
of(sortedDataMock as DotCMSContentType[])
of(SORTED_CONTENT_TYPE_MOCK)
);
spyOn(dotContentTypeService, 'getContentTypes').and.returnValues(of([]));

dotPaletteStore.loadContentTypes(['blog', 'banner']);
dotPaletteStore.vm$.subscribe((data) => {
expect(data.contentTypes).toEqual(SORTED_CONTENT_TYPE_MOCK);
done();
});
});

it("should load contentTypes and remove the hidden is the CONTENT_PALETTE_HIDDEN_CONTENT_TYPES is setted'", (done) => {
spyOn(dotContentTypeService, 'filterContentTypes').and.returnValues(
of(SORTED_CONTENT_TYPE_MOCK)
);
spyOn(dotContentTypeService, 'getContentTypes').and.returnValues(of([]));
spyOn(dotPropertiesService, 'getKeyAsList').and.returnValue(of(['Form']));

const expectedData = SORTED_CONTENT_TYPE_MOCK.filter((item) => item.variable !== 'Form');

dotPaletteStore.loadContentTypes(['blog', 'banner']);
dotPaletteStore.vm$.subscribe((data) => {
expect(data.contentTypes).toEqual(sortedDataMock as DotCMSContentType[]);
expect(data.contentTypes).toEqual(expectedData);
done();
});
});

it('should load inly widgets to store if allowedContent is empty', (done) => {
const sortedDataMock = contentTypeDataMock.sort((a, b) => a.name.localeCompare(b.name));
it('should load only widgets to store if allowedContent is empty', (done) => {
spyOn(dotContentTypeService, 'filterContentTypes').and.returnValues(of([]));
spyOn(dotContentTypeService, 'getContentTypes').and.returnValues(
of(sortedDataMock as DotCMSContentType[])
of(SORTED_CONTENT_TYPE_MOCK)
);
dotPaletteStore.loadContentTypes([]);
dotPaletteStore.vm$.subscribe((data) => {
expect(data.contentTypes).toEqual(sortedDataMock as DotCMSContentType[]);
expect(data.contentTypes).toEqual(SORTED_CONTENT_TYPE_MOCK);
done();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ComponentStore } from '@ngrx/component-store';
import { forkJoin, Observable } from 'rxjs';
import { forkJoin, Observable, of } from 'rxjs';

import { Injectable } from '@angular/core';

Expand All @@ -10,14 +10,17 @@ import { debounceTime, map, take } from 'rxjs/operators';
import {
DotContentTypeService,
DotESContentService,
DotPropertiesService,
DotSessionStorageService,
PaginatorService
} from '@dotcms/data-access';
import {
ComponentStatus,
DEFAULT_VARIANT_ID,
DotCMSBaseTypesContentTypes,
DotCMSContentlet,
DotCMSContentType,
DotConfigurationVariables,
ESContent
} from '@dotcms/dotcms-models';

Expand Down Expand Up @@ -163,7 +166,8 @@ export class DotPaletteStore extends ComponentStore<DotPaletteState> {
private dotContentTypeService: DotContentTypeService,
private paginatorESService: DotESContentService,
private paginationService: PaginatorService,
private dotSessionStorageService: DotSessionStorageService
private dotSessionStorageService: DotSessionStorageService,
private dotConfigurationService: DotPropertiesService
) {
super({
contentlets: null,
Expand Down Expand Up @@ -249,37 +253,41 @@ export class DotPaletteStore extends ComponentStore<DotPaletteState> {
*/
getContenttypesData(): void {
this.setLoading();
this.state$.pipe(take(1)).subscribe(({ filter, allowedContent }) => {
if (allowedContent && allowedContent.length) {
forkJoin([
this.dotContentTypeService.filterContentTypes(filter, allowedContent.join(',')),
this.dotContentTypeService.getContentTypes({ filter, page: 40, type: 'WIDGET' })
])
.pipe(take(1))
.subscribe((results: DotCMSContentType[][]) => {
const [allowContent, widgets] = results;

// Some pages bring widgets in the CONTENT_PALETTE_HIDDEN_CONTENT_TYPES, and others don't.
// However, all pages allow widgets, so we make a request just to get them.
// Full comment here: https://github.com/dotCMS/core/pull/22573#discussion_r921263060
// This filter is used to prevent widgets from being repeated.
const contentLets = allowContent.filter(
(item) => item.baseType !== 'WIDGET'
);

// Merge both array and order them by name
const contentTypes = [...contentLets, ...widgets]
.sort((a, b) => a.name.localeCompare(b.name))
.slice(0, 40);

this.loadContentypes(contentTypes);
});
} else {
this.dotContentTypeService
.getContentTypes({ filter, page: 40, type: 'WIDGET' })
.pipe(take(1))
.subscribe((data: DotCMSContentType[]) => this.loadContentypes(data));
}
this.state$.pipe(take(1)).subscribe(({ filter, allowedContent = [] }) => {
// Note: This store needs to be refactored
const hasAllowedContent = allowedContent && allowedContent.length > 0;
const contentTypes$ = hasAllowedContent
? this.dotContentTypeService.filterContentTypes(filter, allowedContent.join(','))
: of([]);

forkJoin({
contentTypes: contentTypes$,
widgets: this.dotContentTypeService.getContentTypes({
filter,
page: 40,
type: 'WIDGET'
}),
hiddenContentTypes: this.dotConfigurationService.getKeyAsList(
DotConfigurationVariables.CONTENT_PALETTE_HIDDEN_CONTENT_TYPES
)
})
.pipe(take(1))
.subscribe(({ contentTypes, widgets, hiddenContentTypes }) => {
/**
* This filter is used to prevent widgets from being repeated.
* More information here: https://github.com/dotCMS/core/pull/22573#discussion_r921263060
*/
const filteredContentTypes = contentTypes.filter(
(item) => item.baseType !== DotCMSBaseTypesContentTypes.WIDGET
);
const mergedContentAndWidgets = [...filteredContentTypes, ...widgets];
const data = mergedContentAndWidgets
.filter((item) => !hiddenContentTypes.includes(item.variable))
.sort((a, b) => a.name.localeCompare(b.name))
.slice(0, 40);

this.loadContentypes(data);
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
DEFAULT_VARIANT_NAME,
DotCMSContentlet,
DotCMSContentType,
DotConfigurationVariables,
DotContainerStructure,
DotContentletEventAddContentType,
DotExperiment,
Expand Down Expand Up @@ -377,9 +378,8 @@ browse from the page internal links
}

private setAllowedContent(pageState: DotPageRenderState): void {
const CONTENT_HIDDEN_KEY = 'CONTENT_PALETTE_HIDDEN_CONTENT_TYPES';
this.dotConfigurationService
.getKeyAsList(CONTENT_HIDDEN_KEY)
.getKeyAsList(DotConfigurationVariables.CONTENT_PALETTE_HIDDEN_CONTENT_TYPES)
.pipe(take(1))
.subscribe((results) => {
this.allowedContent = this.filterAllowedContentTypes(results, pageState) || [];
Expand Down
5 changes: 5 additions & 0 deletions core-web/libs/dotcms-models/src/lib/shared-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export const enum FeaturedFlags {
FEATURE_FLAG_UVE_PREVIEW_MODE = 'FEATURE_FLAG_UVE_PREVIEW_MODE'
}

export const enum DotConfigurationVariables {
CONTENT_PALETTE_HIDDEN_CONTENT_TYPES = 'CONTENT_PALETTE_HIDDEN_CONTENT_TYPES',
WYSIWYG_IMAGE_URL_PATTERN = 'WYSIWYG_IMAGE_URL_PATTERN'
}

export const FEATURE_FLAG_NOT_FOUND = 'NOT_FOUND';

export type DotDropdownGroupSelectOption<T> = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
DotESContentService,
DotPropertiesService
} from '@dotcms/data-access';
import { DotCMSContentType } from '@dotcms/dotcms-models';
import { DotConfigurationVariables } from '@dotcms/dotcms-models';

import {
DotPaletteStore,
Expand All @@ -17,8 +17,23 @@ import {
PALETTE_TYPES
} from './edit-ema-palette.store';

const WIDGET_MOCK = {
baseType: 'WIDGET',
id: 'widgetTest1',
name: 'widgetTest1',
variable: 'widgetTest1'
};

const VALID_CONTENT_TYPE_MOCK = {
baseType: 'someContent',
id: 'contentTypeTest1',
name: 'contentTypeTest1',
variable: 'contentTypeTest1'
};

describe('EditEmaPaletteStore', () => {
let spectator: SpectatorService<DotPaletteStore>;
let dotPropertiesService: DotPropertiesService;
const createService = createServiceFactory({
service: DotPaletteStore,
providers: [
Expand All @@ -43,32 +58,29 @@ describe('EditEmaPaletteStore', () => {
useValue: {
filterContentTypes: () =>
of([
{
baseType: 'someContent',
id: 'contentTypeTest1',
name: 'contentTypeTest1'
},
VALID_CONTENT_TYPE_MOCK,
{
baseType: 'WIDGET',
id: 'contentTypeTest2',
name: 'contentTypeTest2'
name: 'contentTypeTest2',
variable: 'contentTypeTest2'
}
]),
getContentTypes: () =>
of([
{
baseType: 'WIDGET',
id: 'widgetTest1',
name: 'widgetTest1'
}
])
getContentTypes: () => of([WIDGET_MOCK])
}
},
{
provide: DotPropertiesService,
useValue: {
getKeyAsList: () => of([])
}
}
]
});

beforeEach(() => {
spectator = createService();
dotPropertiesService = spectator.inject(DotPropertiesService);
});

describe('updaters', () => {
Expand Down Expand Up @@ -105,26 +117,19 @@ describe('EditEmaPaletteStore', () => {
spectator.inject(DotContentTypeService),
'getContentTypes'
);

const getKeyAsListSpy = jest.spyOn(dotPropertiesService, 'getKeyAsList');
spectator.service.loadContentTypes({
filter: '',
allowedContent: ['contentTypeTest1']
});
const expected = [
{
baseType: 'someContent',
id: 'contentTypeTest1',
name: 'contentTypeTest1'
},
{
baseType: 'WIDGET',
id: 'widgetTest1',
name: 'widgetTest1'
}
];
spectator.service.vm$.subscribe((state) => {
expect(state.contenttypes.items).toEqual(expected as DotCMSContentType[]);
expect(state.contenttypes.items).toEqual([VALID_CONTENT_TYPE_MOCK, WIDGET_MOCK]);
expect(patchStateSpy).toHaveBeenCalled();
expect(filterContentTypesSpy).toHaveBeenCalledWith('', 'contentTypeTest1');
expect(getKeyAsListSpy).toHaveBeenCalledWith(
DotConfigurationVariables.CONTENT_PALETTE_HIDDEN_CONTENT_TYPES
);
expect(getContentTypesSpy).toHaveBeenCalledWith({
filter: '',
page: 40,
Expand All @@ -134,6 +139,26 @@ describe('EditEmaPaletteStore', () => {
});
});

it('should load content types with filter hidden content types', (done) => {
const getKeyAsListSpy = jest
.spyOn(dotPropertiesService, 'getKeyAsList')
.mockReturnValue(of(['contentTypeTest1']));

const payload = {
filter: '',
allowedContent: ['contentTypeTest1']
};

spectator.service.loadContentTypes(payload);
spectator.service.vm$.subscribe((state) => {
expect(state.contenttypes.items).toEqual([WIDGET_MOCK]);
expect(getKeyAsListSpy).toHaveBeenCalledWith(
DotConfigurationVariables.CONTENT_PALETTE_HIDDEN_CONTENT_TYPES
);
done();
});
});

it('should load contentlets', (done) => {
const contentServiceSpy = jest.spyOn(spectator.inject(DotESContentService), 'get');
const patchStateSpy = jest.spyOn(spectator.service, 'patchState');
Expand Down
Loading