Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 packages/pluggableWidgets/datagrid-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Fixed

- We fixed an issue where the top bar did not stack its content vertically in narrow containers.
- We fixed an issue where custom pagination ignored "Position of pagination" and always rendered below the grid. It now renders above the grid, below the grid, or — for "Both" — once, below the grid.

### Added

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-10
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## Context

`pagingPosition` (`"top" | "bottom" | "both"`) already drives the built-in `Pagination` control:

- `WidgetTopBar.tsx`: `<If condition={!pgConfig.customPaginationEnabled && pgConfig.pagingPosition !== "bottom"}><Pagination /></If>`
- `WidgetFooter.tsx`: `<If condition={!pgConfig.customPaginationEnabled && pgConfig.pagingPosition !== "top"}><Pagination /></If>`

Custom pagination (the `customPagination` widgets placeholder, shown when `useCustomPagination` is true and `pagination === "buttons"`) is wired separately and ignores position:

- `WidgetFooter.tsx`: `<If condition={pgConfig.customPaginationEnabled}>{customPagination.get()}</If>` — always renders, no position check.
- `WidgetTopBar.tsx`: no custom pagination branch at all.
- `Datagrid.editorPreview.tsx`: `useCustomPagination()` returns `props.useCustomPagination` with no position check; only rendered in the footer's `CustomPagination`.
- `Datagrid.editorConfig.ts` currently hides `pagingPosition` from the properties panel whenever `useCustomPagination` is true (added when custom pagination shipped, as a stopgap since the property had no effect). Once position has an effect, hiding it is wrong.

Gallery (`gallery-web`) had the exact same defect and fixed it under WC-3505 (`GalleryFooterControls.tsx`, `GalleryTopBarControls.tsx`), which was explicitly scoped to exclude Data Grid 2 to keep that PR reviewable. This change ports the same rule to Data Grid 2. Data Grid 2's bars have no alignment/slot-resolution machinery (`resolveSlots`) — that part of the Gallery change was about a _different_, already-working design property (pagination alignment) that Data Grid 2 doesn't have — so none of that machinery is needed here.

## Goals / Non-Goals

**Goals:**

- Custom pagination widgets render in the top bar when `pagingPosition === "top"`, in the footer when `"bottom"`, and exactly once (footer) when `"both"`.
- Editor preview matches runtime for all three positions.
- `pagingPosition` stays visible/editable in Studio Pro when custom pagination is enabled.
- A `check()` warning fires for `useCustomPagination && pagingPosition === "both"`, mirroring Gallery's wording/severity.

**Non-Goals:**

- No pagination-alignment design property for Data Grid 2 (out of scope per WC-3505's own scoping note — Data Grid 2 doesn't have this property; adding one is a feature request, not this fix).
- No changes to `widget-plugin-grid` or `data-widgets` — the built-in `Pagination` component's own position logic is already correct and untouched.
- No changes to the unrelated `_datagrid.scss` top-bar container-query typo noted in the Gallery proposal as a separate, deliberately excluded fix.

## Decisions

- **Reuse the existing `pgConfig.pagingPosition` value already threaded into both bars** rather than introducing new state. `usePaginationConfig()` already exposes `pagingPosition` and `customPaginationEnabled`; both bars just need an additional condition.
- **"Both" resolves to footer-only, not top-bar-only or duplicated.** Same reasoning as Gallery: `customPagination.get()` returns the actual configured widgets placeholder — rendering it in two places would duplicate widget instances, DOM ids, and MobX-backed state. Footer is chosen (not top bar) purely for consistency with Gallery's precedent and because it's the current de facto behavior developers have already built pages against.
- **Condition shape mirrors Gallery exactly** for auditability:
- Footer: `pgConfig.customPaginationEnabled && pgConfig.pagingPosition !== "top"`
- Top bar: `pgConfig.customPaginationEnabled && pgConfig.pagingPosition === "top"`
- **Drop `pagingPosition` from the `hidePropertiesIn` call** in `Datagrid.editorConfig.ts` for the `useCustomPagination` branch; keep `showPagingButtons` hidden there since it only affects the built-in control's prev/next buttons, which custom pagination replaces entirely.
- **Warning lives in `consistency-check.ts`** (not inlined in `editorConfig.ts`) to match this package's existing convention — `editorConfig.ts` re-exports `check` from `consistency-check.ts`, and all other structural warnings/errors already live there.
- **No new capability abstraction (no `resolveSlots`-equivalent).** Data Grid 2's bars have exactly one pagination-bearing slot each (`pb-end` / `tb-end`); there's no counter/load-more displacement problem to solve, so a plain boolean condition is the right level of complexity — introducing a slot-resolution function here would be solving a problem this widget doesn't have.

## Risks / Trade-offs

- **[Risk]** Existing apps that already set `pagingPosition` to `"top"` or `"both"` while using custom pagination will see a visible layout change (widgets moving from footer to top bar, or a warning appearing) on upgrade. → **Mitigation**: this is the bug fix itself — the property was always meant to have this effect, per its own description in Studio Pro ("Position of pagination"). Documented in the changelog as a behavior fix, not flagged as breaking (no API/prop shape change).
- **[Risk]** Unhiding `pagingPosition` when custom pagination is on could surprise developers who never noticed the property while it was hidden. → **Mitigation**: matches Gallery's existing (never-hidden) behavior for the same property; the new `check()` warning immediately explains the "both" caveat if they hit it.

## Migration Plan

No data migration. Widget-level runtime/config-only change, shipped as a normal `datagrid-web` release. No rollback complexity beyond a standard revert.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Why

Data Grid 2's "Position of pagination" property (`pagingPosition`: Above grid / Below grid / Both) already gates the built-in `Pagination` control in both `WidgetTopBar` and `WidgetFooter`. Custom pagination — the developer-supplied widgets placeholder shown when "Custom pagination" is enabled — ignores it completely: `WidgetFooter` renders the custom pagination widgets unconditionally whenever custom pagination is on, and `WidgetTopBar` never renders them at all. Setting "Above grid" has no effect; the widgets always render below the grid (WC-3548).

Gallery had the identical bug and fixed it under WC-3505, which explicitly scoped Data Grid 2 out to keep that change reviewable ("DataGrid 2's custom-pagination position bug ... needs its own ticket"). This change is that ticket. Unlike Gallery, Data Grid 2's editor preview already agrees with its runtime (both footer-only today), so there is no preview/runtime divergence to fix here — only the ineffective property, plus a related Studio Pro editor-config quirk: when custom pagination is enabled, `pagingPosition` is currently hidden from the properties panel entirely (a pre-existing mitigation for the very bug this change fixes), so once the position has a real effect it must be shown again.

## What Changes

- **`WidgetTopBar` renders custom pagination widgets when `pagingPosition` is `"top"`.** Today it only ever renders the built-in `Pagination` component and never checks for custom pagination.
- **`WidgetFooter` renders custom pagination widgets only when `pagingPosition` is not `"top"`** (i.e. `"bottom"` or `"both"`), instead of unconditionally.
- **`"Both"` renders custom pagination once, in the footer**, not in both bars — duplicating the `customPagination` widgets placeholder would duplicate widget instances, DOM ids, and state, exactly as identified in the Gallery fix.
- **A design-time `check()` warning** is added for the `useCustomPagination && pagingPosition === "both"` combination, explaining that custom pagination renders once (below the grid) and suggesting the developer pick a single position.
- **`Datagrid.editorConfig.ts` stops hiding `pagingPosition`** when custom pagination is enabled — the property now has an effect, so it must stay visible and editable (mirrors Gallery, which never hid it in this case). `showPagingButtons` stays hidden for custom pagination since it only affects the built-in control.
- **`Datagrid.editorPreview.tsx`** picks up the same top/footer/both rule so preview continues to agree with runtime.

## Capabilities

### New Capabilities

- `datagrid-custom-pagination-position`: where Data Grid 2's custom pagination widgets render relative to the grid, honouring `Position of pagination` ("Above grid" / "Below grid" / "Both"), including the single-render rule for "Both", the accompanying design-time warning, and parity between runtime and editor preview.

### Modified Capabilities

_None — `openspec/specs/` in this package currently documents no pagination-placement capability, so this is captured as a new capability rather than a delta._

## Impact

`packages/pluggableWidgets/datagrid-web`

- `src/components/WidgetFooter.tsx`, `src/components/WidgetTopBar.tsx` — gate custom pagination rendering on `pagingPosition`.
- `src/Datagrid.editorConfig.ts` — stop hiding `pagingPosition` for custom pagination; add the `"both"` + custom pagination warning (in `src/consistency-check.ts`, which `editorConfig.ts` re-exports `check` from).
- `src/Datagrid.editorPreview.tsx` — mirror the top/footer/both placement rule for the preview's `CustomPagination` placeholder.
- `src/components/__tests__/` — unit tests covering `pagingPosition` × custom pagination for both bars; `consistency-check` tests for the new warning.
- `CHANGELOG.md` — user-facing entry: custom pagination now respects "Position of pagination".

Cross-cutting

- No shared-package changes (`widget-plugin-grid` is untouched; Data Grid 2 has no pagination-alignment design property, unlike Gallery, so no `data-widgets` module changes are needed).
- No breaking changes: `pagingPosition` and `useCustomPagination` keep their existing keys and defaults; only the rendering behavior for an already-visible property changes.
- Release vehicle: `datagrid-web` patch/minor release; version bump and changelog entry added at release time per repo convention.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## ADDED Requirements

### Requirement: Custom pagination honors the position setting

When custom pagination is enabled, Data Grid 2 SHALL render the custom pagination widgets according to the `pagingPosition` property ("Above grid" / "Below grid" / "Both") instead of always rendering them in the footer.

#### Scenario: Position set to "Above grid"

- **WHEN** custom pagination is enabled and `pagingPosition` is `"top"`
- **THEN** the custom pagination widgets render in the top bar
- **AND** the custom pagination widgets do not render in the footer

#### Scenario: Position set to "Below grid"

- **WHEN** custom pagination is enabled and `pagingPosition` is `"bottom"`
- **THEN** the custom pagination widgets render in the footer
- **AND** the custom pagination widgets do not render in the top bar

#### Scenario: Position set to "Both"

- **WHEN** custom pagination is enabled and `pagingPosition` is `"both"`
- **THEN** the custom pagination widgets render exactly once, in the footer
- **AND** the custom pagination widgets do not also render in the top bar

### Requirement: Editor preview matches runtime placement

Studio Pro's editor preview for Data Grid 2 SHALL place the custom pagination placeholder using the same `pagingPosition` rule as the runtime, so the page editor never disagrees with the running app.

#### Scenario: Preview reflects "Above grid"

- **WHEN** custom pagination is enabled and `pagingPosition` is `"top"` in the page editor
- **THEN** the custom pagination placeholder renders above the grid preview

#### Scenario: Preview reflects "Below grid" or "Both"

- **WHEN** custom pagination is enabled and `pagingPosition` is `"bottom"` or `"both"` in the page editor
- **THEN** the custom pagination placeholder renders below the grid preview, exactly once

### Requirement: Position property remains configurable with custom pagination

The `pagingPosition` property SHALL remain visible and editable in the Studio Pro properties panel when custom pagination is enabled, since the property now affects rendering.

#### Scenario: Custom pagination enabled

- **WHEN** a developer enables custom pagination on a Data Grid 2 configured with `pagination` set to `"buttons"`
- **THEN** the "Position of pagination" property remains visible and editable in the properties panel

### Requirement: Design-time warning for "Both" with custom pagination

Data Grid 2's consistency check SHALL emit a warning when custom pagination is enabled and `pagingPosition` is `"both"`, explaining that the widgets render once, below the grid.

#### Scenario: Custom pagination with "Both" position configured

- **WHEN** a developer enables custom pagination and sets `pagingPosition` to `"both"`
- **THEN** Studio Pro shows a warning on the `pagingPosition` property explaining that custom pagination renders once, below the grid, and suggesting a single position instead
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## 1. Runtime placement

- [x] 1.1 `WidgetFooter.tsx`: gate the existing `<If condition={pgConfig.customPaginationEnabled}>` custom pagination block on `pgConfig.pagingPosition !== "top"`.
- [x] 1.2 `WidgetTopBar.tsx`: add a custom pagination branch that renders `customPagination.get()` when `pgConfig.customPaginationEnabled && pgConfig.pagingPosition === "top"` (needs `useCustomPagination` from `../model/hooks/injection-hooks`, not currently imported there).
- [x] 1.3 Unit tests for `WidgetFooter`/`WidgetTopBar` (or their existing test files) covering `pagingPosition` × `useCustomPagination` for `"top"`, `"bottom"`, `"both"`, and custom pagination disabled.

## 2. Editor preview

- [x] 2.1 `Datagrid.editorPreview.tsx`: add a `usePagingTopCustom`/equivalent check (or extend `usePagingTop`) so the top bar's preview renders `<CustomPagination />` when `useCustomPagination && pagingPosition === "top"`.
- [x] 2.2 `Datagrid.editorPreview.tsx`: change the footer's `useCustomPagination()` check so it excludes `pagingPosition === "top"` (footer renders custom pagination for `"bottom"` and `"both"` only).
- [x] 2.3 ~~Update/add preview snapshot or structure tests~~ — not feasible: `Datagrid.editorPreview.tsx` imports `mendix/preview/Selectable`, which has no jest moduleNameMapper stub anywhere in this repo (confirmed repo-wide: no `*-web` package unit-tests its `editorPreview.tsx`). Adding one would mean changing the shared `@mendix/pluggable-widgets-tools` jest preset, out of scope for this fix. Verified instead by code inspection (mirrors the already-tested `WidgetTopBar`/`WidgetFooter` runtime logic 1:1) plus manual Studio Pro QA.

## 3. Studio Pro properties panel

- [x] 3.1 `Datagrid.editorConfig.ts`: in the `useCustomPagination` branch (inside `pagination === "buttons"`), stop hiding `pagingPosition` — only hide `showPagingButtons`.
- [x] 3.2 Add a `checkCustomPaginationPosition` (or similarly named) check to `consistency-check.ts`: emit a `"warning"` on property `pagingPosition` when `values.useCustomPagination && values.pagingPosition === "both"`, wired into `check()` alongside the existing selection/column checks.
- [x] 3.3 Unit test for the new consistency-check warning (present for custom+both, absent for custom+top/bottom and for non-custom pagination).

## 4. Docs

- [x] 4.1 Add a `CHANGELOG.md` entry: custom pagination now respects "Position of pagination" (top/bottom/both, with "both" rendering once below the grid).
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function getProperties(values: DatagridPreviewProps, defaultProperties: P
if (values.useCustomPagination === false) {
hidePropertyIn(defaultProperties, values, "customPagination");
} else {
hidePropertiesIn(defaultProperties, values, ["pagingPosition", "showPagingButtons"]);
hidePropertyIn(defaultProperties, values, "showPagingButtons");
}
} else {
hidePropertyIn(defaultProperties, values, "showPagingButtons");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ function WidgetTopBar(): ReactElement {
<div className={cls.topBar}>
<div className={cls.pagingTop}>
<div className={cls.ptStart}>{useTopCounter() ? <SelectionCounter /> : null}</div>
<div className={cls.ptEnd}>{usePagingTop() ? <Pagination /> : null}</div>
<div className={cls.ptEnd}>
{usePagingTop() ? <Pagination /> : null}
{useCustomPaginationTop() ? <CustomPagination /> : null}
</div>
</div>
</div>
);
Expand Down Expand Up @@ -139,7 +142,7 @@ function WidgetFooter(): ReactElement {
</div>
<div className={cls.pbEnd}>
{usePagingBot() ? <Pagination /> : null}
{useCustomPagination() ? <CustomPagination /> : null}
{useCustomPaginationBottom() ? <CustomPagination /> : null}
</div>
</div>
</div>
Expand Down Expand Up @@ -403,7 +406,12 @@ function usePagingBot(): boolean {
return visible && props.pagingPosition !== "top";
}

function useCustomPagination(): boolean {
function useCustomPaginationTop(): boolean {
const props = useProps();
return props.useCustomPagination && props.pagingPosition === "top";
}

function useCustomPaginationBottom(): boolean {
const props = useProps();
return props.useCustomPagination;
return props.useCustomPagination && props.pagingPosition !== "top";
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`consistency check custom pagination position warns when custom pagination is enabled and position is both 1`] = `
[
{
"message": "Custom pagination cannot be shown in both positions and will render below the grid. Set "Position of pagination" to "Above grid" or "Below grid" to choose a single position.",
"property": "pagingPosition",
"severity": "warning",
},
]
`;

exports[`consistency check returns error when row select method and action trigger overlap selection: multi by row click 1`] = `
[
{
Expand Down
Loading
Loading