Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4bf5ed6
feat: start container migration
iobuhov Dec 23, 2025
5987735
feat: add gallery v2
iobuhov Dec 29, 2025
10c0dec
feat: start pagination
iobuhov Dec 29, 2025
acfb138
feat: move to brandi
iobuhov Jan 30, 2026
006edca
fixes
iobuhov Jan 30, 2026
70204f9
chore: small fixes
iobuhov Feb 2, 2026
47eab26
feat: add refresh status
iobuhov Feb 4, 2026
d96d34c
feat: add preview
iobuhov Feb 4, 2026
4d42d5f
chore: fix preview and minor issues
iobuhov Feb 6, 2026
eba8d61
chore: fix preview
iobuhov Feb 6, 2026
7eec66f
feat: enable auto select for gallery
iobuhov Feb 6, 2026
1a84562
feat: add auto select check
iobuhov Feb 9, 2026
3a59c1e
feat: update editor preview
iobuhov Feb 9, 2026
956b4e8
feat: migrate settings storage
iobuhov Feb 10, 2026
af5b2af
feat: refactor dg container
iobuhov Feb 12, 2026
5fd040d
feat: remove dead code
iobuhov Feb 12, 2026
81dab3e
feat: update editor config
iobuhov Feb 12, 2026
4464bf9
chore: update changelog
iobuhov Feb 12, 2026
6082638
chore: remove trace calls
iobuhov Feb 13, 2026
7eb448d
chore: apply suggestions
iobuhov Feb 18, 2026
7a7d059
fix: restore missing jsx
iobuhov Feb 18, 2026
e3e95df
chore: format code
iobuhov Feb 19, 2026
83bf661
fix: correct typos
iobuhov Feb 20, 2026
2483bc9
fix: change gallery styling
iobuhov Feb 23, 2026
8759c18
chore: update changelog
iobuhov Feb 24, 2026
564a053
fix: address minor issues
iobuhov Feb 25, 2026
17ffbbd
fix: add sort api for preview
iobuhov Feb 26, 2026
538d859
test(datagrid-text-filter-web): update screenshot baseline
leonardomendix Feb 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.1",
"@mendix/pluggable-widgets-tools": "10.21.2",
"@prettier/plugin-xml": ">=3.4.1",
"@testing-library/react": ">=15.0.6",
"@types/big.js": "^6.2.2",
"@types/node": "~22.14.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,52 +307,6 @@ $root: ".widget-datagrid";
width: 100%;
}

.pagination-bar {
display: flex;
justify-content: flex-end;
white-space: nowrap;
align-items: baseline;
margin: 16px;
color: $pagination-caption-color;

.paging-status {
padding: 0 8px 0;
}

.pagination-button {
padding: 6px;
color: var(--gray-darker, $gray-darker);
border-color: transparent;
background-color: transparent;

&:hover {
color: var(--brand-primary, $brand-primary);
border-color: transparent;
background-color: transparent;
}

&:disabled {
border-color: transparent;
background-color: transparent;
}

&:focus:not(:focus-visible) {
outline: none;
}

&:focus-visible {
outline: 1px solid var(--brand-primary, $brand-primary);
}
}
.pagination-icon {
position: relative;
top: 4px;
display: inline-block;
width: 20px;
height: 20px;
}
}

/* Column selector for hidable columns outside DG context */
/* List of columns to select */
.column-selectors {
Expand Down Expand Up @@ -522,6 +476,10 @@ $root: ".widget-datagrid";
}
}

.widget-datagrid .progress-bar {
margin: var(--spacing-medium, 16px);
}

.widget-datagrid .widget-datagrid-load-more {
display: block !important;
margin: var(--spacing-small, 8px) 0;
Expand Down Expand Up @@ -593,7 +551,7 @@ $root: ".widget-datagrid";
display: contents;
}

:where(#{$root}-paging-bottom, #{$root}-padding-top) {
:where(#{$root}-paging-bottom, #{$root}-paging-top) {
display: flex;
flex-flow: row nowrap;
align-items: center;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
========================================================================== */
$gallery-screen-lg: $screen-lg;
$gallery-screen-md: $screen-md;
$root: ".widget-gallery";

@mixin grid-items($number, $suffix) {
@for $i from 1 through $number {
Expand Down Expand Up @@ -113,17 +114,18 @@ $gallery-screen-md: $screen-md;
:where(.widget-gallery-fc-start, .widget-gallery-tb-start, .widget-gallery-fc-end, .widget-gallery-tb-end) {
flex-grow: 1;
flex-basis: 33.33%;
min-height: 20px;
height: 54px;
padding: var(--spacing-small) 0;
}

:where(.widget-gallery-fc-start, .widget-gallery-tb-start) {
padding-inline: var(--spacing-medium);
:where(.widget-gallery-fc-start, .widget-gallery-tb-start):not(:empty) {
padding: var(--spacing-small) var(--spacing-medium);
display: flex;
align-items: center;
}

:where(.widget-gallery-fc-end, .widget-gallery-tb-end):not(:empty) {
padding: var(--spacing-small) 0;
}

.widget-gallery-clear-selection {
cursor: pointer;
background: transparent;
Expand Down Expand Up @@ -162,3 +164,19 @@ $gallery-screen-md: $screen-md;
}
}
}

#{$root}-btn-link {
cursor: pointer;
background: transparent;
border: none;
color: var(--link-color);
padding: 0.3em 0.5em;
border-radius: 6px;
display: inline-block;
white-space: nowrap;

&:hover,
&:focus-visible {
background-color: var(--brand-primary-50, #e6e7f2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.pagination-bar {
display: flex;
justify-content: flex-end;
white-space: nowrap;
align-items: baseline;
color: $pagination-caption-color;

.paging-status {
padding: 0 8px 0;
}

.pagination-button {
padding: 6px;
color: var(--gray-darker, $gray-darker);
border-color: transparent;
background-color: transparent;

&:hover {
color: var(--brand-primary, $brand-primary);
border-color: transparent;
background-color: transparent;
}

&:disabled {
border-color: transparent;
background-color: transparent;
}

&:focus:not(:focus-visible) {
outline: none;
}

&:focus-visible {
outline: 1px solid var(--brand-primary, $brand-primary);
}
}
.pagination-icon {
position: relative;
top: 4px;
display: inline-block;
width: 20px;
height: 20px;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import "../../../theme/web/custom-variables";
@import "variables";
@import "pagination-bar";
@import "datagrid";
@import "datagrid-filters";
@import "datagrid-dropdown-filter";
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export function getProperties(values: DatagridPreviewProps, defaultProperties: P
"dynamicPage",
"dynamicPageSize",
"useCustomPagination",
"customPagination"
"customPagination",
"totalCountValue"
]);
}

Expand Down
116 changes: 103 additions & 13 deletions packages/pluggableWidgets/datagrid-web/src/Datagrid.editorPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { If } from "@mendix/widget-plugin-component-kit/If";
import { Pagination as PagingButtons } from "@mendix/widget-plugin-grid/components/Pagination";
import cn from "classnames";
import { GUID, ObjectItem } from "mendix";
import { Selectable } from "mendix/preview/Selectable";
Expand Down Expand Up @@ -51,13 +52,18 @@ const numberOfItems = 3;
const cls = {
root: "widget-datagrid",
topBar: "widget-datagrid-top-bar table-header",
pagingTop: "widget-datagrid-paging-top",
ptStart: "widget-datagrid-tb-start",
ptEnd: "widget-datagrid-tb-end",
header: "widget-datagrid-header header-filters",
content: "widget-datagrid-content",
grid: "widget-datagrid-grid table",
gridHeader: "widget-datagrid-grid-head",
gridBody: "widget-datagrid-grid-body table-content",
pb: "widget-datagrid-padding-bottom",
footer: "widget-datagrid-footer table-footer",
pb: "widget-datagrid-paging-bottom",
pbStart: "widget-datagrid-pb-start",
pbMid: "widget-datagrid-pb-middle",
pbEnd: "widget-datagrid-pb-end"
};

Expand All @@ -79,9 +85,7 @@ export function preview(props: DatagridPreviewProps): ReactElement {
<GridBody />
</Grid>
</WidgetContent>
<WidgetFooter>
<PaddingBottom />
</WidgetFooter>
<WidgetFooter />
</WidgetRoot>
</PropsCtx.Provider>
);
Expand All @@ -96,8 +100,18 @@ function WidgetRoot({ children }: PropsWithChildren): ReactElement {
);
}

function WidgetTopBar({ children }: PropsWithChildren): ReactElement {
return <div className={cls.topBar}>{children}</div>;
function WidgetTopBar(): ReactElement {
return (
<div className={cls.topBar}>
<div className={cls.pagingTop}>
<div className={cls.ptStart}>{useTopCounter() ? <SelectionCounter /> : null}</div>
<div className={cls.ptEnd}>
{usePagingTop() ? <Pagination /> : null}
{useCustomPagination("top") ? <CustomPagination /> : null}
</div>
</div>
</div>
);
}

function WidgetHeader(): ReactNode {
Expand All @@ -113,8 +127,26 @@ function WidgetContent({ children }: PropsWithChildren): ReactElement {
return <div className={cls.content}>{children}</div>;
}

function WidgetFooter({ children }: PropsWithChildren): ReactElement {
return <div className="widget-datagrid-footer">{children}</div>;
function WidgetFooter(): ReactElement {
const props = useProps();
return (
<div className={cls.footer}>
<div className={cls.pb}>
<div className={cls.pbStart}>{useBottomCounter() ? <SelectionCounter /> : null}</div>
<div className={cls.pbMid}>
{props.pagination === "loadMore" ? (
<button className="btn btn-primary widget-datagrid-load-more">
{props.loadMoreButtonCaption}
</button>
) : null}
</div>
<div className={cls.pbEnd}>
{usePagingBot() ? <Pagination /> : null}
{useCustomPagination("bottom") ? <CustomPagination /> : null}
</div>
</div>
</div>
);
}

function Grid({ children }: PropsWithChildren): ReactElement {
Expand Down Expand Up @@ -269,14 +301,45 @@ function EmptyPlaceholder(): ReactElement {
);
}

function PaddingBottom(): ReactElement {
const SelectionCounter = (): ReactNode => {
const props = useProps();
return (
<div className={cls.pb}>
<div className={cls.pbStart} />
<div className={cls.pbEnd} />
<div className="widget-datagrid-selection-counter">
<span className="widget-datagrid-selection-text" aria-live="polite" aria-atomic="true">
{props.selectedCountTemplateSingular || "[row count]"}
</span>
&nbsp;|&nbsp;
<button className="widget-datagrid-btn-link">{props.clearSelectionButtonLabel}</button>
</div>
);
}
};

const Pagination = (): ReactNode => {
const props = useProps();
return (
<PagingButtons
canNextPage
canPreviousPage
gotoPage={() => {}}
nextPage={() => {}}
numberOfItems={props.pageSize ?? 20}
page={0}
pageSize={props.pageSize ?? 10}
showPagingButtons={"always"}
previousPage={() => {}}
pagination={props.pagination}
/>
);
};

const CustomPagination = (): ReactNode => {
const { customPagination } = useProps();
return (
<customPagination.renderer caption="Custom pagination: Place widgets here">
<div style={{ flexGrow: 1 }} />
</customPagination.renderer>
);
};

function useColumns(): ColumnsPreviewType[] {
const { columns } = useProps();
Expand Down Expand Up @@ -320,3 +383,30 @@ function useGridStyle(): CSSProperties {
"--widgets-grid-template-columns": sizes.join(" ")
} as CSSProperties;
}

function useTopCounter(): boolean {
const { itemSelection, selectionCounterPosition } = useProps();
return itemSelection === "Multi" && selectionCounterPosition === "top";
}

function useBottomCounter(): boolean {
const { itemSelection, selectionCounterPosition } = useProps();
return itemSelection === "Multi" && selectionCounterPosition === "bottom";
}

function usePagingTop(): boolean {
const props = useProps();
const visible = (props.showNumberOfRows || props.pagination === "buttons") && !props.useCustomPagination;
return visible && props.pagingPosition !== "bottom";
}

function usePagingBot(): boolean {
const props = useProps();
const visible = (props.showNumberOfRows || props.pagination === "buttons") && !props.useCustomPagination;
return visible && props.pagingPosition !== "top";
}

function useCustomPagination(location: "top" | "bottom"): boolean {
const props = useProps();
return props.useCustomPagination && (props.pagingPosition === location || props.pagingPosition === "both");
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { RefreshIndicator } from "@mendix/widget-plugin-component-kit/RefreshIndicator";
import { observer } from "mobx-react-lite";
import { ReactNode } from "react";
import { useLoaderViewModel } from "../model/hooks/injection-hooks";
Expand All @@ -8,5 +7,11 @@ export const RefreshStatus = observer(function RefreshStatus(): ReactNode {

if (!loaderVM.showRefreshIndicator) return null;

return loaderVM.isRefreshing ? <RefreshIndicator /> : null;
return loaderVM.isRefreshing ? (
<div className="tr" role="row">
<div className={"th mx-refresh-container"}>
<progress className="mx-refresh-indicator" />
</div>
</div>
) : null;
});
Loading
Loading