Releases: serenity-is/Serenity
10.0.1
Features
- First .NET 10 version. You'll need to install Visual Studio 2026 and .NET 10 SDK.
- Please stay at 9.2.x versions if you still need to use Visual Studio 2022 and .NET 8.
- Strict Content-Security-Policy (CSP) is enabled in StartSharp template by default.
- AddCspDirective overloads for ControllerBase and HttpContext
- Instead of using sleek-vars.rtl sleek-vars.ltr etc. change the values of --l, --r variables based on rtl. Increase number of supported columns to 100 from 50 which should be enough for most cases.
- Add styleNonce option to SleekGrid constructor support more strict CSP directives. If not passed, it will be looked up from a meta element with csp-nonce name, or style/script with nonce value in document head.
- Replace inline style attributes to potentially support stricter CSP directives. Currently only CKEditor 4 is an issue as it requires unsafe-inline.
- Configured
HtmlNoteContentEditorandHtmlReportContentEditorto useTiptapinstead ofCKEditor(StartSharp). See tiptap-init.ts file in latest template. Tiptap usage is currently opt-in. CKEditor support will be removed in a future version as it is out-of-date and is not compatible with strict CSP. - Add helpers to add / get CSP directives, both from the layout and per specific pages (like emailclient page which uses CKEditor 4 that requires unsafe-inline)
- New
useUpdatableComputedhelper indomwiseto create a factory for computed signals that can be manually updated as a batch (by internally incrementing a counter signal). This may be useful for computed signals that depend not only on other signals, but externally changing state.
Bugfixes
- Fix potential issue with SlickFormatting.treeToggle when formatter returns a string, and enableHtmlRendering is false. Update the default comment in FormatterContext.enableHtmlRendering
- Fix remaining discovered CSP related issues in sample pages
9.1.3
@serenity-is/domwiseis our new library to create DOM elements with full TypeScript support. It is primarily based on jsx-dom with integrated signal support via @preact/signals-core and some extra types / ideas from ryansolid/dom-expressions (SolidJS) and lusito/tsx-dom.@serenity-is/domwisealso exports signal functions likesignal,computed,effectetc. from@preact/signals-coreand supports signals as attribute values and element children, and reactively updates the attributes and dom nodes when signal values change. Please removejsx-domfrom yourpackage.json, and set thejsxImportSourceto@serenity-is/domwise. There are a few differences betweenjsx-domanddomwise. The HTML element attributes are lowercased to match the actual HTML attribute casing, e.g.,readonlyinstead ofreadOnly,forinstead ofhtmlFor. We aliased some liketabIndex,htmlFor,maxLengthetc. for easier transition. This should make it easier to copy/paste actual HTML code to our.tsxfiles, e.g., from a Bootstrap sample. For compatibility, event handlers are left as camelCase, e.g., onClick.- The layout system for SleekGrid is completely rewritten, and we now introduce
EnhancedLayout(StartSharp), which is an improved and feature-complete version of previously available (but unused)FrozenLayout. It supports pinning columns to the left (start) or right (end) and frozen columns at the top or bottom.EnhancedLayoutis enabled by default for all grids in StartSharp, though pinning / frozen rows are opt-in via column menu / options. If you were usingFrozenLayoutin some of your grids (by overridinggetSlickOptions), it is recommended to remove it. - Columns now have a functional dropdown menu with actions to hide columns, show column picker, reset columns, edit column filter, change sort order, move the column to the left or right, pin columns (EnhancedLayout), auto-size the column (AutoColumnWidthMixin), group by the column (DraggableGroupingMixin), and set the Summary/Aggregate types (CustomSummaryMixin).
- Automatic Registration for Grid Mixins/Plugins (StartSharp): All the available grid plugins / mixins like
HeaderMenuPlugin,AutoColumnWidthMixin,CustomSummaryMixin,DraggableGroupingMixinandHeaderFiltersMixinnow provide an option to automatically register with all grids in the application. It is also possible to limit the registration to a subset of the grids via a predicate. Auto-registration is configured in StartSharp viaModules/Common/script-init/autoregister-init.tsfile where you may customize the auto-registration process and override options for the auto-registered instance. - HeaderFiltersMixin provides an option (
showButtonWhenNotFiltered) to hide the filter button when the column is not filtered. This is useful to save space for the column header itself. - DraggableGroupingMixin has an option named
showPanelWhenNoGroupsthat, if set to false, hides the grouping bar (one with textDrag a column here...) when there are no grouped columns. - CustomSummaryMixin offers an option named
showFooterWhenNoSummariesthat, if set to false, hides the grid footer when there are currently no columns with summaries. It also shows indicator symbols for the current aggregate type. - Renamed
[ScriptSkip]attribute to[TransformIgnore]attribute to better match the intent, e.g., ignoring the class or property during Sergen/Pro.Coder transforms from C# to TypeScript.ScriptSkipstill exists but is deprecated. The old name felt a bit like 'skip this on the client/script side'. - Renamed
[Ignore]attribute toIgnoreUIFieldto better match the intent, e.g., ignoring the generation of column / form fields for that property.Ignoreattribute still exists but is deprecated. The old name was not clear enough. - Renamed
IgnoreNameAttributetoSkipNameCheckAttributeto better match the intent.IgnoreNameattribute still exists but is deprecated. - [OneWay] attribute is also obsolete; use the [SkipOnSave] attribute. There is now a [SkipOnLoad] attribute which does for loading what [OneWay]/[SkipOnSave] does for saving. We also now have an [Unbound] attribute, which when placed on a column property, will cause the column to have no field, and act like a mix of [SkipOnSave, SkipOnLoad, SkipNameCheck].
- Renamed
Grid(@serenity-is/sleekgrid) toSleekGridto avoid mix-ups withDataGrid,EntityGridand other grid types. - New
Attributesnamespace which works similarly to the@Decoratorsnamespace for [Symbol.typeInfo] style type registration, e.g., [Attributes.panel()] instead of [new PanelAttribute()] - The
FilterableAttributewhich used to control advanced filtering on grids via a bottom filter bar and an advanced filter dialog is renamed toAdvancedFilteringAttributewhich you may use viaAttributes.advancedFiltering(). - When there is no active advanced filter, the filter bar will no longer be shown to save space. To make it possible to edit an advanced filter when the filter bar is hidden, we added a tool button next to the column picker. The filter bar is also moved to the top of the grid. This will make it easier to identify when a grid has an advanced filter.
- The getColumns method is deprecated (DataGrid/EntityGrid). We had a
protected getColumns()method inDataGridsubclasses that is used to create the initial column set for a grid, but because its name matched theSleekGrid'sgetColumns()method that returned the current set of visible columns in the grid, sometimes users tried to call this method to get the current columns, instead ofdataGrid.sleekGrid.getColumns(). Please overrideprotected createColumns()method instead which better matches the intent. To access currently visible columns for a data/entity grid, you may use.columnsproperty, or.sleekGrid.getColumns(). To access all the columns (including invisible ones, not necessarily matching the visible order) use.allColumnsproperty or.sleekGrid.getAllColumns(). - For compatibility with jQuery, we previously chose to set
style.displaytononewhen an element is hidden viaFluent.togglemethods. We now decided to use thehiddenattribute which applies to all element types, and actually hides the element even if thestyle.displayproperty is overridden by a Bootstrap class liked-block. Fluent tries to handle this transition gracefully, but it is recommended to update the logic in your own files. - SleekGrid now has a better column dragging system that also works on touch devices.
- Rewrote the layout system of the SleekGrid from the ground up. It now uses a
CSS Gridlayout for the main panels / viewport. - SleekGrid is now able to handle dynamic changes of pinned columns and frozen rows. It also handles them responsively so that when the viewport gets small (e.g., on mobile devices) or when there are not enough data rows, the number of pinned columns and frozen rows is temporarily reduced to make the scrollable area visible.
- Note that SleekGrid that was hosted as a submodule inside Serenity repository, is now embedded as a sub-tree. If you are using Serenity as a submodule, you may need to rename / delete
Serenity/packages/sleekgridfolder before pulling latest changes. - To avoid direct dependency on SleekGrid and RemoteView classes, we now have interface versions of these types, ISleekGrid and IRemoteView. This may affect you if you referenced one of these types directly.
- SleekGrid now has an improved and more secure HTML sanitizer, based on DOMParser. Even though we enable DOMPurify in our application templates instead of this sanitizer, the built-in sanitizer should be sufficient for most cases. The old regex-based basic sanitizer could fail in some complex cases.
@serenity-is/corelibnow also has asanitizeHtmlfunction that uses SleekGrid's configured one if available, DOMPurify or its own DOMParser-based sanitizer. We recommend using this function where you need HTML sanitization for user-provided content (unless you directly reference DOMPurify). - Strings Returned From Formatters Are Assumed To Be Text (
**[Breaking Change]**) asgridDefaults.enableHtmlRenderingis now false by default. This means strings returned from formatters are assumed to be text, not raw HTML. You should convert those formatters to return HTML elements (viadomwiseetc). Note that when this flag is false (default now), the ctx.escape() function will return strings as is and will not HTML-encode them, as it would otherwise result in double HTML escaping.
If you want to revert to the previous behavior (not recommended), set gridDefaults.enableHtmlRendering = true in ScriptInit.ts etc. - Added a
MVC:AsNamespaceoption to sergen.json which causes a.MVCnamespace suffix to be generated instead of anMVCclass. When enabled, theESMhelper class will also be generated under this namespace, e.g.,RootNamespace.MVC. This should reduce confusion when a namespace likeSerenity.Extensionsis added to global usings as it also has its ownMVChelper. The default is false for now, as it would be a problem forESM.references, as they would need to be written asMVC.ESM.or an alias would need to be assigned in the project file like<Using Include="$(MSBuildProjectName).MVC.ESM" Alias="ESM" />. New projects have this alias defined by default. The newdefaults@9.0.0insergen.jsoncan be used instead ofdefaults@6.6.0to enable this flag by default (new projects will also usedefaults@9.0.0). - Added VSIX support for Visual Studio 2026
- Added comments to HeaderFiltersPlugin options. Added a getFilterText option to override the displayed value. Pass 'header-filter' as purpose to formatters. Allow passing more options through the mix...
9.0.0
-
Type registration via decorators (e.g.,
Decorators.registerClass,Decorators.registerEditor, etc.) is now deprecated (it still works but is not recommended):import { Decorators, Widget } from "@serenity-is/corelib"; @Decorators.registerClass("MyProject") export class MyType extends Widget<any> { }
Instead, use a static
[Symbol.typeInfo]property declaration:import { Widget } from "@serenity-is/corelib"; export class MyType extends Widget<any> { static [Symbol.typeInfo] = this.registerClass("MyProject.MyModule.MyType"); }
This new approach offers several advantages over decorators. Most importantly, decorator information is not emitted by TypeScript in declaration files (e.g.,
.d.ts), making it difficult or impossible to identify registration names for referenced project/npm package types.Another reason for this change is a longstanding bug in esbuild (see issue) with decorators and property initializers, which currently only has a workaround by using
experimentalDecorators: trueintsconfig.json.The new registration methods also support passing interfaces or attributes as an array in the second argument. For example, to set
@Decorators.panel(true)on a class, you can do:import { Widget, PanelAttribute } from "@serenity-is/corelib"; export class MyType extends Widget<any> { static [Symbol.typeInfo] = this.registerClass("MyProject.MyModule.MyType", [new PanelAttribute(true)]); }
Note: When using this new registration method, TypeScript will not allow any decorators on the containing type, as the
thisexpression is referenced in a static field initializer. If you need to use third-party decorators, you can use an alternative registration style:import { Widget, classTypeInfo, registerType } from "@serenity-is/corelib"; @someCustomDecorator("test") export class MyType extends Widget<any> { static [Symbol.typeInfo] = classTypeInfo("MyProject.MyModule.MyType"); static { registerType(this); } }
This is equivalent to
this.registerType, as theregisterTypestatic method inWidgetcallsclassTypeInfoandregisterTypeinternally.This style is also useful for formatter registration, as formatters typically do not have a base type and cannot simply call
this.registerTypelike widget subclasses:export class MyFormatter implements Formatter { static [Symbol.typeInfo] = formatterTypeInfo("MyProject.MyModule.MyFormatter"); static { registerType(this); } }
For consistency, you may also choose to extend the new
FormatterBaseclass:export class MyFormatter extends FormatterBase implements Formatter { static [Symbol.typeInfo] = this.registerFormatter("MyProject.MyModule.MyFormatter"); }
For all registration styles, you can now pass only the namespace ending with a dot (the enclosing type name will be auto-appended):
import { Widget } from "@serenity-is/corelib"; export class MyType extends Widget<any> { static [Symbol.typeInfo] = this.registerClass("MyProject.MyModule."); }
Sergen / Serenity.Pro.Coder now also generates namespace constants under the
Modules/ServerTypes/Namespaces.tsfile, so you can use them to avoid typos:import { Widget } from "@serenity-is/corelib"; import { nsMyModule } from "../../ServerTypes/Namespaces"; export class MyType extends Widget<any> { static [Symbol.typeInfo] = this.registerClass(nsMyModule); }
We hope this will reduce mistakes when renaming classes or using a namespace with mismatched casing.
-
Added an
EditLinkfunction that can be used instead ofSlickHelpers.itemLinkwith JSX elements. TheDataGridalso has anEditLinkarrow function for use in formatters. -
The row type itself is now used as the base row type if a row type is passed to the
PropertyItemProvider. -
Imports generated by
ServerTypingsGeneratorare now ordered and organized. -
You can now pass
formatterTypeby reference inPropertyItem. -
Added a
valueargument to theValidator.optionalmethod, allowing validation methods to be called without an element—just a value—when desired. -
Adjusted the
Validator'sonfocusoutmethod: if a required input is cleared and loses focus, it is still validated if it has a "required" rule. Otherwise, errors were not shown until the form is saved. -
Introduced a new Inline Editing sample in the Grid Editor (StartSharp).
-
Added new options and improved features in
GridEditController(StartSharp):- New
getPropertyItemoption to optionally override the column item. - The
bulkSaveHandleroption now works. - The
saveHandlercallback receives anargsargument with adefaultHandlerproperty, allowing you to call the default save handler when needed.
- New
-
Splitted WizardDialog's renderContents method into renderButtons, renderCancelButton, renderBackButton, renderNextButton etc. to make it easier to modify/inject content (StartSharp)