diff --git a/core-web/libs/edit-content/jest.config.ts b/core-web/libs/edit-content/jest.config.ts index d9cfaa6d4e51..2728432e8776 100644 --- a/core-web/libs/edit-content/jest.config.ts +++ b/core-web/libs/edit-content/jest.config.ts @@ -4,7 +4,6 @@ export default { preset: '../../jest.preset.js', setupFilesAfterEnv: ['/src/test-setup.ts'], globals: {}, - coverageDirectory: '../../coverage/libs/edit-content', transform: { '^.+\\.(ts|mjs|js|html)$': [ 'jest-preset-angular', diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.html new file mode 100644 index 000000000000..a3bf0f1018ef --- /dev/null +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.html @@ -0,0 +1,17 @@ +
+ @for (category of $categoriesToShow(); track category.key) { + + } @if ($showAllBtn()) { + + } +
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.spec.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.spec.ts new file mode 100644 index 000000000000..a454ab2c0f7b --- /dev/null +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.spec.ts @@ -0,0 +1,99 @@ +import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; + +import { ButtonModule, ButtonDirective } from 'primeng/button'; +import { ChipModule, Chip } from 'primeng/chip'; + +import { DotMessageService } from '@dotcms/data-access'; + +import { DotCategoryFieldChipsComponent } from './dot-category-field-chips.component'; + +import { MAX_CHIPS } from '../../dot-edit-content-category-field.const'; +import { CATEGORIES_KEY_VALUE, CATEGORY_MESSAGE_MOCK } from '../../mocks/category-field.mocks'; + +describe('DotCategoryFieldChipsComponent', () => { + let spectator: Spectator; + + const createComponent = createComponentFactory({ + component: DotCategoryFieldChipsComponent, + providers: [ + { + provide: DotMessageService, + useValue: CATEGORY_MESSAGE_MOCK + } + ], + imports: [ChipModule, ButtonModule] + }); + + beforeEach(() => { + spectator = createComponent({ + detectChanges: false, + props: { + categories: CATEGORIES_KEY_VALUE + } as unknown as DotCategoryFieldChipsComponent + }); + }); + + it('should be created', () => { + spectator.detectChanges(); + expect(spectator.component).toBeTruthy(); + }); + + it('should the max input be equal to constant by default', () => { + spectator.detectChanges(); + expect(spectator.component.$max()).toBe(MAX_CHIPS); + }); + + it('should be show a max of categories', () => { + spectator.setInput('max', 2); + spectator.detectChanges(); + const chips = spectator.queryAll(Chip); + expect(chips.length).toBe(2); + }); + + it('should be show all categories', () => { + spectator.setInput('max', 2); + spectator.component.$showAll.set(true); + spectator.detectChanges(); + const chips = spectator.queryAll(Chip); + expect(chips.length).toBe(CATEGORIES_KEY_VALUE.length); + }); + + it('should be show the more btn with the proper label', () => { + spectator.setInput('max', 2); + spectator.detectChanges(); + const showBtn = spectator.query(ButtonDirective); + const size = spectator.component.$categories().length - spectator.component.$max(); + expect(showBtn.label).toBe(`${size} More`); + }); + + it('should be show the less btn with the proper label', () => { + spectator.setInput('max', 2); + spectator.component.$showAll.set(true); + spectator.detectChanges(); + const showBtn = spectator.query(ButtonDirective); + expect(showBtn.label).toBe(`Less`); + }); + + it('should not show a btn and the label be null', () => { + spectator.setInput('max', CATEGORIES_KEY_VALUE.length + 1); + spectator.detectChanges(); + const showBtn = spectator.query(ButtonDirective); + const label = spectator.component.$btnLabel(); + expect(showBtn).toBeNull(); + expect(label).toBeNull(); + }); + + describe('toogleShowAll', () => { + it('should set showAll to true', () => { + spectator.component.$showAll.set(false); + spectator.component.toogleShowAll(); + expect(spectator.component.$showAll()).toBe(true); + }); + + it('should set showAll to false', () => { + spectator.component.$showAll.set(true); + spectator.component.toogleShowAll(); + expect(spectator.component.$showAll()).toBe(false); + }); + }); +}); diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.ts new file mode 100644 index 000000000000..a1dbc3611338 --- /dev/null +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-chips/dot-category-field-chips.component.ts @@ -0,0 +1,107 @@ +import { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core'; + +import { ButtonModule } from 'primeng/button'; +import { ChipModule } from 'primeng/chip'; +import { TooltipModule } from 'primeng/tooltip'; + +import { DotMessageService } from '@dotcms/data-access'; + +import { MAX_CHIPS } from '../../dot-edit-content-category-field.const'; +import { DotCategoryFieldKeyValueObj } from '../../models/dot-category-field.models'; + +/** + * Represents the Dot Category Field Chips component. + * + * @export + * @class DotCategoryFieldChipsComponent + */ +@Component({ + selector: 'dot-category-field-chips', + standalone: true, + imports: [ButtonModule, ChipModule, TooltipModule], + templateUrl: './dot-category-field-chips.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DotCategoryFieldChipsComponent { + /** + * Represents the variable 'dotMessageService' which is of type 'DotMessageService'. + * + * @memberof DotCategoryFieldChipsComponent + */ + readonly #dotMessageService = inject(DotMessageService); + /** + * Represents the variable 'showAll' which is of type 'signal'. + * + * @memberof DotCategoryFieldChipsComponent + */ + $showAll = signal(false); + /** + * Represents the variable 'max' which is of type 'number'. + * + * @memberof DotCategoryFieldChipsComponent + */ + $max = input(MAX_CHIPS, { alias: 'max' }); + /** + * Represents the variable 'categories' which is of type 'DotCategoryFieldKeyValueObj[]'. + * + * @memberof DotCategoryFieldChipsComponent + */ + $categories = input.required({ alias: 'categories' }); + /** + * Represents the variable 'label' which is of type 'string'. + * + * @memberof DotCategoryFieldChipsComponent + */ + $categoriesToShow = computed(() => { + const categories = this.$categories(); + if (this.$showAll()) { + return categories; + } + + return categories.slice(0, this.$max()); + }); + /** + * Represents the variable '$showAllBtn' which is of type 'computed'. + * + * @memberof DotCategoryFieldChipsComponent + */ + $showAllBtn = computed(() => { + const size = this.$categories().length; + + if (size > this.$max()) { + return true; + } + + return false; + }); + /** + * Represents the variable 'btnLabel' which is of type 'computed'. + * + * @memberof DotCategoryFieldChipsComponent + */ + $btnLabel = computed(() => { + const size = this.$categories().length; + const max = this.$max(); + + if (this.$showAll()) { + return this.#dotMessageService.get('edit.content.category-field.list.show.less'); + } + + if (size > max) { + return this.#dotMessageService.get( + 'edit.content.category-field.list.show.more', + `${size - max}` + ); + } + + return null; + }); + /** + * Method to toogle the show all categories. + * + * @memberof DotCategoryFieldChipsComponent + */ + toogleShowAll(): void { + this.$showAll.update((showAll) => !showAll); + } +} diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-sidebar/dot-category-field-sidebar.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-sidebar/dot-category-field-sidebar.component.ts index 452709f087c9..c208650423c5 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-sidebar/dot-category-field-sidebar.component.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/components/dot-category-field-sidebar/dot-category-field-sidebar.component.ts @@ -16,7 +16,6 @@ import { DotMessagePipe } from '@dotcms/ui'; import { CategoryFieldStore } from '../../store/content-category-field.store'; import { DotCategoryFieldCategoryListComponent } from '../dot-category-field-category-list/dot-category-field-category-list.component'; - /** * The DotCategoryFieldSidebarComponent is a sidebar panel that allows editing of content category field. * It provides interfaces for item selection and click handling, and communicates with a store diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.html index 5236a116a5ec..16fa93ebf57b 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.html +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.html @@ -1,14 +1,7 @@ @if (store.selected().length) { -
- @for (category of store.selected(); track category.key) { - - } -
+
+ +
}
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.ts index c4150c2f4271..5bdbdb811e5c 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.component.ts @@ -14,15 +14,13 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ControlContainer, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { ButtonModule } from 'primeng/button'; -import { ChipModule } from 'primeng/chip'; -import { ChipsModule } from 'primeng/chips'; -import { TooltipModule } from 'primeng/tooltip'; import { delay } from 'rxjs/operators'; import { DotCMSContentlet, DotCMSContentTypeField } from '@dotcms/dotcms-models'; import { DotDynamicDirective, DotMessagePipe } from '@dotcms/ui'; +import { DotCategoryFieldChipsComponent } from './components/dot-category-field-chips/dot-category-field-chips.component'; import { DotCategoryFieldSidebarComponent } from './components/dot-category-field-sidebar/dot-category-field-sidebar.component'; import { CLOSE_SIDEBAR_CSS_DELAY_MS } from './dot-edit-content-category-field.const'; import { CategoriesService } from './services/categories.service'; @@ -40,14 +38,12 @@ import { CategoryFieldStore } from './store/content-category-field.store'; selector: 'dot-edit-content-category-field', standalone: true, imports: [ - ChipsModule, ReactiveFormsModule, ButtonModule, - ChipModule, NgClass, - TooltipModule, DotMessagePipe, - DotDynamicDirective + DotDynamicDirective, + DotCategoryFieldChipsComponent ], templateUrl: './dot-edit-content-category-field.component.html', styleUrl: './dot-edit-content-category-field.component.scss', diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.const.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.const.ts index d6b69030a7be..1165e2b74363 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.const.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/dot-edit-content-category-field.const.ts @@ -1 +1,3 @@ export const CLOSE_SIDEBAR_CSS_DELAY_MS = 300; + +export const MAX_CHIPS = 10; diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/mocks/category-field.mocks.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/mocks/category-field.mocks.ts index 5374f048bbb3..c1d9b67f6aae 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/mocks/category-field.mocks.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-category-field/mocks/category-field.mocks.ts @@ -1,6 +1,10 @@ import { DotCMSContentlet, DotCMSContentTypeField } from '@dotcms/dotcms-models'; +import { MockDotMessageService } from '@dotcms/utils-testing'; -import { DotCategoryFieldCategory } from '../models/dot-category-field.models'; +import { + DotCategoryFieldCategory, + DotCategoryFieldKeyValueObj +} from '../models/dot-category-field.models'; export const CATEGORY_FIELD_VARIABLE_NAME = 'categorias'; @@ -203,3 +207,43 @@ export const CATEGORY_LIST_MOCK: DotCategoryFieldCategory[][] = [ * Represent the selected categories */ export const SELECTED_LIST_MOCK = [CATEGORY_LEVEL_1[1].inode, CATEGORY_LEVEL_1[2].inode]; + +export const CATEGORIES_KEY_VALUE: DotCategoryFieldKeyValueObj[] = [ + { + key: '0ab5e687775e4793679970e561380560', + value: 'Electrical', + path: 'Electrical' + }, + { + key: 'cb83dc32c0a198fd0ca427b3b587f4ce', + value: 'Doors & Windows', + path: 'Doors & Windows' + }, + { + key: '1f208488057007cedda0e0b5d52ee3b3', + value: 'Cleaning Supplies', + path: 'Cleaning Supplies' + }, + { + key: 'd2fb8e67c390e3b84cd613fa15aad5d4', + value: 'Concrete & Cement', + path: 'Concrete & Cement' + }, + { + key: '3a3effac9f26593810c8687e692817a6', + value: 'Flooring', + path: 'Flooring' + }, + { + key: '977ba2c4e2af65e303c748ec39f0f1ca', + value: 'Garage Organization', + path: 'Garage Organization' + } +]; + +const MESSAGES_MOCK = { + 'edit.content.category-field.list.show.less': 'Less', + 'edit.content.category-field.list.show.more': '{0} More' +}; + +export const CATEGORY_MESSAGE_MOCK = new MockDotMessageService(MESSAGES_MOCK); diff --git a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties index 2a07847b5276..26af8f1c4684 100644 --- a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties +++ b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties @@ -5746,3 +5746,5 @@ content.type.form.banner.message= Enable Edit Content Beta for a fresh ed edit.content.category-field.show-categories-dialog=Select edit.content.category-field.sidebar.header.select-categories=Select categories edit.content.category-field.sidebar.button.clear-all=Clear all +edit.content.category-field.list.show.more={0} More +edit.content.category-field.list.show.less=Less