Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Our versioning strategy is as follows:

### 🐛 Bug Fixes

* `[Next.js]` `[Redirects]` Preserve default locale in external absolute urls ([#2142](https://github.com/Sitecore/jss/pull/2142))
* `[Next.js]` [Redirects] Preserve default locale in external absolute urls ([#2142](https://github.com/Sitecore/jss/pull/2142))
* `[React]` Custom properties are not applied to empty field in editing metadata mode ([#2141](https://github.com/Sitecore/jss/pull/2141))

## 22.9.0
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.MD
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2025 Sitecore

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"lerna": "5.6.2",
"packages": ["packages/*", "samples/*"],
"version": "22.10.0-canary.6",
"version": "22.10.0-canary.8",
"npmClient": "yarn",
"useWorkspaces": true
}
2 changes: 1 addition & 1 deletion packages/create-sitecore-jss/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-sitecore-jss",
"version": "22.10.0-canary.6",
"version": "22.10.0-canary.8",
"description": "Sitecore JSS initializer",
"bin": "./dist/index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/sitecore-jss-angular-schematics/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sitecore-jss/sitecore-jss-angular-schematics",
"version": "22.10.0-canary.6",
"version": "22.10.0-canary.8",
"description": "Scaffolding schematics for Sitecore JSS Angular apps",
"scripts": {
"build": "tsc -p tsconfig.json",
Expand Down
37 changes: 16 additions & 21 deletions packages/sitecore-jss-angular/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sitecore-jss/sitecore-jss-angular",
"version": "22.10.0-canary.6",
"version": "22.10.0-canary.8",
"description": "",
"scripts": {
"build": "ng-packagr -p ng-package.json",
Expand All @@ -23,16 +23,16 @@
"url": "https://github.com/sitecore/jss/issues"
},
"devDependencies": {
"@angular-devkit/build-angular": "~18.2.14",
"@angular-eslint/eslint-plugin": "^18.4.3",
"@angular/cli": "~18.2.13",
"@angular/common": "~18.2.13",
"@angular/compiler": "~18.2.13",
"@angular/compiler-cli": "~18.2.13",
"@angular/core": "~18.2.13",
"@angular/platform-browser": "~18.2.13",
"@angular/platform-browser-dynamic": "~18.2.13",
"@angular/router": "~18.2.13",
"@angular-devkit/build-angular": "~20.3.5",
"@angular-eslint/eslint-plugin": "^20.3.0",
"@angular/cli": "~20.3.4",
"@angular/common": "~20.3.4",
"@angular/compiler": "~20.3.4",
"@angular/compiler-cli": "~20.3.4",
"@angular/core": "~20.3.4",
"@angular/platform-browser": "~20.3.4",
"@angular/platform-browser-dynamic": "~20.3.4",
"@angular/router": "~20.3.4",
"@types/jasmine": "^5.1.8",
"@types/node": "^22.9.0",
"codelyzer": "^6.0.1",
Expand All @@ -46,28 +46,23 @@
"karma-firefox-launcher": "^2.1.3",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"ng-packagr": "^18.2.1",
"ng-packagr": "^20.3.0",
"reflect-metadata": "^0.2.2",
"rxjs": "~7.8.1",
"tslib": "^2.3.1",
"typescript": "~5.4.0",
"zone.js": "~0.14.7"
"typescript": "~5.9.3",
"zone.js": "~0.15.1"
},
"peerDependencies": {
"@sitecore-cloudsdk/events": "^0.5.2",
"@types/debug": "^4.1.12",
"rxjs": "~7.8.1"
},
"dependencies": {
"@sitecore-jss/sitecore-jss": "22.10.0-canary.6"
"@sitecore-jss/sitecore-jss": "22.10.0-canary.8"
},
"main": "dist/esm2022/sitecore-jss-sitecore-jss-angular.mjs",
"module": "dist/esm2022/sitecore-jss-sitecore-jss-angular.js",
"es2022": "dist/esm2022/sitecore-jss-sitecore-jss-angular.js",
"module": "dist/fesm2022/sitecore-jss-sitecore-jss-angular.mjs",
"typings": "dist/index.d.ts",
"metadata": "dist/sitecore-jss-sitecore-jss-angular.metadata.json",
"esm2022": "dist/esm2022/sitecore-jss-sitecore-jss-angular.js",
"fesm2022": "dist/fesm2022/sitecore-jss-sitecore-jss-angular.js",
"sideEffects": false,
"gitHead": "2f4820efddf4454eeee58ed1b2cc251969efdf5b",
"files": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, DebugElement, Input, TemplateRef } from '@angular/core';
import { Component, DebugElement, TemplateRef, input } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

Expand All @@ -8,12 +8,12 @@ import { TestBaseDirective } from '../test-data/test-base.directive';
@Component({
selector: 'test-base',
template: `
<span *scTestBase="field; editable: editable"></span>
<span *scTestBase="field(); editable: editable()"></span>
`,
})
class TestComponent {
@Input() field: TextField;
@Input() editable = true;
readonly field = input<TextField>(undefined);
readonly editable = input(true);
}

const emptyTextFieldEditingTemplateId = 'emptyTextFieldEditingTemplate';
Expand All @@ -37,9 +37,9 @@ const emptyTextFieldEditingTemplate =
`,
})
class TestEmptyTemplateComponent {
@Input() field: TextField;
@Input() emptyFieldEditingTemplate: TemplateRef<unknown>;
@Input() editable = true;
readonly field = input<TextField>();
readonly emptyFieldEditingTemplate = input<TemplateRef<unknown>>();
readonly editable = input(true);
}

describe('<span *scTestBase />', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Directive, Type, ViewContainerRef, EmbeddedViewRef, TemplateRef } from '@angular/core';
import {
Directive,
Type,
ViewContainerRef,
EmbeddedViewRef,
TemplateRef,
inject,
InputSignal,
} from '@angular/core';
import { RenderingField } from './rendering-field';
import { GenericFieldValue, isFieldValueEmpty } from '@sitecore-jss/sitecore-jss/layout';
import { FieldMetadataMarkerComponent } from './field-metadata-marker.component';
Expand All @@ -9,36 +17,38 @@ import { MetadataKind } from '@sitecore-jss/sitecore-jss/editing';
*/
@Directive()
export abstract class BaseFieldDirective {
protected viewRef: EmbeddedViewRef<unknown>;
protected abstract field: RenderingField<GenericFieldValue>;
protected abstract editable: boolean;
protected viewRef?: EmbeddedViewRef<unknown>;
protected readonly viewContainer: ViewContainerRef = inject(ViewContainerRef);

protected abstract field: InputSignal<RenderingField<any> | undefined>;
protected abstract editable: InputSignal<boolean>;
/**
* Custom template to render in Pages in Metadata edit mode if field value is empty
*/
protected abstract emptyFieldEditingTemplate: TemplateRef<unknown>;
protected abstract emptyFieldEditingTemplate: InputSignal<TemplateRef<unknown> | undefined>;
/**
* 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);
const field = this.field();
return !field || !!field?.editable || !isFieldValueEmpty(field);
}

/**
* 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.field()?.metadata && this.editable()) {
this.renderMetadata(MetadataKind.Open);
if (this.emptyFieldEditingTemplate) {
this.viewContainer.createEmbeddedView(this.emptyFieldEditingTemplate);
const template = this.emptyFieldEditingTemplate();
if (template) {
this.viewContainer.createEmbeddedView(template);
} else {
this.viewContainer.createComponent(this.defaultFieldEditingComponent);
}
Expand All @@ -51,11 +61,12 @@ export abstract class BaseFieldDirective {
* @param {string} kind - 'open' or 'close' to indicate the start or end of the metadata chrome
*/
protected renderMetadata(kind: MetadataKind) {
if (this.field?.metadata && this.editable) {
const field = this.field();
if (field?.metadata && this.editable()) {
const metadataChrome = this.viewContainer.createComponent(FieldMetadataMarkerComponent);
metadataChrome.setInput('kind', kind);
if (kind === MetadataKind.Open) {
metadataChrome.setInput('metadata', this.field.metadata);
metadataChrome.setInput('metadata', field.metadata);
}
}
}
Expand Down
26 changes: 13 additions & 13 deletions packages/sitecore-jss-angular/src/components/date.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DatePipe, formatDate } from '@angular/common';
import { Component, DebugElement, Input, TemplateRef } from '@angular/core';
import { Component, DebugElement, TemplateRef, input } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { textField as eeTextData } from '../test-data/ee-data';
Expand All @@ -18,16 +18,16 @@ const defaultFormattedDate = formatDate(testIsoDateValue, testFormat, testLocale
selector: 'test-date',
template: `
<span
*scDate="field; editable: editable; format: format; locale: locale; timezone: timezone"
*scDate="field(); editable: editable(); format: format(); locale: locale(); timezone: timezone()"
></span>
`,
})
class TestComponent {
@Input() field: TextField;
@Input() editable = true;
@Input() format = testFormat;
@Input() locale = testLocale;
@Input() timezone = testTimezone;
readonly field = input<TextField>(undefined);
readonly editable = input(true);
readonly format = input(testFormat);
readonly locale = input(testLocale);
readonly timezone = input(testTimezone);
}

const emptyDateFieldEditingTemplateId = 'emptyDateFieldEditingTemplate';
Expand All @@ -50,12 +50,12 @@ const emptyDateFieldEditingTemplate =
`,
})
class TestEmptyTemplateComponent {
@Input() field: TextField;
@Input() editable = true;
@Input() format = testFormat;
@Input() locale = testLocale;
@Input() timezone = testTimezone;
@Input() emptyFieldEditingTemplate: TemplateRef<unknown>;
readonly field = input<TextField>();
readonly editable = input(true);
readonly format = input(testFormat);
readonly locale = input(testLocale);
readonly timezone = input(testTimezone);
readonly emptyFieldEditingTemplate = input<TemplateRef<unknown>>();
}

describe('<span *scDate />', () => {
Expand Down
56 changes: 31 additions & 25 deletions packages/sitecore-jss-angular/src/components/date.directive.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,51 @@
import { DatePipe } from '@angular/common';
import {
Directive,
Input,
inject,
OnChanges,
SimpleChanges,
TemplateRef,
Type,
ViewContainerRef,
input,
InputSignal,
} from '@angular/core';
import { DateField } from './rendering-field';
import { MetadataKind } from '@sitecore-jss/sitecore-jss/editing';
import { BaseFieldDirective } from './base-field.directive';
import { DefaultEmptyFieldEditingComponent } from './default-empty-text-field-editing-placeholder.component';
import { MetadataKind } from '@sitecore-jss/sitecore-jss/editing';
import { DateField } from './rendering-field';

@Directive({
selector: '[scDate]',
})
export class DateDirective extends BaseFieldDirective implements OnChanges {
@Input('scDateFormat') format?: string;
readonly format = input<string>(undefined, { alias: 'scDateFormat' });

@Input('scDateTimezone') timezone?: string;
readonly timezone = input<string>(undefined, { alias: 'scDateTimezone' });

@Input('scDateLocale') locale?: string;
readonly locale = input<string>(undefined, { alias: 'scDateLocale' });

@Input('scDateEditable') editable = true;
readonly editable = input(true, { alias: 'scDateEditable' });

@Input('scDate') field: DateField;
readonly field: InputSignal<DateField | undefined> = input<DateField | undefined>(undefined, {
alias: 'scDate',
});

/**
* Custom template to render in Pages in Metadata edit mode if field value is empty
*/
@Input('scDateEmptyFieldEditingTemplate') emptyFieldEditingTemplate: TemplateRef<unknown>;
readonly emptyFieldEditingTemplate = input<TemplateRef<unknown>>(undefined, {
alias: 'scDateEmptyFieldEditingTemplate',
});

/**
* Default component to render in Pages in Metadata edit mode if field value is empty and emptyFieldEditingTemplate is not provided
*/
protected defaultFieldEditingComponent: Type<unknown>;
protected defaultFieldEditingComponent = DefaultEmptyFieldEditingComponent;

private readonly templateRef: TemplateRef<unknown> = inject(TemplateRef);
private readonly datePipe: DatePipe = inject(DatePipe);

constructor(
viewContainer: ViewContainerRef,
private templateRef: TemplateRef<unknown>,
private datePipe: DatePipe
) {
super(viewContainer);
this.defaultFieldEditingComponent = DefaultEmptyFieldEditingComponent;
constructor() {
super();
}

ngOnChanges(changes: SimpleChanges) {
Expand All @@ -54,7 +56,8 @@ export class DateDirective extends BaseFieldDirective implements OnChanges {
}

private updateView() {
if (!this.shouldRender()) {
const field = this.field();
if (!field || !this.shouldRender()) {
super.renderEmpty();
return;
}
Expand All @@ -63,15 +66,18 @@ export class DateDirective extends BaseFieldDirective implements OnChanges {
this.viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
this.renderMetadata(MetadataKind.Close);

const field = this.field;

const html = field.editable && this.editable ? field.editable : field.value;
const setDangerously = field.editable && this.editable;
const html = field.editable && this.editable() ? field.editable : field.value;
const setDangerously = field.editable && this.editable();
this.viewRef.rootNodes.forEach((node) => {
if (setDangerously) {
node.innerHTML = html;
} else {
node.textContent = this.datePipe.transform(html, this.format, this.timezone, this.locale);
node.textContent = this.datePipe.transform(
html,
this.format(),
this.timezone(),
this.locale()
);
}
});
}
Expand Down
Loading