@progress/sitefinity-widget-designers-sdk
v0.2.5
Published
This package aims to create a parity for widget designer generation similar to the [autogenerated widget designers](https://www.progress.com/documentation/sitefinity-cms/autogenerated-field-types) in Sitefinity. Due to some limitations in Typescript and J
Downloads
3,803
Maintainers
Readme
Sitefinity widget designers SDK
This package aims to create a parity for widget designer generation similar to the autogenerated widget designers in Sitefinity. Due to some limitations in Typescript and Javascript, there are several cases where additional data must be provided (see usage guide). Full API documentation could be found in the api.md in the source.
Intallation
via NPM:
npm i @progress/sitefinity-widget-designers-sdk --save
via yarn:
yarn add @progress/sitefinity-widget-designers-sdk
The package relies on the Typescript decorators, which requires adding support for decorators to your tsconfig.json file:
{
"compilerOptions": {
...
"experimentalDecorators": true
}
}
Concept
The metadata, generated from this package, aims to equal the metadata generated by Sitefinity or the ASP.NET Core external renderer and replace the usage of JSON metadata files in external renderers.
Usage
Exctracting the metadata
import { EntityMetadataGenerator, MetadataModel } from "@progress/sitefinity-widget-designers-sdk";
const designerMetadata: MetadataModel = EntityMetadataGenerator.extractMetadata(widgetEntity);
Extracting default values from the metadata
The the metadata generator will go through the designer JSON definition, extract and parse the default value for each property is such is set. Normally the return type would match the type definition of the widget entity model.
import { EntityMetadataGenerator } from "@progress/sitefinity-widget-designers-sdk";
const defaultValues: { [key: string]: any } = EntityMetadataGenerator.extractDefaultValues(designerMetadata);
Parsing serialized widget model values
Sitefinity preserves the widget data on the server in stringified JSON form so when it is received, a parsing depending on the property type is necessary to match the entity model properties' types.
import { EntityMetadataGenerator } from "@progress/sitefinity-widget-designers-sdk";
const deserializedValues: { [key: string]: any } = EntityMetadataGenerator.parseValues(valuesFromServerCall, designerMetadata);
Creating a widget entity and property metadata
The widget model should be decorated using the WidgetEntity decorator.
import { WidgetEntity } from "@progress/sitefinity-widget-designers-sdk";
@WidgetEntity("SitefinitySection", "Section")
export class SectionEntity {
@Category('QuickEdit')
CssSystemGridSize: number = 12;
}
DataModel and Model property decorators
Properties that have complex models as types need their data models explicitly specified (while in C# these types and their subproperties would be generated OOB). For more details on complex objects see here
import { Model, WidgetEntity} from "@progress/sitefinity-widget-designers-sdk";
@WidgetEntity("ComplexWidget", "Complex Widget")
class EntityWithComplexProperties {
@DisplayName("Complex property")
@DataModel(ComplexPropModel)
ComplexProperty?: ComplexPropModel;
}
@Model()
class ComplexPropModel {
@DataType("string")
ChildString: string | null = null;
}
is the same as in C#
public class EntityWithComplexProperties {
[DisplayName("Complex property")]
public ComplexPropModel ComplexProperty { get; set; }
}
public class ComplexPropModel {
public string ChildString { get; set; }
}
Specifics
Default values
The default value of the properties would be taken either from the DefaultValue decorator or a default value set to the property in the class declaration:
@DisplayName("Number prop")
@DefaultValue(42)
@DataType("number")
NumberProperty: number;
// DeafultValue="42" Type="number"
If a default value is set to basic types, the DataType decorator can be omitted. The following code would produce the same output:
@DisplayName("Number prop")
NumberProperty: number = 42;
// DeafultValue="42" Type="number"
Property display configuration
Sections can be arraged either manually or via the SectionsOrder class decorator.
Properties can be arranged in sections in the autogenerated designers and ordered by a given index via the ContentSection decorator.
Sections can be separed in categories: Basic, Advanced, QuickEdit. Setting the category to the property will move the property there and create a section for it in that category.
Properties can be given several types of description via the Description and DescriptionExtended decorators.
@WidgetEntity('CustomEntity', 'Custom entity')
@SectionsOrder('First section', 'Second section')
class Entity {
@ContentSection('Second section', 0)
SecondSectionFirstProperty: any;
@ContentSection('Second section', 1)
SecondSectionSecondProperty: any;
@ContentSection('First section', 0)
@Description('simple description')
FirstSectionFirstProperty: any;
@Category('Advanced')
@DescriptionExtended({
Description: '...',
InlineDescription: '...',
InstructionalNotes: '...'
})
FirstPropertyAdvancedCategoryNewSection: any;
}
Field type
The property editor field is defined via the DataType decorator. Besides the basic types such as string, number, bool/boolean, there are some predefined types in the KnownFieldTypes enum as well as the several variations in the ComplexType enum. Types that are a variation of choices should have a Choice decorator defined as well.
@DataType('string')
StringProperty: string | null = null;
// @DataType('string') for basic types the decorator can be omitted if there is a default value of the same type set
StringProperty: string = 'default value';
@DataType(KnownFieldTypes.Choices)
@Choice([{Title: 'First choice', Value: 'first'}, {Title: 'Second choice', Value: 'second'}])
ChoiceProperty: string | null = null;
// checkbox
@DataType(KnownFieldTypes.CheckBox)
YesNoProperty: boolean = false;
// choices with multiple selection
@DataType(KnownFieldTypes.Choices)
@Choice([{Title: 'First choice', Value: 'first'}, {Title: 'Second choice', Value: 'second'}], true)
MultipleChoiceProperty: string = 'first';
// chip choice
@DataType(KnownFieldTypes.ChipChoice)
@Choice({Choices: [
{Title:'Yes', Name: 'Yes', Value: 'True', Icon: null},
{Title:'No', Name: 'No', Value: false, Icon: null}
]})
ChipChoiceSingle: boolean = true;
Validation
Property values can be validated on the front end based on decorators set in the entity. Most of them can receive and custom validation failed error message.
- Required
- Readonly
- Range
- MinLength
- MaxLength
- RegularExpression:
- Url (predefined regexp)
- EmailAddress (predefined regexp)
- DecimalPlaces (for floating point numbers)
- StringLength
Associated content or media items
A property can refer to a content item of any of the types that are found or created in Sitefinity. The autogenerated designer will display a content selector for that content type. The full type name should be used - e.g. for pages: Telerik.Sitefinity.Pages.Model.PageNode
Related content and media
@Content({
Type: 'Telerik.Sitefinity.Pages.Model.PageNode',
AllowMultipleItemsSelection: false
})
SelectedPage: MixedContentContext = null;
// Media item is shorthand for Content
@MediaItem('images', true)
SelectedMedia: MixedContentContext = null;
@Content({
Type: 'Telerik.Sitefinity.Libraries.Model.Image',
AllowMultipleItemsSelection: false
})
FullDecoratorSelectedMedia: MixedContentContext = null;
Taxonomy
@TaxonomyContent({Type: 'Taxonomy_Tags'})
SelectedTags: MixedContentContext = null
// or
@TaxonomyContent({Type: 'Tags'})
ShorthandSelectedTags: MixedContentContext = null
Conditional visibility
Fields can be shown or hidden based on other property values using the ConditionalVisibility decorator.
FirstProperty: string = '';
@ConditionalVisibility({
conditions: [{
fieldName: 'FirstProperty',
operator: 'Equals',
value: 'show second property'
}]
})
SecondProperty: string = '';
Complex objects
In the autogenerated designers in Sitefinity there are several ways to define and to represent properties with complex object types.
@DataModel(PropModel)
// @DataType(ComplexType.Complex)
ComplexObjProp: PropModel = null; // => renders a section with the model
@DataModel(PropModel)
// @DataType(ComplexType.Complex)
@TableView("TableTitle")
ComplextTableProp: PropModel = null; // => renders the model as a table
@DataModel(PropModel)
@DataType(ComplexType.Enumerable)
MultipleComplexObj: PropModel[] = null; // renders editable rows for each instance of the model
@DataModel(PropModel)
// @DataType(ComplexType.Enumerable)
@TableView({ Reorderable: true, Selectable: true, MultipleSelect: true })
MultipleComplexObj: PropModel[] = null; // renders full editable table with a row for each instance of the model
...
@Model()
class PropModel {
stringProp: string = "default-value";
numberProp: number = 13;
...
}
Collections
Having a properties of collection types, it should be explicitly specified:
Array
Currently arrays are supported for objects/classes and string types. Other types would be rendered as a simple string field.
import { ComplexType, DataModel, DataType } from "@progress/sitefinity-widget-designers-sdk";
// would receive Type="enumerable"
@DataType(ComplexType.Enumerable, "string")
ValidStringCollection: string[] = null;
// would receive Type="enumerable"
@DataModel("string")
@DataType(ComplexType.Enumerable)
AlsoValidStringCollection: string[] = null;
// would receive Type="enumerable"
// with TypeChildProperties of the DataModel
@DataModel(ObjectModel)
@DataType(ComplexType.Enumerable)
ObjectCollection: ObjectModel[] = null
// invalid properties
// would receive Type=null
@DataType(ComplexType.Enumerable)
InvalidCollection: boolean[] | number[] = null;
Dictionary
Currently a collection of type dictionary is supported only when LengthDependsOn property decorator is also present and the value type is an object/class. Otherwise the field would be rendered as a simple string field. For collections of objects that don't depend on another property, we suggest using an array.
import { ComplexType, DataType, Model } from "@progress/sitefinity-widget-designers-sdk";
@DataModel(DictValue)
@DataType(ComplexType.Dictionary)
@LengthDependsOn("PropName", "PropDisplayName", "PropTitle")
ObjectDictionary: { [key: string]: DictValue } = null;
....
@Model()
class DictValue {
@DataType("number")
Int: number;
@DataType("string")
Str: string;
}
LinkModel
This property displays a link selector. It can be used to select external links or link to a specific Sitefinity CMS page or content item.
The field is available in the following two representations:
- A simple field that has option to either manually enter a url or select a Sitefinity content to link to. It must be used with the
@DataType('linkInsert')
property decorator - A field that represents a simple add button (with a plus icon) that opens a dialog when clicked with additional properties that can be set for the link (such as query params, anchors, text to display, open in new tab etc.). It must be used with the
@DataType('linkSelector')
property decorator
import { LinkModel } from "@progress/sitefinity-widget-designers-sdk";
@DataType('linkInsert')
public LinkInsert: LinkModel | null = null;
@DataType('linkSelector')
public LinkSelector: LinkModel | null = null;
Additionally, link selectors can be used within a complex object or an array of complex objects. The complex object class has to be marked with the @Model()
property decorator and the property that will be the complex object field has to be marked with the @DataModel()
property decorator:
import { LinkModel, ComplexType, DataModel, DataType } from "@progress/sitefinity-widget-designers-sdk";
import { ComplexObjectWithLinks } from "...";
@DataModel(ComplexObjectWithLinks)
@DataType(ComplexType.Complex)
public ObjectWithLinks: ComplexObjectWithLinks | null = null;
@DataModel(ComplexObjectWithLinks)
@DataType(ComplexType.Enumerable)
public ArrayOfObjectWithLinks: ComplexObjectWithLinks[] | null = null;
The complex object model:
import { LinkModel, DataType, Model } from "@progress/sitefinity-widget-designers-sdk";
@Model()
export class ComplexObjectWithLinks {
@DataType('linkInsert')
public Link: LinkModel | null = null;
@DataType('linkSelector')
public LinkModel: LinkModel | null = null;
@DataType('linkSelector')
public LinkModelMultiple: LinkModel[] | null = null;
}