Repository navigation
v3.3.0
Changes since v3.2.7. No public API was removed: these are fixes, security hardening and accessibility work, with a few behaviour changes that apps may need to act on, listed under Upgrade notes.
Known issue: with Objection.js 3.2.0, queries that mix
withGraphJoined()andwithGraphFetched()fail. This is fixed in v3.3.1, which requires Objection.js 3.3.0. Please upgrade to v3.3.1.
Upgrade notes
Dependencies
@floating-ui/domis a new peer dependency of@ditojs/admin(a
required peer of@tiptap/vue-3). Add it to the app:
pnpm add @floating-ui/dom.type-festis now a regular dependency of@ditojs/admin, as only its
type declarations use it. Apps that only installed it to satisfy the peer
can remove it.@tiptap/extension-text-styleis no longer a peer of the admin; remove it
unless the app uses it itself.vueis now a peer dependency of@ditojs/ui, so its dist no longer
bundles a second Vue runtime. Apps already providevue; nothing to do
unless@ditojs/uiis used without it.- The admin's UMD /
require()build now exposes{ DitoAdmin, default }
instead of the class itself; read.DitoAdminor.defaultfrom it. ESM
imports are unchanged, andimport { DitoAdmin }now works too. - Library builds of
@ditojs/buildkeep subpaths of dependencies external
(e.g.@tiptap/pm/*,@vueuse/integrations/*); apps must provide these
packages instead of relying on inlined copies. CSS subpaths are still
bundled.
Admin: notifications, labels and dialogs
notify()escapestextandtitle. Markup intextnow shows as
literal tags (a dev warning points to it). Pass formatted text through the
newhtmloption instead, and escape any data in it withescapeHtml():
notify({ type: 'info', html: '<b>Saved</b> ' + escapeHtml(name) }).
textandhtmlare mutually exclusive in the types.- Item labels escape item data.
getItemLabel()escapes the item's
string values; HTML returned by developerrender()/itemLabel()
callbacks is still rendered as HTML. Apps that stored markup in item fields
to style labels must render it throughitemLabel()instead.
itemLabel()callbacks return HTML, so they must escape the values they
insert withescapeHtml().DitoContextgainsitemLabelText, the
plain-text form of the HTMLitemLabel; formaria-labels use this text. - Confirmations are admin dialogs instead of native
window.confirm()
(delete, remove, leave with unsaved changes, remove file). E2E suites that
answer native dialogs (page.on('dialog')) must click the dialog's buttons
instead:Cancel, or the submit button named after the verb, e.g.
Delete. The clipboard reports errors through notifications instead of
alert(). - A
beforeunloadprompt shows when a form or view has unsaved changes.
E2E suites that reload or navigate away from dirty forms may need to accept
the nativebeforeunloaddialog, or save / discard first. - Plain select values whose option disappeared are kept and shown as
"(unavailable)", instead of being cleared silently. Apps relying on the
value being dropped must clear it themselves, e.g. inonChangeof the
field the options depend on. - Color fields emit
changeonly once the user's edit is done, and no longer
for values changed by code.onChangehandlers that relied on programmatic
changes must react to the source of the change instead. - Deleting items from a list with its own
resourceno longer marks the form
dirty (the server already applied the change);changeis still emitted. - Validation errors are rendered in flow below the field (an alert list
linked viaaria-describedby) instead of in a tippy tooltip, so forms with
errors are taller. Inputs receive the focus instead of their wrappers. - Schema handlers in the
onXform are now wired to native DOM events like
events: { x }already was. Unknown validator names, schema members that
override component members and unsupportedemitEvent()options now log
warnings.
Admin: deprecations
setLoading()on resources and views andDitoRoot.registerLoading()
are deprecated; they forward to the new loading trackers
(loadingTracker.begin()/.track()).isLoadingis now read-only and
only declared on resource components, views and the root.$pulldown-*SCSS variables ($pulldown-radius,$pulldown-padding*)
are deprecated until 4.0; the pulldown no longer uses them.options.groupBy(the unused type in the declarations) is deprecated; use
the schema'sgroupBy, now also supported by radio and checkboxes.
Admin / UI: removed and renamed CSS classes
Apps that style or select these classes (including e2e locators) must update:
.dito-pulldown,.dito-pulldown--open,.dito-pulldown__item,
.dito-pulldown__item--disabled: the create button and account menu use
ui'sDitoMenuButton(.dito-menu-button__menu,__item,
__item--disabled).- The tippy
errortheme ([data-theme~='error']): error tips are gone,
see above. .dito-label--active: replaced by:focus-visible..dito-page--width-60/--width-80,.dito-sidebar--width-60/
--width-99: breakpoints use container queries now..dito-drop-target: now.dito-upload--drop-targeton the upload..dito-date-time-picker__inner(and its--focusmodifier).- Dead selectors that never matched were fixed:
.dito-switch-label→
.dito-switch__label,.dito-upload-footer→.dito-upload__footer. - Markup changes: calendar controls and the login link are
<button>s
instead of<a>s; drag handles are focusableDitoDragHandles; the
calendar grid has rows (.dito-calendar__week,.dito-calendar__item);
tree items render
DitoEditButtonsinstead of inline buttons; date and time pickers render
throughDitoPickerInput.
Server
- Related models are validated fully also under models with
$initialize(). Relations are now parsed and initialized with the parent's
options before its$initialize(), so{ id }references in graphs are
validated as full models and fail on required fields. Link by foreign key
(authorId: 1) or use#ref/relate: trueinstead of{ id }stubs. new Model(json)no longer validates, as its comment always promised;
callModel.fromJson()to validate.- Not-found errors include the model id:
'Item' model with id 12 not found. Adjust tests matching the message. DatabaseError.causepoints at the driver's native error
(error.nativeError) instead of the wrapped Objection error. Formatted
errors keep their stack.NotImplementedErrorresponds with 501 instead of 404.
AuthenticationErrorcarries passport's failure status.type: 'color'validates through the newcolorformat, as a hex color
(#rgb,#rgba,#rrggbb,#rrggbbaa) or a color keyword (it accepted any
string). Stored values such as
'rgb(…)','ff0000'(no#) or''now fail validation on updates;
migrate the data or use another type / format for these fields.- Collection
PATCH/PUTwith a single object instead of an array returns
400 (BodyValidation). - Scoped deletes only delete the models of the requested scope; users excluded
by the session scope are logged out. - Invalid date strings in filters are left to the format check (400) instead
of being parsed. from: 'member'parameters and$ownerchecks go through
member.getagain, so app overrides ofmember.getapply. Overrides receive
(ctx, modify, { query, forUpdate })and should pass these on, e.g. to the
newfetchMember().- Asset steps (
handleAddedAndRemovedAssets()etc.) run through the
Application methods again on model writes, so app overrides apply. All asset
methods return arrays, also without anAssetmodel. - Actions are routed by the HTTP methods Node.js supports
(http.METHODS); other method names throw at setup. - Sessions serialize users by
$id();$is()compares by$id(). AdminController.getPath(name)is deprecated; useresolveConfigPath(name).
Type declarations
- Admin:
DitoContext.userandparentItemDataPathare nullable, so
callbacks under strict TypeScript must handlenull; new
DitoContext.itemLabelText;notify()takestextorhtml;
rootComponentisDitoRootInstance;
setLoading()/isLoadingmoved to resource, view and root interfaces;
panel schemas mapped inSchemaByType;DateSchema.dateFormatmarked
deprecated (no effect). - Server: graph and fetch methods return
SingleQueryBuilderor
ArrayQueryBuilderdepending on the input;allowScope(),
clearAllowScope(),allowFilter()andomit()return the builder (they
are now chainable at runtime too);filterGraph(),populateGraph()and
fromJson()are typed by model class; asset methods return arrays without
undefined/null; graph data accepts nullable relations and#id/
#ref; newfetchMember()(with amodify(query, trx)callback),
relateAndFetch(),resolveModelClass(),resolveConfigPath(). Code
relying on the previous wider types may need narrowing or casts removed.
Security
- Stored XSS through item labels: item values are escaped in labels of lists,
objects, trees and the trail. - Stored XSS through notifications:
titleandtextare escaped; item
labels and server error messages in form notifications go throughhtml
with escaped values.
Fixes
admin
- Compare route paths by whole segments (
/items/1vs/items/12), for
closing forms, active menu items and tree items. - Validate unmounted fields before closing mutating forms.
- Don't take over user edits made while options load as clean changes.
- Run
compute()when options resolve toundefined; forget options of
removed components; don't mutate computed values when setting defaults. - Stop waiting for pending loads after a timeout.
- Key computed values, options and item stores by data identity instead of
list index; transfer uids by item id after saving. - Data loads only abort the previous load; request callbacks are awaited.
- Buttons: running state,
errorhandlers receiving the error, notifications
per context. - Don't cache failed requests; key resource requests by URL.
showDialog()rejects when its components fail to set up.notifications.destroyAll()works.- Ignore malformed filter entries in the query; show server errors of list
filters at their fields. new DitoAdmin(el, { views })falls back to the globalditoobject.schema.data()receives a context.- Dialogs: only close
clickToClosedialogs on overlay clicks; Escape honours
cancel buttons; label dialogs withsettings.label. - Markup: TipTap 3
autofocus/enableRules, swapped sub- / superscript,
pending edits written before destroy, no<p>null</p>. - Multiselect: added tags no longer leak into shared options and emit
change; stored tags show without options. - Checkboxes keep
0andfalsevalues. - Uploads: rows keyed by file, download responses checked, API headers and
credentials sent with uploads and downloads, live upload status,video/mp4.
Relative download URLs (files/1) now resolve against the API URL, for
links and downloads alike; absolute and root-relative URLs are used as is. - Code editor calls
onInput; slider number input labelled. decimals: 0in the decimals validator; clear errors on empty validation
results.- Resolve items of named sub-menus.
- Temporary ids of unrelated sources removed on the clipboard.
- Select the default tab once the item has loaded, keeping a user-selected
tab; focus tabs by key. - Only open tree items along existing edit paths.
- Section labels render in flow; panels of components show in tabs, and
validation errors in them select their tab. isApiUrl()requires a boundary; store defaults aren't written in getters;
defaultMultiple,defaultWidth,getSourceTypemerged through
extends.- Buttons inside forms no longer submit them (
type="button"). - Dead and leaking CSS selectors fixed; edit buttons of tree items stay
compact. - The
cache: 'global'load cache is cleared on logout and user change.
ui
- Triggers close with Escape and clean up their listeners; positioning stops
when hidden or unmounted. - Pagination: don't submit forms, clamp the page to 1.
- Switch with a single custom label sized correctly.
- Input
sizebinding restored. - Date stepping clamps the day; spaces aren't parsed as digits; custom date
format functions applied inDitoDateTimePicker; disabled dates and times
are never committed. - Focused fields keep their error border.
- Calendar resets the time to 00:00 after the date was cleared.
- Time inputs let Cmd / Ctrl / Alt shortcuts through.
server
- Query parameters: range separator, nulls order error message.
- Validator:
_-prefixed keywords and formats resolved;instanceof
accepts constructors; self-referencing andadditionalPropertiesschemas
converted; failed conversions not cached. - Controllers: member lookups with named parameters, per-request loggers,
ModelControllermodel class lookup. - Graphs: parseable
relation(mod) as aliasexpressions; only relatable
models reduced to references; plain graph data filtered and populated;
relate: truein collection graphs; relates through non-owned relations;
Locationheader of related models. clone()keeps all scopes;upsertGraph()child queries keep the default
scope.- Relations loaded through
fetchGraph(), including properties of relations. - Streamed response logging and connect middleware (
next(), errors, mount
paths). - Storages: empty uploads, mime-type detection.
- Silent env restore, migration errors, JSON seeds.
- UUID / hyphenated ids in sessions;
AssetMixincallssuper.$parseJson();
rollback warning about modified asset files.
build
- Externalize dependency subpaths in library builds; UMD globals derived from
module ids. - Base64-encode raw bytes of non-svg data URIs.
- New
dist:checkscript, also run in CI, checks built bundles for
undeclared or bundled dependencies. - Test harnesses are no longer published with admin and ui.
utils
deprecate()test made independent of test order (no runtime change).
Improvements
- Keyboard accessibility: tabs (arrow keys, Home / End, Enter / Space),
menus and create button, date and time pickers (calendar grid with arrow
keys), drag handles (Alt+ArrowUp / Down, also inlined items), editor resize
handles, tree edit buttons on focus, focus rings via:focus-visible. - New
DitoMenuButtonin@ditojs/ui, replacing the mouse-only pulldowns of
the create button and account menu;DitoTriggergains click toggle,
open()/close()/toggle()andmatchTargetWidth. DitoButtonsupports verbs, link rendering and disabled links; buttons
across the admin render through it.- Menus and trails use real links (modifier-clicks open new tabs); sub-menus
open on hover and focus. - Validation errors are announced to assistive tech; aria fixes in trail and
table head. - Radio and checkboxes support
groupBy,editableandmultiple: false. - New type option
rendersOwnLabel. addEvents()in ui supports abort signals.- Container queries for breakpoints instead of JS width measurement.
- Upload drop targets tracked reactively.
Internal
- Tests: Vitest coverage (
pnpm coverage), component tests in happy-dom
mounting the real admin, unit tests across admin, ui, server, utils and
build; line coverage now ~96%. Known bugs marked withtest.fails(). - Screenshot tests of all admin components and states in the Playwright
Docker image (pnpm -C tests screenshots). - Refactors behind unchanged APIs:
Session,ViewRegistry,
LoadingTracker,ItemStores,ListQuery,FileDropTargetsand clipboard
state as plain modules;LoadingMixinandPulldownMixinremoved;
AssetManagerand shared handler parsing /ParameterValidatoron the
server; shared componentsDitoSourceItem,DitoDragHandle,
DitoInputAffixes,DitoResizeHandle,DitoPickerInput,DitoTimePanel. - DOM handlers removed via abort signals; release docs in the README.