Add DynamicDataTable, ProgressBar, CollapsiblePanel, icons and color themes to wicket-extensions - #1637
Draft
reiern70 wants to merge 10 commits into
Draft
Add DynamicDataTable, ProgressBar, CollapsiblePanel, icons and color themes to wicket-extensions#1637reiern70 wants to merge 10 commits into
reiern70 wants to merge 10 commits into
Conversation
Contributor
Author
|
@papegaaij this branch is in WIP... and I still need to do several iterations. But anyways I would love your feedback. On ideas about what such components should support. |
reiern70
force-pushed
the
dynamic-data-table
branch
from
October 2, 2026 17:15
5b3a2f4 to
adade30
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1637 +/- ##
============================================
+ Coverage 61.95% 65.90% +3.95%
- Complexity 11280 11877 +597
============================================
Files 1250 1301 +51
Lines 48429 54030 +5601
Branches 6792 6960 +168
============================================
+ Hits 30002 35609 +5607
+ Misses 15729 15673 -56
- Partials 2698 2748 +50 🚀 New features to boost your workflow:
|
reiern70
force-pushed
the
dynamic-data-table
branch
from
October 2, 2026 17:31
adade30 to
4a8db01
Compare
reiern70
force-pushed
the
dynamic-data-table
branch
3 times, most recently
from
October 2, 2026 20:22
deccd56 to
9f02bcb
Compare
A build of wicket-extensions without clean failed in the resources optimizer: the Map and for...of loops in wicket-veil.js made the Closure compiler, which transpiles to ES5, add its polyfill prelude to wicket-veil.min.js, and the next build, which compiles that file again, rejected the prelude's @define statements (JSC_NON_CONST_DEFINE). Only a clean build passed. The registry of local veils is now a plain object without prototype, read with Object.keys, which compiles without polyfills, so the minified script compiles again and repeated builds pass. The behavior of the veil is unchanged. GitHub issue #1631
reiern70
force-pushed
the
dynamic-data-table
branch
2 times, most recently
from
October 3, 2026 18:10
e4e1395 to
6f2fdea
Compare
The script that sets up a page's web socket connection, wicket-websocket-setup.js.tmpl, stored the page id, the context, the connection token and the other settings with jQuery.extend(Wicket.WebSocket, ...). Wicket's JavaScript no longer needs jQuery, so on a page that does not load it a WebSocketBehavior failed with "ReferenceError: jQuery is not defined": the connection was never opened and no push message reached the page. The settings are now copied with Object.assign, which does the same for this flat object. Pages that load jQuery see no difference; pages without it get a working web socket connection. WebSocketTesterBehaviorTest checks that the rendered setup script does not refer to jQuery. GitHub issue #1640
…apse Pages often carry content that only some users need all the time: long explanations, help texts, advanced filter forms. Applications keep building their own show/hide toggles for this, usually with inline script or inline style, both of which a strict Content Security Policy blocks. CollapsiblePanel in wicket-extensions renders the native <details> and <summary> elements: it works without JavaScript, is keyboard accessible and needs no inline script or style. It is collapsed by default and can be rendered expanded with setExpanded(true). With setRememberExpanded(true) every toggle is reported via Ajax, so the panel keeps the user's choice when it or its page is rendered again. The title is a model, rendered escaped; the body is any component returned by newBody(String). The color of the body's text is the CSS custom property --wicket-collapsible-text if it is set, else the color of the panel's parent. The ajax examples get a CollapsiblePanel page showing a collapsed, an expanded and a remembering panel, the last with a form as its body, with some space between the panels and the body text in normal weight. WicketExamplePage gets newExplanation(String, IModel), so an example can show its explanation in a collapsed panel. The OSGi export list of wicket-extensions gains the package. GitHub issue #1636
Content shown on top of a page or a component, such as a form editing a row of a table, keeps needing the same frame: a title, a way to close it and a body. Applications build it each time, often with inline styles a strict Content Security Policy blocks. FloatingPanel, in the new package org.apache.wicket.extensions.markup.html.floating, renders a title bar with the title, escaped, and a close button calling onClose(target), hidden when isClosable() is false, above the body returned by newBody(id). It needs no inline script or style. The panel is a CSS grid: the close button, a cross drawn as SVG so it is centered whatever the font, comes after the body in the markup and is placed in the title bar by the grid, centered vertically, so a focus trap starts in the body; it is named for screen readers from FloatingPanel.close. The OSGi export list and module-info gain the package. New API only; nothing existing changes for applications.
Applications showing values users copy, such as phone numbers or identifiers, write their own clipboard code, and rarely confirm that the copy happened. The new package org.apache.wicket.extensions.markup.html.clipboard brings wicket-clipboard.js: Wicket.Clipboard.copy(text, iconTarget) copies the text, then shows a check mark over the end of iconTarget, inside its box so a cell clipping its content does not hide it, and announces the copy to screen readers. Every element with a data-wicket-copy attribute copies its value on a click, or on a double click with data-wicket-copy-on="dblclick", which also works for markup rendered in the browser. ClipboardCopyBehavior writes these attributes, escaped, for a component and renders the script, its style sheet and the localized message (ClipboardCopyBehavior.copied); renderHeadItems() renders them for markup rendered elsewhere. Browsers allow writing to the clipboard only in a secure context and in response to a user action. The OSGi export list and module-info gain the package; the script and its tests run with the other JavaScript tests. New API only; nothing existing changes for applications.
Components showing an icon, such as the action buttons of a table, either hard-code their own few SVG paths or ask the application for markup, with no common type to pass an icon around. The new package org.apache.wicket.extensions.markup.html.icon has: - IIcon, an icon that renders its own markup (getMarkup()), written into the page as is; serializable and usable as a lambda. - FontAwesomeIcon, the 2001 solid icons of Font Awesome Free as an enum, whose markup is <i class="fas fa-name" aria-hidden="true"></i>; the page has to load the Font Awesome style sheet. getName() gives the Font Awesome name, such as pen-to-square; the digits are DIGIT_0 to DIGIT_9. - SvgIcon, the same icons as inline SVG, one em high, as wide as their view box, filled with the current color and hidden from screen readers, so they need no style sheet or font. Wicket ships no Font Awesome content: SvgIcon reads each icon, the first time it is rendered, from the Font Awesome Free web jar (org.webjars.npm:fortawesome__fontawesome-free) the application puts on its class path, and keeps the attribution comment of the file. Without the web jar it throws a WicketRuntimeException naming the dependency. The icons are licensed under CC BY 4.0, a Category B license under the ASF third-party policy, which may not be included in source releases; this keeps it out of Wicket's artifacts altogether. wicket-extensions uses the web jar for its tests only; its version is managed in the root pom (fontawesome-free.version). The OSGi export list and module-info gain the package. New API only; nothing existing changes for applications.
reiern70
force-pushed
the
dynamic-data-table
branch
from
October 4, 2026 01:45
6f2fdea to
aad2ddb
Compare
…from JSON
DataTable renders every cell as a Wicket component, so each refresh re-renders
the whole body on the server and a click on a row that has changed or gone
since it was rendered targets a component that no longer exists.
DynamicDataTable, in wicket-extensions, renders only the <table> and its
header. The rows come as JSON from the table's own endpoint, on load, on
refresh(target) and optionally on a poll interval, and each cell is a column
template evaluated in the browser by a template engine: a built-in one with
{{path}} (escaped) and {{{path}}} (raw), or Handlebars. Links in the cells are
plain markup carrying data-dt-action; a click goes to the table, which looks
the row up by its key through IDynamicDataProvider and hands it to the
IAjaxActionColumnContributor registered for the action, or to
onNotFoundAction() if the row is gone. Rendered rows carry an id,
<tableId>-row-<key>, next to data-key, so keys have to be unique and stable.
The action prefix _wicket is reserved for the actions the table handles
itself; registering a contributor for such an action throws an
IllegalArgumentException.
Templates are trusted markup written into the page as is; the Javadoc of
IDynamicColumn says they have to be authored by the developer.
Columns can read their templates from .html resources the way a component
reads its markup: ResourceDynamicColumn and ResourceDynamicColumnContributor
look the file up through the application's IResourceStreamLocator for the
table's style, variation and locale, take the content of its <wicket:panel>
(so a license header outside it is left out), honour stripComments, and are
cached per table outside development mode. Columns can render their header as
any component through IDynamicColumn#newHeader(String), a label of getHeader()
by default.
Row keys are typed: DynamicDataTable, IDynamicDataProvider,
ISortableDynamicDataProvider and SortableDynamicDataProvider take a type
parameter K. keyOf(T) defaults to IIdentifiable<K>;
SortableDynamicDataProvider takes the key type and an optional key function
such as Contact::getId. Keys become strings only at the browser boundary,
through the provider's formatKey(K) and parseKey(String), the latter
converting with the application's converter for getKeyType() by default.
The table
- is paged like DataTable, its navigation on top, at the bottom or both, the
navigator, its label and an optional rows-per-page choice on one line
- has toolbars like DataTable: rows of the table above or below the body,
added with addTopToolbar/addBottomToolbar (AbstractDynamicToolbar). The
navigation (DynamicNavigationToolbar) and the selection bar
(DynamicSelectionToolbar) are toolbars themselves, created by
newNavigationToolbar/newSelectionToolbar and placed by a ToolbarPosition.
The navigation has a slot in front of the navigator (newPrefix), for example
for a filter, and one after it for table-wide actions, added with
addToolbarAction(IDynamicToolbarAction). While a prefix is shown the
navigation stays visible and only its paging part is repainted, so a filter
field keeps its focus.
- exports to CSV with CsvExportToolbarAction, an icon button in the
navigation: the selected rows if there are any, all rows of the provider in
the current order otherwise. A column takes part through
IDynamicColumn#isExportable and getExportValue(row), or
AbstractDynamicColumn#setExportValue; columns without a value are left out.
Dates and times of java.time are written in ISO-8601, so other tools read
them unambiguously; other values go through the table's converters. The file
name, delimiter and download location are configurable.
- sorts by the columns wrapped in a SortableDynamicColumn, an
ISortableDynamicDataProvider receiving the sort state; sortable headers show
an up and a down arrow, the current one highlighted
- numbers its rows across pages with a RowNumberColumn and marks them even and
odd
- lets the user resize its columns through the new ResizableColumnsBehavior
script, stretching the table (ColumnResizing.STRETCH, the default) or not
at all (NONE); a column can opt out, and the widths are reported to the
server and rendered again from there. While dragging, a line with arrowheads
shows where the edge will land, a shaded band the new extent of the column
and a label its width in pixels. They are removed whatever ends the drag: a
release, a cancel, Escape, the window losing focus, an error, or a new drag;
the click a browser fires when the drag ends reaches no button under the
pointer. On hover the handle shows a line in its middle. While the columns
can be resized, every row ends with an empty cell, 3em wide, leaving room
after the last column; header cells marked data-resizable-skip are left out
of the columns but keep their width, also while the table is hidden when it
is attached. getCellCount() gives the colspan of a whole row.
- lets the user hide columns marked IHideableColumn with a
ColumnChooserToolbarAction, a button in the navigation opening a list of
those columns with a tick on the shown ones and an Apply button; the
application hides any column with setColumnVisible(column, visible).
getColumns() returns every column, getVisibleColumns() the shown ones, and
CSV export writes the shown exportable columns in display order.
- lets the user move columns marked IMovableColumn: a grip in front of the
title drags the column with the mouse, a pen or a finger (pointer events,
not HTML5 drag and drop), a ghost of the header following the pointer and a
marker with arrowheads showing where it will land; dropping on the left or
right half of a column puts it before or after. The arrow keys on the grip
move it one place. Columns without the mark keep their places: no marker is
shown where they would move, and the server rejects such a move.
moveColumn(column, index) moves a column for the application, its width and
proportion along.
- remembers the order, visibility and widths of its columns as a ColumnState
in an IColumnStateStore, loaded when the table is initialized and saved
after every move, show, hide or resize by the user, once the application
sets a store with setColumnStateStore: SessionColumnStateStore keeps them
in the session, so reloading the page keeps the layout, and an application
keeps them elsewhere, for example by user in its database, with a store of
its own. A table has no store by default. getColumnState() and
setColumnState(state) read and apply a state. Columns are referred to by
IDynamicColumn#getId() (AbstractDynamicColumn#setId; a SortableDynamicColumn
has the id of the column it wraps), or by their index in the constructor's
list for columns without one; ids have to be unique, which the table checks
(IllegalArgumentException). The key comes from getColumnStateKey(), by
default the page class and the table's path.
- sends the rows of its page along when it is rendered again in an Ajax
request, for example after a move, instead of letting the browser fetch
them, so its body never shows empty; the JSON is escaped so it cannot end
the script.
- sizes its columns in proportion to each other with
setColumnProportions(double...), and takes the full width of its parent with
setAdjustToParentWidth(true), following the window's size until the user
resizes a column; both work with every ColumnResizing
- selects rows with a SelectionColumn: a checkbox per row and one in the
header for the rows of the page, the selection kept on the server by key so
it survives paging, sorting, refreshing and pushes. Once the whole page is
selected, a bar offers to select every row of the provider. Sorting clears
the selection. getSelection() returns an ISelection, a list of keys or all
rows, whose fetch() reads the rows from the provider lazily;
onSelectionChanged is called after every change. Every row is sent with its
state, available to templates as {{@selected}}.
- shows any component in an overlay, a window over a veil covering only the
table: showOverlay(component, target), closeOverlay(target),
isOverlayShown() and onOverlayClosed(target). The focus is trapped in the
window while it is shown, and the window is placed within the part of the
table the user sees. Escape closes it unless something in it was changed: a
form field differing from its initial value, or an element marked
data-dt-changed, for example a form rendered again with errors.
- is blocked while one of its Ajax requests runs, by a LocalVeilBehavior from
newVeilBehavior(), which shows a spinner when the request takes a while;
returning null leaves the table usable
- centers its cells vertically, shows the text of its navigation in bold, and
fills action buttons with the accent color on hover
IconBasedRowAction is a row action shown as an icon button, its tooltip as the
button's title and aria-label. The icon is an IIcon: one of the built-in
IconBasedRowAction.Icon (edit, delete, download, add), an SvgIcon, a
FontAwesomeIcon or the application's own; getIcon() is asked on every render.
IconBasedToolbarAction is the same for the navigation, an icon button calling
onClick(table, target). With a
confirmation, setConfirmation(model) or getConfirmation(row), a click first
asks the question with Cancel and Confirm, in a FloatingPanel titled with the
tooltip, in the table's overlay, and onRowAction(row, target) runs only once
the user confirms and the row still exists.
AjaxDownloadActionColumnContributor is an action whose link downloads the
clicked row as a JSON file, produced by the table's serializer from the row
looked up again by its key when the file is requested (404 if it is gone). It
exposes the AjaxDownloadBehavior settings: the location, the SameSite
attribute of the completion cookie, the file name, and success and failure
callbacks. setIcon(IIcon) shows it as an icon button, and getContent(row) and
getContentType() replace the JSON with another format.
IAjaxActionColumnContributor gets a default bind(DynamicDataTable), called
once when the contributor is registered, for such behaviors.
Pushing over web sockets: an IWebSocketLightWeightMessage carries tableId,
typeId and its own data, and is sent with send(IWebSocketConnection),
send(Component), which finds the page's connection, or send(Application,
sessionId, IKey), which needs no request and can be called from a background
thread with what WebSocketBehavior#onConnect provides. Each table registers
the browser-side handlers of its IWebSocketMessageTypes, called as
function(message). Registered by default are UpdateRowsMessageType (repaints
the rows from the message), RefreshMessageType (fetches the rows again, see
refreshViaWebSockets()) and UpdateRowMessageType, which repaints one row,
found by its id and ignored if it is not shown; build it with
DynamicDataTable#newUpdateRowMessage(row) or, without the table, with
UpdateRowMessageType.newMessage(tableId, key, data). Client-side instances are
kept in Wicket.DynamicDataTable.instances and destroyed when the table is
removed in an Ajax or web socket request, or when Wicket removes or replaces
its element or one of its ancestors in the page. refresh(target) and sorting
send the rows with the Ajax response instead of letting the browser fetch them
again. onRowsSent(keys) tells the application which rows the browser shows, so
it can push updates for those only. wicket-extensions gains an optional
dependency on wicket-native-websocket-core for this, like the one on Jackson.
ProgressBar, in the new package
org.apache.wicket.extensions.markup.html.progress, renders a native <progress>
element for a percentage clamped to 0..100, with no inline style, so it works
under a strict content security policy. A null value renders an indeterminate
bar, for work whose end is unknown (also available as
ProgressBar.indeterminateMarkup()). The bar is striped and animated, unless
the user prefers reduced motion; a finished bar, at 100, is solid. Its label
is bold, on a chip of the track's color, so it stays readable over the filled
part and on dark pages; the middle of its digits is placed on the middle of
the bar (an empty inline-block as high as the label, middle-aligned and
raised by 1cap - 1ex), in every browser supporting the cap unit. Its markup is
also available from
ProgressBar.markup(value), which ProgressBarColumn uses as the template of a
DynamicDataTable column; the table renders the header items of columns
implementing IHeaderContributor, so the column brings the bar's style sheet
along.
The texts of the table are in the default bundle and, at the request of the
author, in German in Initializer_de; other translations are left to native
speakers.
The OSGi export list of wicket-extensions gains the new package and the
dynamic table package.
The repeater examples get a DynamicDataTablePage over a thousand contacts,
showing an ID column, an address column and an Actions column whose buttons
edit, delete and download a contact: edit opens, in a FloatingPanel in the
table's overlay, a validated form for the name and the address (country, city
and street, all required), delete asks for a confirmation and download gives
a jCard (RFC 7095) of the contact. The page shows paging with 25, 50, 100 or
500 rows, sorting, resizing, full width and column proportions,
selection, CSV export, a filter by name in front of the navigation, push
refresh and push of rows, and a Progress column whose values a timer advances,
pushing only the rows the browser shows, while the page's web socket
connection is open; every column reads a localized template file and the long
explanation sits in a CollapsiblePanel. The /repeater application now runs on
JavaxWebSocketFilter. The ajax examples get a stand-alone progress bar page
with determinate and indeterminate bars. A WicketTester test covers the
page's actions and its edit form. A Selenium test of the push refresh
runs only with -Pselenium, which needs Chrome; the Jetty the example tests
start now registers Wicket's web socket endpoint for it. The phone numbers are
labelled and aligned, and copy to the clipboard on a double click; the
settings of the example are drop-downs in a grid. The example adds contacts
with an IconBasedToolbarAction, hides Home phone, Cell phone, City and Country
at first and lets the columns after ID be hidden and moved; the selection, #,
Actions and ID columns cannot be resized. An "Icons" option switches the
actions between the built-in icons, SvgIcon and FontAwesomeIcon, and a
"Column state" option between the session and a FileColumnStateStore keeping
the state in a file of the temporary directory, with "Reset the columns". The
examples depend on the Font Awesome web jar, serve its style sheet and fonts
through FontAwesomeResourceReference and name it in their NOTICE and, appended
by the release build, in their LICENSE, with the licenses of its icons (CC BY
4.0), fonts (OFL 1.1) and code (MIT). The example's columns have ids and keep
their layout in the session. A new Icons
page shows the three sets of icons, filtered by name, copying an icon's Java
constant when clicked. A QUnit page checks that the progress bar's label is
centered, at sizes from 12 to 64 pixels.
New API only; nothing existing changes for applications.
GitHub issue #1635
Every component of wicket-extensions brings its own style sheet with its own hard-coded colors, so an application wanting its tables, progress bars and panels to match its look overrides each style sheet separately. The new package org.apache.wicket.extensions.theme defines the colors once, as CSS custom properties named --wicket-theme-* (primary, on-primary, primary-dark, accent, surface, surface-alt, border, text, toolbar, hover, highlight, selected, progress, progress-dark, track, color-scheme). A theme is a CSS class setting them; Theme ships DEFAULT, BLUE, RED, GREY, GREEN, ORANGE, LIGHT_BLUE and DARK, and Theme.CSS is the style sheet defining them. --wicket-theme-color-scheme gives the color scheme of the form controls inside a themed component, so a text field or check box matches the theme even on a page that declares color-scheme: light dark and is shown in dark mode. ThemeBehavior puts a theme's class on a component, next to the marker class wicket-theme (Theme.MARKER_CSS_CLASS), and contributes the style sheet; the theme comes from a model, so re-rendering the component switches it. A theme on the page applies to every themable component inside it, a theme on one component only to that one. Themes are switched by class only and need no inline style, so they work under a strict Content Security Policy. An application defines a theme of its own as a CSS class setting the same properties, and puts it on an element together with wicket-theme, which components use for the parts they style only inside a theme. The DynamicDataTable (header, rows, toolbars and their form controls), the resize handles, guide, band and label of ResizableColumnsBehavior, the column move ghost and drop marker, the ProgressBar and the CollapsiblePanel (title in the theme's primary colors, like a table header, with a border and rounded corners, and the body text in the theme's text color unless --wicket-collapsible-text is set) read their colors from the theme, and so do the table's overlay window, its buttons and the icon actions, the FloatingPanel (title bar in the colors of the table headings), the check mark of the clipboard copy, and the veil of wicket-extensions: --wicket-theme-veil dims what a veil or an overlay blocks, and the spinner takes the accent and track colors. The DARK theme sets dark surfaces and the dark color scheme for form controls. The OSGi export list gains the package. They read the properties only inside a theme's class, so a component without a theme looks as it did before; the progress bar and the resize guide keep their current colors as fallbacks. The examples get ThemeChoice, a drop-down switching the theme via Ajax, on the DynamicDataTable, progress bar and collapsible panel pages. New API only; nothing existing changes for applications. GitHub issue #1638
…hemes The DynamicDataTable, the ProgressBar, the CollapsiblePanel and the FloatingPanel take their colors from the shared themes of wicket-extensions, but the other components on the same page keep their own hard-coded look: a white modal dialog with no header on a dark theme, yellow auto-complete suggestions, tabs without a style and an upload progress bar unlike the ProgressBar. - ModalDialog: its DefaultTheme reads the theme. The overlay dims with --wicket-theme-veil and the dialog has --wicket-theme-surface, falling back to the previous rgba(0, 0, 0, 0.2) and white. Inside a theme the dialog also takes the text and border colors, the color scheme and the accent for links, and content using the classes modal-dialog-header, modal-dialog-body and modal-dialog-footer gets a header in the primary colors and a footer in the toolbar color. Outside a theme a dialog looks as before. - AutoCompleteTextField: the suggestions are appended to the body, outside the theme of the field, so the script copies the theme properties of the field onto them, through the CSS object model, and marks them wicket-aa-themed. A new style sheet, AbstractAutoCompleteBehavior.THEME_CSS, styles marked suggestions only: surface, text and border colors, the hover color under the pointer and the primary colors for the selected one. Fields outside a theme are not marked and keep their look; DefaultCssAutoCompleteTextField's style sheet is unchanged. - TabbedPanel: TabsStyleBehavior gives a tabbed panel one of the looks of TabsStyle (TABS, PILLS, UNDERLINE) by a CSS class, with colors from the theme and fallbacks outside one. Tabbed panels without the behavior are untouched. - UploadProgressBar renders the markup of the ProgressBar, a native <progress> with the percentage as label, and its style sheet, so both bars look the same and follow the theme; a test keeps the markup in line with ProgressBar.markup(). The script sets the value and the label, ignores a status without a number instead of failing and stopping the polling, and still updates the old markup of an application that replaced it. UploadProgressBar.css only places the bar and the status; getCss() returning null now leaves out the ProgressBar's style sheet too. The ajax examples of these components get the layout of the DynamicDataTable example: the explanation in a CollapsiblePanel and a theme drop-down, in the page's theme. The tabbed panel example also chooses the look of the tabs. A new theme editor example edits the colors of a theme with a picker per property, shows themed components following every change and gives the CSS class defining the theme. What applications see: an UploadProgressBar looks like a ProgressBar, so CSS written for its old .wupb-border, .wupb-background and .wupb-foreground elements no longer applies; such an application keeps its own look by returning its style sheet from getCss() and replacing the markup with a variation. Every page with an AutoCompleteTextField loads one small extra style sheet, which styles nothing outside a theme. The rest is new API or applies only inside a theme. GitHub issue #1645 Part of GitHub issue #1639
The examples index lists the components of wicket-extensions under the technique they use (repeaters, Ajax, ...), so someone looking for what wicket-extensions offers has to know where each one lives. The home page gets an "Extensions" entry leading to ExtensionsIndex, a bundle-driven index like the other ones, linking the repeaters (among them the DynamicDataTable), trees, modal dialog, progress bar, collapsible panel, auto-complete, tabbed panel, wizard, breadcrumb, captcha, dates and the other examples built on wicket-extensions. The repeaters, tree, wizard, breadcrumb, captcha and dates entries move from the home page into this index, and it lists the veil example of the ajax examples, the theme editor and the Icons page of the repeater examples.
reiern70
force-pushed
the
dynamic-data-table
branch
from
October 5, 2026 11:23
aad2ddb to
1398213
Compare
Contributor
Author
dynamic-data-table-demo.mp4 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a DynamicDataTable and a set of supporting components to wicket-extensions, each usable on its own:
org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.dynamicorg.apache.wicket.extensions.markup.html.repeater.data.tableorg.apache.wicket.extensions.markup.html.progressorg.apache.wicket.extensions.themeorg.apache.wicket.extensions.markup.html.collapsibleorg.apache.wicket.extensions.markup.html.floatingorg.apache.wicket.extensions.markup.html.clipboardorg.apache.wicket.extensions.markup.html.iconorg.apache.wicket.extensions.markup.html.tabsIssues:
clean(fixed in its own commit)Everything is new API for 11.0.0 and works under a strict Content Security Policy: no inline script or style. The only visible change for existing applications is the UploadProgressBar, which now renders the ProgressBar's bar (see Themes). There are WicketTester tests, QUnit tests and examples;
mvn clean verify -Pjs-testis green.Commits, in order, on top of master
ee62ec8c01(up to date with master as of 2026-10-05):Themes
Colors defined once, as CSS custom properties (
--wicket-theme-*), and read by the style sheets of the components below.Themeships default, blue, light blue, red, orange, grey, green and dark;Theme.CSSis the style sheet defining them.--wicket-theme-veil) and spinner.ThemeBehaviorputs a theme's CSS class on a page or on a single component and contributes the style sheet. The theme comes from a model, so re-rendering switches it.wicket-theme; a custom theme puts it next to its own class.TabsStyleBehavior: tabs, pills or underline) and UploadProgressBar (now looking like the ProgressBar) follow the themes too; outside a theme they keep their look, except the UploadProgressBar, whose bar is now the ProgressBar's.CollapsiblePanel
A title and a body the user can expand and collapse, rendered as native
<details>/<summary>.setExpanded(true)renders it expanded.setRememberExpanded(true)reports every toggle via Ajax, so the panel keeps the user's choice when it or its page is rendered again.newBody(String).--wicket-collapsible-text.FloatingPanel
A window with a title bar and a body, for content floating on top of a page or a component.
onClose(target)(hidden whenisClosable()is false).newBody(id).ClipboardCopyBehavior
Copies a text to the clipboard and shows a check mark where the copy was made.
Wicket.Clipboard.copy(text, iconTarget)copies from any script, shows the check mark over the end oficonTargetand announces the copy to screen readers.data-wicket-copycopies its value on a click, or a double click withdata-wicket-copy-on="dblclick", also in markup rendered in the browser.ClipboardCopyBehaviorwrites these attributes (escaped) for a component and renders the script.Icons
IIconis an icon that renders its own markup, so components can take any icon.FontAwesomeIcon: the 2001 solid icons of Font Awesome Free, as<i class="fas fa-name">; the page loads the Font Awesome style sheet.SvgIcon: the same icons as inline SVG, one em high, in the current text color, needing no style sheet or font.SvgIconreads the icons from the Font Awesome web jar the application puts on its class path, keeping the attribution comment of each file. The icons are CC BY 4.0, Category B under the ASF third-party policy, which may not be part of a source release; wicket-extensions uses the web jar for its tests only.ProgressBar
A generic progress bar, for the progress of any work, not only uploads.
<progress>element for a percentage clamped to 0..100.nullvalue renders an indeterminate bar, for work whose end is unknown.prefers-reduced-motion; a finished bar is solid.capunit, checked by a QUnit test.ProgressBar.markup(value)andProgressBar.indeterminateMarkup(), for bars rendered in the browser.ResizableColumnsBehavior
Lets the user resize the columns of a table by dragging the edge of a header, or with the arrow keys on it.
DynamicDataTable
A table that renders only the
<table>and its header on the server. The rows come as JSON from the table's own endpoint, and each cell is a column template evaluated in the browser (built-in{{path}}/{{{path}}}engine, or Handlebars).DataTablerenders every cell as a component, so each refresh re-renders the whole body on the server, and a click on a row that has changed or gone since it was rendered targets a component that no longer exists.DynamicDataTablelooks the clicked row up by its key throughIDynamicDataProviderinstead, and callsonNotFoundAction()if the row is gone..htmlresources resolved like markup (style, variation, locale); headers as any component; row numbers; a progress bar column.data-dt-action, dispatched by the table.IconBasedRowActionshows an action as an icon button with a tooltip, anyIIcon(built-in edit, delete, download, add, orSvgIcon/FontAwesomeIcon), optionally asking for a confirmation first.IconBasedToolbarActionis an icon button in the navigation.AjaxDownloadActionColumnContributordownloads a row, as JSON by default or in any format (getContent,getContentType), as a link or an icon.showOverlay(component, target)shows any component, typically aFloatingPanelwith an edit form or a confirmation, in a window over a veil that blocks only the table; focus stays in the window, and Escape closes it unless something in it was changed.LocalVeilBehaviorblocks the table while one of its Ajax requests runs (newVeilBehavior()).IIdentifiable<K>,formatKey/parseKey).SortableDynamicColumnandISortableDynamicDataProvider.SelectionColumn,ISelection), including "select all rows of the provider".DataTable's (addTopToolbar/addBottomToolbar); the navigation and the selection bar are toolbars, with a slot before the navigator (for example a filter) and pluggable toolbar actions after it.CsvExportToolbarActionexports the selected rows, or all rows if none is selected; columns take part throughgetExportValue.java.timevalues are written in ISO-8601.IHideableColumncan be hidden by the user with aColumnChooserToolbarAction; the application hides any column withsetColumnVisible.IMovableColumnare dragged by a grip in front of their title (pointer events, so mouse, pen and touch), a marker with arrowheads showing where they will land, or moved with the arrow keys; other columns keep their places.ColumnStatein anIColumnStateStore, loaded when the table is created and saved after every change by the user, once a store is set:SessionColumnStateStorekeeps them for the session, an application's own store can keep them in its database, by user, so they survive restarts. Columns are referred to by their uniquegetId().setColumnProportions(double...)andsetAdjustToParentWidth(boolean), with any resizing mode. A table rendered again via Ajax gets its rows with the response, so its body never shows empty.ResizableColumnsBehavior's script. While the columns can be resized, every row ends with an empty cell leaving room after the last column;getCellCount()gives the colspan of a whole row.send(Application, sessionId, IKey)).Templates are trusted markup written into the page as is. The Javadoc of
IDynamicColumnsays they have to be authored by the developer.Examples
SvgIconandFontAwesomeIcon, filtered by name; a click copies an icon's Java constant.