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

[sitecore-jss] [sitecore-jss-angular] Default Placeholder Content for empty fields in editMode metadata #1916

Merged
merged 32 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
709b49c
empty editing placeholder for rich text field
yavorsk Sep 2, 2024
8b816d7
minor updates
yavorsk Sep 2, 2024
3926a04
empty editing placeholder for date and text fields
yavorsk Sep 2, 2024
4688d9e
date, text directive fixes
yavorsk Sep 2, 2024
3f2d8d0
refactoring
yavorsk Sep 2, 2024
fa029aa
remove consolelog
yavorsk Sep 2, 2024
5a34c4a
empty field placeholder for link fields
yavorsk Sep 2, 2024
c8de103
empty field editing placeholder for image field
yavorsk Sep 2, 2024
195dc5b
unit tests for image, text, richtext, date
yavorsk Sep 3, 2024
e5c106d
link directives unit tests
yavorsk Sep 3, 2024
090b004
remove console.log
yavorsk Sep 3, 2024
cd22f89
fix lint errors
yavorsk Sep 4, 2024
48e48fe
minor naming fixes
yavorsk Sep 4, 2024
1b3e562
minor fix
yavorsk Sep 4, 2024
5396fd0
add Date type to possible types for GenericFieldValue base package ty…
yavorsk Sep 4, 2024
aa4d3d3
update changelog
yavorsk Sep 4, 2024
0558e8d
add unit test for invalid Date object
yavorsk Sep 5, 2024
1a10142
fix lint error
yavorsk Sep 5, 2024
194abcd
rename default placeholder components
yavorsk Sep 5, 2024
6400715
additional renaming
yavorsk Sep 5, 2024
c82be95
fix lint issue
yavorsk Sep 5, 2024
1c2a3cd
fix lint issue
yavorsk Sep 5, 2024
e50b520
add jsdoc comments
yavorsk Sep 5, 2024
afa5f29
updates to changelog
yavorsk Sep 5, 2024
29a593e
reafactoring: add property defaultFieldEditingComponent to field dire…
yavorsk Sep 5, 2024
42c071d
additional tsdoc comments
yavorsk Sep 5, 2024
6c1c7c6
fix lint issue
yavorsk Sep 5, 2024
c8edacf
remove unnecessary function
yavorsk Sep 5, 2024
18f6a11
base directive unit tests
yavorsk Sep 5, 2024
1dc55b2
fix lint issue
yavorsk Sep 5, 2024
a670a60
Update packages/sitecore-jss-angular/src/components/base-field.direct…
illiakovalenko Sep 6, 2024
f505d90
Minor renaming
illiakovalenko Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ Our versioning strategy is as follows:
* Promo component ([#1897](https://github.com/Sitecore/jss/pull/1897))
* PageContent component ([#1905](https://github.com/Sitecore/jss/pull/1905))
* Navigation component ([#1894](https://github.com/Sitecore/jss/pull/1894))
* `[sitecore-jss]``[sitecore-jss-angular]` Default Placeholder Content for empty fields in editMode metadata - in edit mode metadata in Pages, angular package field directives will render default or custom placeholder content if the provided field is empty; ([#1916](https://github.com/Sitecore/jss/pull/1916))
* custom placeholder content can be provided to field directives by passing the corresponding input:
- `scDateEmptyFieldEditingTemplate` for _scDate_
- `scGenericLinkEmptyFieldEditingTemplate` for _scGenericLink_
- `scImageEmptyFieldEditingTemplate` for _scImage_
- `scLinkEmptyFieldEditingTemplate` for _scLink_
- `scRichTextEmptyFieldEditingTemplate` for _scRichText_
- `scRouterLinkEmptyFieldEditingTemplate` for _scRouterLink_
- `scTextEmptyFieldEditingTemplate` for _scText_
* `[sitecore-jss]` GenericFieldValue model is updated to accept Date type ([#1916](https://github.com/Sitecore/jss/pull/1916))

### 🛠 Breaking Change

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { Component, DebugElement, Input, TemplateRef } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { TextField } from './rendering-field';
import { TestBaseDirective } from '../test-data/test-base.directive';

@Component({
selector: 'test-base',
template: `
<span *scTestBase="field; editable: editable"></span>
`,
})
class TestComponent {
@Input() field: TextField;
@Input() editable = true;
}

const emptyTextFieldEditingTemplateId = 'emptyTextFieldEditingTemplate';
const emptyTextFieldEditingTemplate =
'<span>[This is a *custom* empty field component for text]</span>';

@Component({
selector: 'test-base-template',
template: `
<span
*scTestBase="
field;
editable: editable;
emptyFieldEditingTemplate: ${emptyTextFieldEditingTemplateId}
"
></span>

<ng-template #${emptyTextFieldEditingTemplateId}>
${emptyTextFieldEditingTemplate}
</ng-template>
`,
})
class TestEmptyTemplateComponent {
@Input() field: TextField;
@Input() emptyFieldEditingTemplate: TemplateRef<unknown>;
@Input() editable = true;
}

describe('<span *scTestBase />', () => {
let fixture: ComponentFixture<TestComponent>;
let de: DebugElement;
let deSpan: DebugElement;
let comp: TestComponent;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestBaseDirective, TestComponent, TestEmptyTemplateComponent],
});

fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

de = fixture.debugElement;
deSpan = de.query(By.css('span'));
comp = fixture.componentInstance;
});

describe('edit mode chromes', () => {
it('should render field value if it is present', () => {
const field: { [prop: string]: unknown } = {
value: 'value',
};
comp.field = field;
fixture.detectChanges();

const rendered = deSpan.nativeElement.innerHTML;
expect(rendered).toBe('value');
});

it('should render field editable if it is present', () => {
const field: { [prop: string]: unknown } = {
value: 'value',
editable: 'editable',
};
comp.field = field;
fixture.detectChanges();

const rendered = deSpan.nativeElement.innerHTML;
expect(rendered).toBe(field.editable);
});

it('should render nothing if field.editable and value are missing', () => {
const field: { [prop: string]: unknown } = {
value: '',
};
comp.field = field;
fixture.detectChanges();

const rendered = deSpan.nativeElement.innerHTML;
expect(rendered).toBe('');
});
});

describe('edit mode metadata', () => {
const testMetadata = {
contextItem: {
id: '{09A07660-6834-476C-B93B-584248D3003B}',
language: 'en',
revision: 'a0b36ce0a7db49418edf90eb9621e145',
version: 1,
},
fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}',
fieldType: 'single-line',
rawValue: 'Test1',
};

it('should render field value if field value is not empty', () => {
const field: { [prop: string]: unknown } = {
value: 'value',
metadata: testMetadata,
};
comp.field = field;
fixture.detectChanges();

const rendered = deSpan.nativeElement.innerHTML;
expect(rendered).toBe('value');
});

describe('field value is empty', () => {
it('should render default empty editing component if custom is not provided', () => {
const field = {
value: '',
metadata: testMetadata,
};
comp.field = field;
fixture.detectChanges();

const rendered = de.nativeElement.innerHTML;
expect(rendered).toContain('<span>[No text in field]</span>');
});

it('should render custom empty editing template if provided', () => {
fixture = TestBed.createComponent(TestEmptyTemplateComponent);
fixture.detectChanges();

de = fixture.debugElement;
comp = fixture.componentInstance;

const field = {
value: '',
metadata: testMetadata,
};
comp.field = field;
fixture.detectChanges();

const rendered = de.nativeElement.innerHTML;
expect(rendered).toContain(emptyTextFieldEditingTemplate);
});

it('should render nothing when field value is empty, when editing is explicitly disabled', () => {
const field = {
value: '',
metadata: testMetadata,
};
comp.field = field;
comp.editable = false;
fixture.detectChanges();

const rendered = deSpan.nativeElement.innerHTML;
expect(rendered).toBe('');
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Directive, Type, ViewContainerRef, EmbeddedViewRef, TemplateRef } from '@angular/core';
import { RenderingField } from './rendering-field';
import { GenericFieldValue, isFieldValueEmpty } from '@sitecore-jss/sitecore-jss/layout';

/**
* Base class that contains common functionality for the field directives.
*/
@Directive()
export abstract class BaseFieldDirective {
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
protected viewRef: EmbeddedViewRef<unknown>;
protected abstract field: RenderingField<GenericFieldValue>;
protected abstract editable: boolean;
/**
* Custom template to render in Pages in Metadata edit mode if field value is empty
*/
protected abstract emptyFieldEditingTemplate: TemplateRef<unknown>;
/**
* Default component to render in Pages in Metadata edit mode if field value is empty and emptyFieldEditingTemplate is not provided
*/
protected abstract defaultFieldEditingComponent: Type<unknown>;

constructor(protected viewContainer: ViewContainerRef) {}

/**
* Determines if directive should render the field as is
* Returns true if we are in edit mode 'chromes' (field.editable is present) or field is not empty
*/
protected shouldRender() {
return !!this.field?.editable || !isFieldValueEmpty(this.field);
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Renders the empty field markup which is required by Pages in editMode 'metadata' in case field is empty.
*/
protected renderEmpty() {
if (this.field?.metadata && this.editable) {
if (this.emptyFieldEditingTemplate) {
this.viewContainer.createEmbeddedView(this.emptyFieldEditingTemplate);
} else {
this.viewContainer.clear();
this.viewContainer.createComponent(this.defaultFieldEditingComponent);
}
}
}
}
Loading