Skip to content
This repository has been archived by the owner on Dec 16, 2024. It is now read-only.

Commit

Permalink
dotCMS/core#21915 Add inline edit to content type name and Icon
Browse files Browse the repository at this point in the history
  • Loading branch information
alfredo-dotcms committed Apr 26, 2022
1 parent f2df2ac commit cbff51d
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class ContentTypeFieldsPropertiesFormComponent implements OnChanges, OnIn
constructor(private fb: FormBuilder, private fieldPropertyService: FieldPropertyService) {}

ngOnChanges(changes: SimpleChanges): void {
if (changes.formFieldData.currentValue && this.formFieldData) {
if (changes.formFieldData?.currentValue && this.formFieldData) {
this.destroy();

setTimeout(this.init.bind(this), 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,42 @@
<div class="main-toolbar-left">
<div class="content-type__title">
<header>
<h4>{{ contentType.name }}</h4>
<dot-icon name="{{ contentType.icon }}"></dot-icon>
<p-inplace #contentTypeInlineEdit (onActivate)="editInlineActivate($event)">
<ng-template pTemplate="display">
<h4>{{ contentType.name }}</h4>
</ng-template>
<ng-template pTemplate="content">
<input
[style.width.px]="contentTypeNameInputSize"
#contentTypeNameInput
type="text"
[value]="contentType.name"
(keydown.enter)="fireChangeName()"
(keydown.escape)="contentTypeInlineEdit.deactivate()"
pInputText
dotAutofocus
/>
</ng-template>
</p-inplace>
<dot-api-link href="api/v1/contenttype/id/{{ contentType.id }}"></dot-api-link>
</header>
</div>

<div class="content-type__info">
{{ 'contenttypes.content.variable' | dm }}:
<dot-copy-link
data-testId="copyVariableName"
[copy]="contentType.variable"
[label]="contentType.variable"
></dot-copy-link>
<span class="content-type__dot-separator"></span>
{{ 'contenttypes.form.identifier' | dm }}: {{ contentType.id }}
{{ 'contenttypes.form.identifier' | dm }}:
<dot-copy-link
data-testId="copyIdentifier"
[copy]="contentType.id"
[label]="contentType.id"
></dot-copy-link>
</div>
</div>
<div class="main-toolbar-right">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,33 @@ $top-height: ($toolbar-height + $tabview-nav-height + $dot-secondary-toolbar-mai
header {
display: flex;
align-items: center;

dot-icon {
align-items: center;
margin-right: $spacing-2;
}

::ng-deep {
p-inplace {
h4,
input {
min-width: 50px;
}

.p-inplace-display {
display: flex;
padding: 0;
}

.p-inplace-content input {
height: 36px;
}

button {
margin-left: $spacing-2;
}
}
}
}

h4 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { TabViewModule } from 'primeng/tabview';
import { SplitButtonModule } from 'primeng/splitbutton';
import { MenuItem } from 'primeng/api';
import { DotPortletBoxModule } from '@components/dot-portlet-base/components/dot-portlet-box/dot-portlet-box.module';
import { InplaceModule } from 'primeng/inplace';

@Component({
selector: 'dot-content-types-fields-list',
Expand Down Expand Up @@ -78,6 +79,7 @@ class FieldDragDropServiceMock {

const fakeContentType: DotCMSContentType = {
...dotcmsContentTypeBasicMock,
icon: 'testIcon',
id: '1234567890',
name: 'name',
variable: 'helloVariable',
Expand Down Expand Up @@ -121,6 +123,7 @@ describe('ContentTypesLayoutComponent', () => {
DotCopyLinkModule,
DotPipesModule,
SplitButtonModule,
InplaceModule,
HttpClientTestingModule,
DotPortletBoxModule
],
Expand Down Expand Up @@ -155,9 +158,8 @@ describe('ContentTypesLayoutComponent', () => {
});

it('should set the field and row bag options', () => {
const fieldDragDropService: FieldDragDropService = fixture.debugElement.injector.get(
FieldDragDropService
);
const fieldDragDropService: FieldDragDropService =
fixture.debugElement.injector.get(FieldDragDropService);
fixture.componentInstance.contentType = fakeContentType;
spyOn(fieldDragDropService, 'setBagOptions');
fixture.detectChanges();
Expand Down Expand Up @@ -214,20 +216,53 @@ describe('ContentTypesLayoutComponent', () => {
});

it('should have elements in the correct place', () => {
expect(
de.query(By.css('.main-toolbar-left header dot-icon')).componentInstance.name
).toBe(fakeContentType.icon);
expect(de.query(By.css('.main-toolbar-left header p-inplace'))).toBeDefined();
expect(
de.query(By.css('.main-toolbar-left header p-inplace h4')).nativeElement.innerHTML
).toBe(fakeContentType.name);
expect(de.query(By.css('.main-toolbar-left .content-type__title'))).toBeDefined();
expect(de.query(By.css('.main-toolbar-left .content-type__info'))).toBeDefined();
expect(de.query(By.css('.main-toolbar-right #form-edit-button'))).toBeDefined();
});

it('should set and emit change name of Content Type', () => {
de.query(By.css('.main-toolbar-left header p-inplace h4')).nativeElement.click();
fixture.detectChanges();

spyOn(de.componentInstance.changeContentTypeName, 'emit');

expect(de.query(By.css('.main-toolbar-left header p-inplace input'))).toBeDefined();
de.query(By.css('.main-toolbar-left header p-inplace input')).nativeElement.value =
'changedName';
de.query(By.css('.main-toolbar-left header p-inplace input')).triggerEventHandler(
'keydown.enter',
{
stopPropagation: jasmine.createSpy('stopPropagation')
}
);
expect(de.componentInstance.changeContentTypeName.emit).toHaveBeenCalledWith(
'changedName'
);
});

it('should have api link component', () => {
expect(de.query(By.css('dot-api-link')).componentInstance.link).toBe(
'/api/v1/contenttype/id/1234567890'
);
});

it('should have copy variable link', () => {
expect(de.query(By.css('dot-copy-link')).componentInstance.copy).toBe(
'helloVariable'
expect(
de.query(By.css('[data-testId="copyVariableName"]')).componentInstance.copy
).toBe('helloVariable');
});

it('should have copy identifier link', () => {
expect(de.query(By.css('[data-testId="copyIdentifier"]')).componentInstance.copy).toBe(
'1234567890'
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import {
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
ViewChild
} from '@angular/core';
import { DotMessageService } from '@services/dot-message/dot-messages.service';
import { DotMenuService } from '@services/dot-menu.service';
import { FieldDragDropService } from '../fields/service';
Expand All @@ -8,6 +17,7 @@ import { DotEventsService } from '@services/dot-events/dot-events.service';
import { DotCMSContentType } from '@dotcms/dotcms-models';
import { DotCurrentUserService } from '@services/dot-current-user/dot-current-user.service';
import { Observable } from 'rxjs';
import { Inplace } from 'primeng/inplace';

@Component({
selector: 'dot-content-type-layout',
Expand All @@ -17,9 +27,14 @@ import { Observable } from 'rxjs';
export class ContentTypesLayoutComponent implements OnChanges, OnInit {
@Input() contentType: DotCMSContentType;
@Output() openEditDialog: EventEmitter<unknown> = new EventEmitter();
@Output() changeContentTypeName: EventEmitter<string> = new EventEmitter();
@ViewChild('contentTypeNameInput') contentTypeNameInput: ElementRef;
@ViewChild('contentTypeInlineEdit') contentTypeInlineEdit: Inplace;

permissionURL: string;
pushHistoryURL: string;
relationshipURL: string;
contentTypeNameInputSize: string;
showPermissionsTab: Observable<boolean>;

actions: MenuItem[];
Expand Down Expand Up @@ -52,10 +67,32 @@ export class ContentTypesLayoutComponent implements OnChanges, OnInit {
}
}

/**
* Emits add-row event to add new row
*
* @memberof ContentTypesLayoutComponent
*/

fireAddRowEvent(): void {
this.dotEventsService.notify('add-row');
}

/**
* Emits new name to parent component and close Edit Inline mode
*
* @memberof ContentTypesLayoutComponent
*/
fireChangeName(): void {
const contentTypeName = this.contentTypeNameInput.nativeElement.value.trim();
this.changeContentTypeName.emit(contentTypeName);
this.contentType.name = contentTypeName;
this.contentTypeInlineEdit.deactivate();
}

editInlineActivate(event): void {
this.contentTypeNameInputSize = event.target.offsetWidth;
}

private loadActions(): void {
this.actions = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<dot-content-type-layout
[contentType]="data"
(openEditDialog)="startFormDialog()"
(changeContentTypeName)="editContentTypeName($event)"
*ngIf="isEditMode()"
>
<dot-content-type-fields-drop-zone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class TestContentTypeFieldsDropZoneComponent {
class TestContentTypeLayoutComponent {
@Input() contentType: DotCMSContentType;
@Output() openEditDialog: EventEmitter<any> = new EventEmitter();
@Output() changeContentTypeName: EventEmitter<string> = new EventEmitter();
}

@Component({
Expand Down Expand Up @@ -752,6 +753,32 @@ describe('DotContentTypesEditComponent', () => {
expect(dotHttpErrorManagerService.handle).toHaveBeenCalledTimes(1);
});

it('should update Content Type name on dot-content-type-layout event', () => {
const responseContentType = Object.assign({}, fakeContentType, {
name: 'CT changed'
});

spyOn(crudService, 'putData').and.returnValue(of(responseContentType));

const contentTypeLayout = de.query(By.css('dot-content-type-layout'));
contentTypeLayout.triggerEventHandler('changeContentTypeName', 'CT changed');

const replacedWorkflowsPropContentType = {
...responseContentType
};

replacedWorkflowsPropContentType['workflow'] = fakeContentType.workflows.map(
(workflow) => workflow.id
);
delete replacedWorkflowsPropContentType.workflows;

expect(crudService.putData).toHaveBeenCalledWith(
'v1/contenttype/id/1234567890',
replacedWorkflowsPropContentType
);
expect(comp.data).toEqual(responseContentType, 'set data with response');
});

describe('update', () => {
let contentTypeForm: DebugElement;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ export class DotContentTypesEditComponent implements OnInit, OnDestroy {
this.setEditContentletDialogOptions();
}

/**
* Set updated name on Content Type and send a request to save it
* @param {string} name
*
* @memberof DotContentTypesEditComponent
*/
editContentTypeName(name: string): void {
const updatedContentType = { ...this.data, name };
this.updateContentType(updatedContentType);
}

/**
* Set the icon, labels and placeholder in the template
* @memberof DotContentTypesEditComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { OverlayPanelModule } from 'primeng/overlaypanel';
import { RadioButtonModule } from 'primeng/radiobutton';
import { SplitButtonModule } from 'primeng/splitbutton';
import { TabViewModule } from 'primeng/tabview';
import { InplaceModule } from 'primeng/inplace';
import { DotRelationshipTreeModule } from '@components/dot-relationship-tree/dot-relationship-tree.module';
import { DotPortletBoxModule } from '@components/dot-portlet-base/components/dot-portlet-box/dot-portlet-box.module';
import { DotMdIconSelectorModule } from '@dotcms/app/view/components/_common/dot-md-icon-selector/dot-md-icon-selector.module';
Expand Down Expand Up @@ -129,6 +130,7 @@ import { DotMdIconSelectorModule } from '@dotcms/app/view/components/_common/dot
DropdownModule,
FormsModule,
IFrameModule,
InplaceModule,
InputTextModule,
MultiSelectModule,
OverlayPanelModule,
Expand Down

0 comments on commit cbff51d

Please sign in to comment.