Skip to content

Commit

Permalink
feat(edit-content): Implement Key/Value Field #26904 (#27465)
Browse files Browse the repository at this point in the history
* dev: start refactor

* dev: update DotKeyValueTableInputRow Component

* dev: move DotKeyValue Component to ui library

* dev: rename dot-key-value-ng to dot-key-value

* clean up

* revert: renaming

* dev: fix DotKeyValueTableInputRow Component tests

* dev: cover tests

* dev: cover tests v2

* clean up

* fix: SonarQube Consistency issue

* clean up v2

* test: feedback v1

* dev: feedback v2

* clean up
  • Loading branch information
rjvelazco authored and dsolistorres committed Feb 9, 2024
1 parent 0968048 commit 089837e
Show file tree
Hide file tree
Showing 43 changed files with 1,540 additions and 1,336 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@
(click)="goToApps(apps.key)"
pButton
data-testid="cancelBtn"
type="button"
></button>
type="button"></button>
<button
[label]="'Save' | dm"
[disabled]="!formValid"
(click)="onSubmit()"
pButton
data-testid="saveBtn"
type="button"
></button>
type="button"></button>
</div>
</div>
</div>
Expand All @@ -29,17 +27,14 @@
[appConfigured]="apps.sites[0].configured"
[formFields]="formFields"
(data)="formData = $event"
(valid)="formValid = $event"
></dot-apps-configuration-detail-form>
(valid)="formValid = $event"></dot-apps-configuration-detail-form>
<div *ngIf="apps.allowExtraParams">
<h5>{{ 'apps.custom.properties' | dm }}</h5>
<dot-key-value-ng
[autoFocus]="false"
[showHiddenField]="true"
[variables]="dynamicVariables"
(delete)="deleteDynamicVariable($event)"
(save)="saveDynamicVariable($event)"
></dot-key-value-ng>
(updatedList)="updateVariables($event)" />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ class MockDotKeyValueComponent {
@Input() autoFocus: boolean;
@Input() showHiddenField: string;
@Input() variables: DotKeyValue[];
@Output() delete = new EventEmitter<DotKeyValue>();
@Output() save = new EventEmitter<DotKeyValue>();
@Output() updatedList = new EventEmitter<DotKeyValue[]>();
}

@Component({
Expand Down Expand Up @@ -334,15 +333,14 @@ describe('DotAppsConfigurationDetailComponent', () => {
it('should update local collection with saved value', () => {
const variableEmitted = { key: 'custom', hidden: false, value: 'changed' };
const keyValue = fixture.debugElement.query(By.css('dot-key-value-ng'));
keyValue.componentInstance.save.emit(variableEmitted);
keyValue.componentInstance.updatedList.emit([variableEmitted]);
expect(component.dynamicVariables[0]).toEqual(variableEmitted);
expect(component.dynamicVariables.length).toEqual(1);
});

it('should delete from local collection', () => {
const variableEmitted = { key: 'custom', hidden: false, value: 'test' };
const keyValue = fixture.debugElement.query(By.css('dot-key-value-ng'));
keyValue.componentInstance.delete.emit(variableEmitted);
keyValue.componentInstance.updatedList.emit([]);
expect(component.dynamicVariables.length).toEqual(0);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import * as _ from 'lodash';

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { pluck, take } from 'rxjs/operators';

import { DotKeyValueUtil } from '@components/dot-key-value-ng/util/dot-key-value-util';
import { DotAppsService } from '@dotcms/app/api/services/dot-apps/dot-apps.service';
import { DotRouterService } from '@dotcms/data-access';
import { DotApp, DotAppsSaveData, DotAppsSecret } from '@dotcms/dotcms-models';
Expand All @@ -19,7 +16,7 @@ import { DotKeyValue } from '@shared/models/dot-key-value-ng/dot-key-value-ng.mo
export class DotAppsConfigurationDetailComponent implements OnInit {
apps: DotApp;

dynamicVariables: DotKeyValue[];
dynamicVariables: DotKeyValue[] = [];
formData: { [key: string]: string };
formFields: DotAppsSecret[];
formValid = false;
Expand Down Expand Up @@ -69,32 +66,13 @@ export class DotAppsConfigurationDetailComponent implements OnInit {
}

/**
* Handle Save event doing if new a prepend, otherwise a replace
* to the local collection
* @param {DotKeyValue} variable
* @memberof DotAppsConfigurationDetailComponent
*/
saveDynamicVariable(variable: DotKeyValue): void {
const indexChanged = DotKeyValueUtil.getVariableIndexChanged(
variable,
this.dynamicVariables
);
if (indexChanged !== null) {
this.dynamicVariables[indexChanged] = _.cloneDeep(variable);
} else {
this.dynamicVariables = [variable, ...this.dynamicVariables];
}
}

/**
* Handle Delete event doing a removing the variable from the local collection
* @param {DotKeyValue} variable
*
*
* @param {DotKeyValue[]} variable
* @memberof DotAppsConfigurationDetailComponent
*/
deleteDynamicVariable(variable: DotKeyValue): void {
this.dynamicVariables = this.dynamicVariables.filter(
(item: DotKeyValue) => item.key !== variable.key
);
updateVariables(variables: DotKeyValue[]): void {
this.dynamicVariables = [...variables];
}

private getTransformedFormData(): DotAppsSaveData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { NgModule } from '@angular/core';

import { ButtonModule } from 'primeng/button';

import { DotKeyValueModule } from '@components/dot-key-value-ng/dot-key-value-ng.module';
import { DotAppsService } from '@dotcms/app/api/services/dot-apps/dot-apps.service';
import { DotCopyButtonComponent, DotMessagePipe } from '@dotcms/ui';
import { DotCopyButtonComponent, DotKeyValueComponent, DotMessagePipe } from '@dotcms/ui';
import { DotPipesModule } from '@pipes/dot-pipes.module';

import { DotAppsConfigurationDetailFormModule } from './dot-apps-configuration-detail-form/dot-apps-configuration-detail-form.module';
Expand All @@ -18,7 +17,7 @@ import { DotAppsConfigurationHeaderModule } from '../dot-apps-configuration-head
imports: [
ButtonModule,
CommonModule,
DotKeyValueModule,
DotKeyValueComponent,
DotCopyButtonComponent,
DotAppsConfigurationHeaderModule,
DotAppsConfigurationDetailFormModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@
class="content-type-fields-drop-zone__container"
[dragulaModel]="fieldRows"
[attr.disabled]="loading"
dragula="fields-row-bag"
>
dragula="fields-row-bag">
<ng-template [ngForOf]="fieldRows" ngFor let-row let-i="index">
<dot-content-type-fields-row
*ngIf="row.columns && row.columns.length; else tab_container"
[fieldRow]="row"
(editField)="editFieldHandler($event)"
(removeField)="removeField($event)"
(removeRow)="removeFieldRow($event, i)"
>
(removeRow)="removeFieldRow($event, i)">
</dot-content-type-fields-row>

<ng-template #tab_container>
<dot-content-type-fields-tab
class="row-header__drag"
[fieldTab]="row"
(editTab)="saveFieldsHandler($event)"
(removeTab)="removeTab($event, i)"
>
(removeTab)="removeTab($event, i)">
</dot-content-type-fields-tab>
</ng-template>
</ng-template>
Expand All @@ -37,8 +34,7 @@
[hideButtons]="hideButtons"
[header]="currentFieldType?.label"
(hide)="removeFieldsWithoutId()"
width="45rem"
>
width="45rem">
<p-tabView [(activeIndex)]="activeTab" (onChange)="handleTabChange($event.index)">
<p-tabPanel [header]="'contenttypes.dropzone.tab.overview' | dm">
<dot-convert-to-block-info
Expand All @@ -48,16 +44,14 @@
"
[currentFieldType]="currentFieldType"
[currentField]="currentField"
(action)="scrollTo($event)"
></dot-convert-to-block-info>
(action)="scrollTo($event)"></dot-convert-to-block-info>
<div class="wrapper">
<dot-content-type-fields-properties-form
#fieldPropertiesForm
[formFieldData]="currentField"
[contentType]="contentType"
(saveField)="saveFieldsHandler($event)"
(valid)="setDialogOkButtonState($event)"
>
(valid)="setDialogOkButtonState($event)">
</dot-content-type-fields-properties-form>
<dot-convert-wysiwyg-to-block
*ngIf="
Expand All @@ -66,33 +60,31 @@
'com.dotcms.contenttype.model.field.ImmutableWysiwygField'
"
[currentFieldType]="currentFieldType"
(convert)="convertWysiwygToBlock($event)"
></dot-convert-wysiwyg-to-block>
(convert)="convertWysiwygToBlock($event)"></dot-convert-wysiwyg-to-block>
</div>
</p-tabPanel>

<p-tabPanel
#panel
*ngIf="!!currentField?.id && isBlockEditorField"
[header]="'Settings'"
[disabled]="!currentField?.id"
>
[disabled]="!currentField?.id">
<dot-block-editor-settings
[field]="currentField"
[isVisible]="panel._selected"
(changeControls)="changesDialogActions($event)"
(save)="toggleDialog()"
(valid)="setDialogOkButtonState($event)"
></dot-block-editor-settings>
(valid)="setDialogOkButtonState($event)"></dot-block-editor-settings>
</p-tabPanel>

<p-tabPanel
#panel
[header]="'contenttypes.dropzone.tab.variables' | dm"
[disabled]="!currentField?.id"
>
[disabled]="!currentField?.id">
<ng-template pTemplate="content">
<dot-content-type-fields-variables
[field]="currentField"
></dot-content-type-fields-variables>
[showTable]="panel.selected"
[field]="currentField"></dot-content-type-fields-variables>
</ng-template>
</p-tabPanel>
</p-tabView>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<dot-key-value-ng
*ngIf="showTable"
[variables]="fieldVariables"
(delete)="deleteExistingVariable($event)"
(save)="updateExistingVariable($event)"
></dot-key-value-ng>
(delete)="deleteFieldVariable($event)"
(save)="updateFieldVariable($event)"
(update)="updateFieldVariable($event.variable)" />
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { of } from 'rxjs';

import { Component, DebugElement } from '@angular/core';
import { ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { DotKeyValueModule } from '@components/dot-key-value-ng/dot-key-value-ng.module';
import { DOTTestBed } from '@dotcms/app/test/dot-test-bed';
import { DotMessageDisplayService } from '@dotcms/data-access';
import { LoginService } from '@dotcms/dotcms-js';
import { DotCMSContentTypeField, DotFieldVariable } from '@dotcms/dotcms-models';
import { DotKeyValueComponent } from '@dotcms/ui';
import {
dotcmsContentTypeFieldBasicMock,
DotFieldVariablesServiceMock,
Expand Down Expand Up @@ -46,7 +44,7 @@ describe('DotContentTypeFieldsVariablesComponent', () => {
beforeEach(() => {
DOTTestBed.configureTestingModule({
declarations: [TestHostComponent, DotContentTypeFieldsVariablesComponent],
imports: [DotKeyValueModule],
imports: [DotKeyValueComponent],
providers: [
{ provide: LoginService, useClass: LoginServiceMock },
{
Expand Down Expand Up @@ -77,25 +75,47 @@ describe('DotContentTypeFieldsVariablesComponent', () => {

fixtureHost.detectChanges();

const dotKeyValue = de.query(By.css('dot-key-value-ng')).componentInstance;
dotKeyValue.save.emit(response);
const dotKeyValue = de.query(By.css('dot-key-value-ng'));
dotKeyValue.triggerEventHandler('save', response);

expect(dotFieldVariableService.save).toHaveBeenCalledWith(
comp.field,
mockFieldVariables[0]
);
expect(comp.fieldVariables[0]).toEqual(mockFieldVariables[0]);
});

it('should update variable a variable', () => {
const variable = {
...mockFieldVariables[0],
value: 'test'
};

spyOn(dotFieldVariableService, 'save').and.returnValue(of(variable));

fixtureHost.detectChanges();

const dotKeyValue = de.query(By.css('dot-key-value-ng'));
dotKeyValue.triggerEventHandler('update', {
variable,
oldVariable: mockFieldVariables[0]
});

expect(dotFieldVariableService.save).toHaveBeenCalledWith(comp.field, variable);

expect(comp.fieldVariables[0]).toEqual(variable);
});

it('should delete a variable from the server', () => {
const variableToDelete = mockFieldVariables[0];
spyOn<any>(dotFieldVariableService, 'delete').and.returnValue(of([]));
spyOn<DotFieldVariablesService>(dotFieldVariableService, 'delete').and.returnValue(of([]));
const deletedCollection = mockFieldVariables.filter(
(item: DotFieldVariable) => variableToDelete.key !== item.key
);
fixtureHost.detectChanges();

const dotKeyValue = de.query(By.css('dot-key-value-ng')).componentInstance;
dotKeyValue.delete.emit(variableToDelete);
const dotKeyValue = de.query(By.css('dot-key-value-ng'));
dotKeyValue.triggerEventHandler('delete', variableToDelete);

expect(dotFieldVariableService.delete).toHaveBeenCalledWith(comp.field, variableToDelete);
expect(comp.fieldVariables).toEqual(deletedCollection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { DotFieldVariablesService } from './services/dot-field-variables.service
templateUrl: './dot-content-type-fields-variables.component.html'
})
export class DotContentTypeFieldsVariablesComponent implements OnChanges, OnDestroy {
@Input()
field: DotCMSContentTypeField;
@Input() field: DotCMSContentTypeField;
@Input() showTable: boolean = true;

fieldVariables: DotFieldVariable[] = [];
blackList = {
Expand All @@ -36,7 +36,7 @@ export class DotContentTypeFieldsVariablesComponent implements OnChanges, OnDest
) {}

ngOnChanges(changes: SimpleChanges): void {
if (changes.field.currentValue) {
if (changes.field?.currentValue) {
this.initTableData();
}
}
Expand All @@ -51,7 +51,7 @@ export class DotContentTypeFieldsVariablesComponent implements OnChanges, OnDest
* @param {DotKeyValue} variable
* @memberof DotContentTypeFieldsVariablesComponent
*/
deleteExistingVariable(variable: DotKeyValue): void {
deleteFieldVariable(variable: DotKeyValue): void {
this.fieldVariablesService
.delete(this.field, variable)
.pipe(take(1))
Expand All @@ -72,7 +72,7 @@ export class DotContentTypeFieldsVariablesComponent implements OnChanges, OnDest
* @param {DotKeyValue} variable
* @memberof DotContentTypeFieldsVariablesComponent
*/
updateExistingVariable(variable: DotKeyValue): void {
updateFieldVariable(variable: DotKeyValue): void {
this.fieldVariablesService
.save(this.field, variable)
.pipe(take(1))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { NgIf } from '@angular/common';
import { NgModule } from '@angular/core';

import { DotKeyValueModule } from '@components/dot-key-value-ng/dot-key-value-ng.module';
import { DotKeyValueComponent } from '@dotcms/ui';

import { DotContentTypeFieldsVariablesComponent } from './dot-content-type-fields-variables.component';
import { DotFieldVariablesService } from './services/dot-field-variables.service';

@NgModule({
imports: [DotKeyValueModule],
imports: [NgIf, DotKeyValueComponent],
exports: [DotContentTypeFieldsVariablesComponent],
providers: [DotFieldVariablesService],
declarations: [DotContentTypeFieldsVariablesComponent]
Expand Down
Loading

0 comments on commit 089837e

Please sign in to comment.