-
Notifications
You must be signed in to change notification settings - Fork 466
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
Added Select and Radio fields. Added basic tests on both components. β¦ #26461
Added Select and Radio fields. Added basic tests on both components. β¦ #26461
Conversation
β¦Changed formData structure and architecture
β¦ first option as value in select and radio field
core-web/libs/edit-content/src/lib/enums/dot-edit-content-field.enum.ts
Outdated
Show resolved
Hide resolved
core-web/libs/edit-content/src/lib/feature/edit-content/edit-content.layout.component.ts
Outdated
Show resolved
Hide resolved
...tent/src/lib/fields/dot-edit-content-radio-field/dot-edit-content-radio-field.component.html
Outdated
Show resolved
Hide resolved
<div class="field-checkbox" *ngFor="let option of options"> | ||
<p-radioButton | ||
[inputId]="option.value" | ||
[value]="option.value" | ||
[formControlName]="field.variable"></p-radioButton> | ||
<label class="ml-2" [for]="option.value">{{ option.label }}</label> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After you do the necessary changes, remember to make use of the :host
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can bind the class .field
to the host.
...t/src/lib/fields/dot-edit-content-radio-field/dot-edit-content-radio-field.component.spec.ts
Outdated
Show resolved
Hide resolved
...nt/src/lib/fields/dot-edit-content-select-field/dot-edit-content-select-field.component.html
Outdated
Show resolved
Hide resolved
let newValue: string | number | boolean = value; | ||
if ( | ||
dataType === DotEditContentFieldDataType.INTEGER || | ||
dataType === DotEditContentFieldDataType.FLOAT | ||
) { | ||
newValue = Number(value); | ||
} | ||
|
||
if (dataType === DotEditContentFieldDataType.BOOL) { | ||
newValue = value === 'true'; | ||
} | ||
|
||
return { label, value: newValue }; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we do all this process even if the label is the same as value?
...edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
Outdated
Show resolved
Hide resolved
...s/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.ts
Outdated
Show resolved
Hide resolved
...dit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.spec.ts
Outdated
Show resolved
Hide resolved
@@ -211,7 +221,7 @@ describe('DotFormComponent', () => { | |||
describe('saveContent', () => { | |||
it('should emit the form value through the `formSubmit` event', () => { | |||
const component = spectator.component; | |||
component.formData = LAYOUT_MOCK; | |||
component.formData = CONTENT_FORM_DATA_MOCK; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, that is the reason you need to add the line 225.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
@@ -0,0 +1,35 @@ | |||
import { DotEditContentFieldDataType } from '../enums/dot-edit-content-field.enum'; | |||
|
|||
export const mapOptions = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a test for this util to be sure it do the works?
@Component({ | ||
selector: 'dot-edit-content-select-field', | ||
standalone: true, | ||
imports: [CommonModule, DropdownModule, ReactiveFormsModule, DotFieldRequiredDirective], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add only the necessary from CommonModule
|
||
const createComponent = createComponentFactory({ | ||
component: DotEditContentSelectFieldComponent, | ||
imports: [CommonModule, DropdownModule, ReactiveFormsModule, DotFieldRequiredDirective], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You dont need add imports. Is a Standalone component.
...src/lib/fields/dot-edit-content-select-field/dot-edit-content-select-field.component.spec.ts
Outdated
Show resolved
Hide resolved
...src/lib/fields/dot-edit-content-select-field/dot-edit-content-select-field.component.spec.ts
Show resolved
Hide resolved
...edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
Outdated
Show resolved
Hide resolved
...dit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.spec.ts
Outdated
Show resolved
Hide resolved
<div class="field-checkbox" *ngFor="let option of options"> | ||
<p-radioButton | ||
[inputId]="option.value" | ||
[value]="option.value" | ||
[formControlName]="field.variable"></p-radioButton> | ||
<label class="ml-2" [for]="option.value">{{ option.label }}</label> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can bind the class .field
to the host.
...t/src/lib/fields/dot-edit-content-radio-field/dot-edit-content-radio-field.component.spec.ts
Outdated
Show resolved
Hide resolved
@@ -1,18 +1,36 @@ | |||
<ng-container [ngSwitch]="field.fieldType"> | |||
<div class="field" *ngSwitchCase="'Text'"> | |||
<div class="field"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can ged rid of this div
and use the :host
]; | ||
spectator.setInput('field', RADIO_FIELD_BOOLEAN_MOCK); | ||
spectator.detectChanges(); | ||
expect(spectator.component.options).toEqual(expectedList); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
expect(spectator.component.options).toEqual(expectedList); | ||
}); | ||
|
||
it('should have a options array as radio with Boolean dataType', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you can group data types test under a describe
and other as well.
]; | ||
spectator.setInput('field', SELECT_FIELD_TEXT_MOCK); | ||
spectator.detectChanges(); | ||
expect(spectator.component.options).toEqual(expectedList); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests the DOM, for example here you need to test that you are passing the options and other attr to the <p-dropdown>
...nt/src/lib/fields/dot-edit-content-select-field/dot-edit-content-select-field.component.html
Outdated
Show resolved
Hide resolved
<dot-edit-content-text-field | ||
*ngSwitchCase="fieldTypes.TEXT" | ||
[field]="field" | ||
[attr.data-testId]="'field-' + field.variable"></dot-edit-content-text-field> | ||
[attr.data-testId]="'field-' + field.variable" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
YES!!!
@@ -211,7 +221,7 @@ describe('DotFormComponent', () => { | |||
describe('saveContent', () => { | |||
it('should emit the form value through the `formSubmit` event', () => { | |||
const component = spectator.component; | |||
component.formData = LAYOUT_MOCK; | |||
component.formData = CONTENT_FORM_DATA_MOCK; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
...dit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.spec.ts
Outdated
Show resolved
Hide resolved
import { DotMessagePipe } from '@dotcms/ui'; | ||
|
||
import { EditContentFormData } from '../../interfaces/dot-edit-content-form.interface'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing this one.
@@ -31,7 +32,7 @@ import { DotEditContentFieldComponent } from '../dot-edit-content-field/dot-edit | |||
changeDetection: ChangeDetectionStrategy.OnPush | |||
}) | |||
export class DotEditContentFormComponent implements OnInit { | |||
@Input() formData: DotCMSContentTypeLayoutRow[] = []; | |||
@Input() formData!: EditContentFormData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
...tent/src/lib/fields/dot-edit-content-radio-field/dot-edit-content-radio-field.component.html
Outdated
Show resolved
Hide resolved
this.options = mapSelectableOptions(this.field.values || '', this.field.dataType); | ||
|
||
if (this.formControl.value === null) { | ||
this.formControl.setValue(this.options[0]?.value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any thoughts on this?
beforeEach(() => { | ||
spectator = createComponent(); | ||
}); | ||
describe('DotEditContentRadioFieldComponent with Text DataType', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
]; | ||
spectator.setInput('field', RADIO_FIELD_TEXT_MOCK); | ||
spectator.detectChanges(); | ||
expect(spectator.component.options).toEqual(expectedList); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be testing the DOM like:
expect(spectator.component.options).toEqual(expectedList); | |
expect(spectator.query(Dropdown).options).toEqual(expectedList); |
expect(spanElement).toBeTruthy(); | ||
expect(spanElement.textContent).toEqual('Option 1'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(spanElement).toBeTruthy(); | |
expect(spanElement.textContent).toEqual('Option 1'); | |
expect(spanElement.textContent).toEqual('Option 1'); |
Because only this will tests both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should unify /interfaces
and /enums
in /models
that's what we have been doing.
...src/lib/fields/dot-edit-content-select-field/dot-edit-content-select-field.component.spec.ts
Outdated
Show resolved
Hide resolved
...edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
Outdated
Show resolved
Hide resolved
DotEditContentSelectFieldComponent, | ||
DotEditContentRadioFieldComponent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove from here and import and export these in the DotEditContentFieldsModule
β¦-form' of https://github.com/dotCMS/core into 26448-edit-content-render-radio-and-select-field-to-the-form
return this.dotEditContentService.getContentTypeFormData(contentType); | ||
return this.dotEditContentService | ||
.getContentTypeFormData(contentType) | ||
.pipe(map((res) => ({ values: { ...contentData }, layout: res }))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.pipe(map((res) => ({ values: { ...contentData }, layout: res }))); | |
.pipe(map((res) => ({ contentlet: { ...contentData }, layout: res }))); |
According to our talk today.
@@ -0,0 +1,7 @@ | |||
<div class="field-checkbox" *ngFor="let option of options"> | |||
<p-radioButton | |||
[inputId]="option.value" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...t/src/lib/fields/dot-edit-content-radio-field/dot-edit-content-radio-field.component.spec.ts
Outdated
Show resolved
Hide resolved
expectedList.forEach((option) => { | ||
expect( | ||
spectator | ||
.queryAll(RadioButton) | ||
.find((radioOption) => radioOption.value === option.value) | ||
).toBeTruthy(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expectedList.forEach((option) => { | |
expect( | |
spectator | |
.queryAll(RadioButton) | |
.find((radioOption) => radioOption.value === option.value) | |
).toBeTruthy(); | |
}); | |
expect(spectator.queryAll(RadioButton).map((radio) => radio.value)).toEqual([ | |
'one', | |
'two' | |
]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, we need to test the inputId
and the label with the for
(to test the semantic relationship) and the label innerText
.
The same applies for all other cases but labels, labels can be tested just once.
it('should render radio options', () => { | ||
spectator.setInput('field', RADIO_FIELD_TEXT_MOCK); | ||
spectator.detectComponentChanges(); | ||
|
||
const radioOptions = spectator.queryAll('p-radiobutton'); | ||
expect(radioOptions.length).toBe(2); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we test the one above correctly, we might not need this one.
spectator.component.formControl.setValue('one'); | ||
spectator.detectComponentChanges(); | ||
|
||
expect(spectator.component.formControl.value).toEqual('one'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you are doing this
spectator.component.formControl.setValue('one');
It doesn't make sense do this:
expect(spectator.component.formControl.value).toEqual('one');
const inputChecked = spectator.queryAll('div.p-radiobutton-checked'); | ||
expect(inputChecked.length).toBe(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PrimeNG is a library that changes the CSS classes and HTML markup in their updates frequently.
Using a selector like div.p-radiobutton-checked
for the test might breaks the tests in an update without us know it... we can check the checked
property of the component instance.
const inputChecked = spectator.queryAll('div.p-radiobutton-checked'); | |
expect(inputChecked.length).toBe(1); | |
const inputChecked = spectator.queryAll(RadioButton).map(radio => radio.checked); | |
expect(inputChecked.length).toBe(1); |
return value; | ||
}; | ||
|
||
export const createSingleSelectableFieldOptions = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const createSingleSelectableFieldOptions = ( | |
export const getSingleSelectableFieldOptions = ( |
SonarQube Quality Gate |
For now we have an issue with the radio buttons. |
π€ Generated by Copilot at 22337ee
Summary
ππ§π
This pull request adds new components and interfaces to improve the edit content form feature. It adds
dot-edit-content-select-field
anddot-edit-content-radio-field
components to handle select and radio fields, andEditContentFormData
interface to define the data for the form. It also refactors and updates the existing components and tests to use the new interface and observables.Walkthrough
EditContentFormData
interface to define the data for the edit content form (link)edit-content-layout
component to fetch and transform the data for the edit content form usingDotEditContentService
andmap
operator (link, link, link, link, link)edit-content-form
component to acceptEditContentFormData
as input and create form controls with initial values (link, link, link, link)edit-content-field
component to render select and radio fields using new components (link, link)dot-edit-content-select-field
component to render a select field usingp-dropdown
component andmapOptions
function (link, link, link, link)dot-edit-content-radio-field
component to render a radio field usingp-radioButton
component andmapOptions
function (link, link, link, link)edit-content-form
andedit-content-layout
components to uselayout
property offormData
(link, link)edit-content-form
andedit-content-layout
components to mockEditContentFormData
and test form initialization and emission (link, link, link, link, link, link, link)DotFieldRequiredDirective
toedit-content-field
module and use it in select and radio field templates to add required indicator to field label (link, link, link)Additional Info
** any additional useful context or info **
Screenshots