Skip to content

[WC-3505]: Gallery pagination design properties are not applied - #2388

Merged
yordan-st merged 12 commits into
mainfrom
fix/WC-3505_gallery-design-properties-not-updated
Sep 10, 2026
Merged

[WC-3505]: Gallery pagination design properties are not applied#2388
yordan-st merged 12 commits into
mainfrom
fix/WC-3505_gallery-design-properties-not-updated

Conversation

@yordan-st

@yordan-st yordan-st commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Pull request type

Bug fix (non-breaking change which fixes an issue)


Description

Gallery's Pagination design property (Left / Center) has done nothing since the pagination overhaul. Its CSS keyed off a .widget-gallery-pagination wrapper that the overhaul deleted, so pagination was always right-aligned regardless of the setting.

Restoring it is not a CSS change. Pagination now lives in a three-zone flex bar (*-start / *-middle / *-end) where the zone, not the bar, decides position, and those zones are already used by the selection counter and the load more button. Overriding justify-content inside the end zone would centre pagination at ~83% of the bar width, and doing it with CSS order or grid placement would reorder visually while leaving DOM order fixed — a WCAG 2.4.3 (Focus Order) and 1.3.2 (Meaningful Sequence) defect for a paging control.

So placement is decided in markup by a pure function, resolveZones:

alignment pagination zone displaced to end zone untouched
Left *-start selection counter load more stays in *-middle
Center *-middle load more button counter stays in *-start
Right *-end nothing everything

The rule is total: at most three occupants, three zones, and custom pagination replaces the built-in bar rather than adding to it, so at most one occupant is ever displaced. Displacement was chosen over wrapping to a second row because the counter appears dynamically at selected > 0 — wrapping would shift the page the moment a user selects their first item.

The same result drives the footer, the top bar and the editor preview, so they cannot drift apart. That drift is what produced the second and third fixes below.

Also fixed here

  • Custom pagination ignored Position of pagination at runtime — it always rendered below the gallery. Above grid now renders it in the top bar. Both renders it once in the footer, because a widgets placeholder rendered twice would duplicate widget instances, DOM ids and state; check() now surfaces a warning explaining that.
  • The editor preview disagreed with runtime on that same setting, showing custom pagination above the gallery while the app rendered it below.

Design property changes (data-widgets)

  • Pagination is now a toggle button group with Atlas align icons, matching every other alignment control in Studio Pro, and gains an explicit Right option.
  • Property and option names are deliberately unchanged. Studio Pro stores design property selections by property and option name, not by CSS class, so renaming raises CE6083 and CE6087 in every existing app until a developer runs "Update all renamed design properties in project". Verified in Studio Pro during development.
  • Dead .widget-gallery-pagination rules removed; fc-middle / tb-middle are now real flex zones so Center is centred by construction rather than incidentally.

Accessibility note, intended: DOM order stays start → middle → end, so alignment now also determines tab and reading order — Left puts the paging controls before the Clear selection button. Visual and focus order stay in agreement, which the CSS-only alternatives could not achieve.

Not in this PR: DataGrid 2's -padding-top container-query typo and its identical custom-pagination position bug ship separately so Gallery and DataGrid 2 changes stay reviewable apart. [WC-3547 Data Grid 2: Stack top bar in narrow containers](#2389 (comment))

Tests: 145 unit tests pass (was 131). resolveZones is covered by an exhaustive 24-combination invariant pass; the bar components are asserted against real rendered DOM per zone.

Planning artifacts: packages/pluggableWidgets/gallery-web/openspec/changes/fix-gallery-pagination-placement/.

What should be covered while testing?

Build both packages into a test project:

  • packages/pluggableWidgets/gallery-web
  • packages/modules/data-widgets
  1. Alignment × position. Gallery with Pagination = Paging buttons. For each of Left / Center / Right, check Position of pagination = Below grid, Above grid, Both. Pagination should sit hard left, truly centred on the bar, or hard right, in both bars.
  2. Displacement. Enable multi selection with Show selection count = Bottom. Select an item. With Left, pagination takes the left slot and the count moves right; with Center, the count stays left. Repeat with Show selection count = Top against the top bar.
  3. No layout shift. Selecting the first item must not change the footer height or add a row.
  4. All three occupants. Pagination = Load more, Show total count on, selection active. Center should give: count left, paging status centred, Load more right.
  5. Custom pagination. Enable it and check Above grid (renders in the top bar), Below grid (footer), Both (footer only, plus a warning on the widget in Studio Pro). Confirm alignment moves the custom widgets too.
  6. Narrow width. Shrink the gallery container below 500px — the bar should still stack vertically and centre everything, whatever the alignment.
  7. Design mode. The page editor should place everything where the running app does, including custom pagination.
  8. Keyboard. Tab through both bars for each alignment; focus order must follow visual order.
  9. Upgrade safety. An existing page that already had Left or Center selected must show no errors and keep its selection.

@yordan-st
yordan-st force-pushed the fix/WC-3505_gallery-design-properties-not-updated branch from 267ca2a to b97098c Compare August 18, 2026 09:28
@yordan-st
yordan-st marked this pull request as ready for review August 18, 2026 09:28
@yordan-st
yordan-st requested a review from a team as a code owner August 18, 2026 09:28
Comment thread packages/pluggableWidgets/gallery-web/src/helpers/resolveZones.ts Outdated

@iobuhov iobuhov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the biggest changes I need is to replace word "zone" with "slot". Slot is more known term in frontend to denote areas where you can put your elements.
Please rewrite spec, comments, variables and other identifiers. Word "zone" is bit too alien.

Comment thread packages/pluggableWidgets/gallery-web/src/components/GalleryTopBarControls.tsx Outdated
Comment thread packages/pluggableWidgets/gallery-web/src/components/GalleryTopBarControls.tsx Outdated
Comment thread packages/pluggableWidgets/gallery-web/src/components/GalleryFooterControls.tsx Outdated
@yordan-st
yordan-st force-pushed the fix/WC-3505_gallery-design-properties-not-updated branch from bd1edd0 to 8060d99 Compare August 21, 2026 13:53
@yordan-st
yordan-st requested a review from iobuhov August 21, 2026 13:53
@github-actions

This comment has been minimized.

gjulivan
gjulivan previously approved these changes Aug 31, 2026
@yordan-st
yordan-st force-pushed the fix/WC-3505_gallery-design-properties-not-updated branch from 8060d99 to 4ea9fb2 Compare September 7, 2026 13:05
@github-actions

This comment has been minimized.

@yordan-st
yordan-st force-pushed the fix/WC-3505_gallery-design-properties-not-updated branch from 595eb63 to 86ccbf4 Compare September 8, 2026 14:55
@github-actions

This comment has been minimized.

@yordan-st
yordan-st force-pushed the fix/WC-3505_gallery-design-properties-not-updated branch from 86ccbf4 to 351ec06 Compare September 9, 2026 09:46
leonardomendix
leonardomendix previously approved these changes Sep 9, 2026
gjulivan
gjulivan previously approved these changes Sep 10, 2026
@yordan-st
yordan-st dismissed stale reviews from gjulivan and leonardomendix via e136e8d September 10, 2026 08:01
@github-actions

This comment has been minimized.

gjulivan
gjulivan previously approved these changes Sep 10, 2026
samuelreichert
samuelreichert previously approved these changes Sep 10, 2026
@yordan-st
yordan-st removed the request for review from iobuhov September 10, 2026 08:28
@yordan-st
yordan-st dismissed stale reviews from samuelreichert and gjulivan via 09dc4dd September 10, 2026 08:34
@yordan-st
yordan-st force-pushed the fix/WC-3505_gallery-design-properties-not-updated branch from e136e8d to 09dc4dd Compare September 10, 2026 08:34
@github-actions

Copy link
Copy Markdown
Contributor

AI Code Review

⚠️ Approved with suggestions — low-severity items only, safe to merge


What was reviewed

File Change
packages/modules/data-widgets/CHANGELOG.md Gallery section added with Changed + Fixed entries
packages/modules/data-widgets/src/themesource/datawidgets/web/_gallery-design-properties.scss Dead .widget-gallery-pagination rule blocks removed; empty marker classes kept
packages/modules/data-widgets/src/themesource/datawidgets/web/_gallery.scss fc-middle/tb-middle promoted to real flex zones; container query updated
packages/modules/data-widgets/src/themesource/datawidgets/web/design-properties.json Pagination changed from Dropdown to ToggleButtonGroup with Right option added
packages/pluggableWidgets/gallery-web/CHANGELOG.md Alignment + custom pagination position fix entries added
packages/pluggableWidgets/gallery-web/src/Gallery.editorConfig.ts check() warning for Both + custom pagination combination
packages/pluggableWidgets/gallery-web/src/Gallery.editorPreview.tsx TopControls and Footer refactored to use resolveSlots; useCustomPagination aligned with runtime
packages/pluggableWidgets/gallery-web/src/components/GalleryFooterControls.tsx Slot-map rendering; FooterPagination / FooterSelectionCounter extracted as observers
packages/pluggableWidgets/gallery-web/src/components/GalleryTopBarControls.tsx Slot-map rendering; new tb-middle slot; TopBarPagination / TopBarSelectionCounter extracted
packages/pluggableWidgets/gallery-web/src/helpers/pagingAlignment.ts New: class→alignment parser with deterministic multi-class tie-breaking
packages/pluggableWidgets/gallery-web/src/helpers/resolveSlots.ts New: pure slot-placement function
packages/pluggableWidgets/gallery-web/src/view-models/GalleryRoot.viewModel.ts pagingAlignment MobX computed added
packages/pluggableWidgets/gallery-web/src/components/__tests__/GalleryBarControls.spec.tsx Integration tests for alignment × slot and custom-pagination position
packages/pluggableWidgets/gallery-web/src/helpers/__tests__/pagingAlignment.spec.ts Unit tests for parser including edge cases
packages/pluggableWidgets/gallery-web/src/helpers/__tests__/resolveSlots.spec.ts 24-combination exhaustive invariant pass for slot function
packages/pluggableWidgets/gallery-web/src/view-models/__tests__/GalleryRoot.viewModel.spec.tsx Tests for pagingAlignment computed and live prop change

Skipped (out of scope): openspec/changes/ planning artifacts, dist/, pnpm-lock.yaml

Note: gh pr checks was not available in this run — CI status unverified. The PR description reports 145 tests pass and lint is clean.


Findings

⚠️ Low — Test description has swapped %s placeholders

File: packages/pluggableWidgets/gallery-web/src/components/__tests__/GalleryBarControls.spec.tsx line 976
Note: The it.each test description template is "renders pagination in the %s slot of the footer for class %s", but the tuple order is [className, slot]. The first %s resolves to the class name and the second to the slot, producing names like "renders pagination in the widget-gallery-pagination-left slot of the footer for class start". The test logic is correct; only the generated test name is misleading in the output.

Fix: Either swap the template placeholders to match the param order:

("renders pagination in the %s slot of the footer for class %s", (slot, className) => {

or reorder the tuple to [slot, className]:

["start", "widget-gallery-pagination-left"],

(same issue exists in the top-bar it.each block on line ~986)


⚠️ Low — Silent no-op if "loadMore" ever reaches a top-bar slot

File: packages/pluggableWidgets/gallery-web/src/components/GalleryTopBarControls.tsx line 47
Note: elements.loadMore is set to null. If resolveSlots ever produced a slot containing "loadMore" (currently structurally impossible because hasLoadMore: false is hardcoded), getElementForSlot would silently render nothing instead of throwing. This is safe today, but the failure mode is invisible. Adding a TypeScript-level guard would make the invariant explicit:

// Option A: narrow the elements type so loadMore is never a valid key here
const elements: Record<Exclude<BarElement, "loadMore">, ReactNode> = { ... };

// Option B: add a runtime assertion in non-production builds
if (process.env.NODE_ENV !== "production" && slots.middle === "loadMore") {
    console.error("GalleryTopBarControls: unexpected loadMore slot — hasLoadMore must be false");
}

Not blocking; just a defensive hygiene note.


Positives

  • resolveSlots is a genuinely pure function — alignment × occupancy × visibility with no side effects. The 24-combination invariant suite (never assigns an element twice + places every visible element exactly once) is exactly the right way to verify this class of logic.
  • The displacement-not-wrapping decision, and the WCAG 2.4.3/1.3.2 reasoning behind rejecting CSS order, are documented directly in design.md with a comparison table. Future maintainers won't have to reconstruct the reasoning.
  • Preserving Left/Center option names while changing the control type from Dropdown to ToggleButtonGroup (with the Right addition) is the correct upgrade-safety call — the Studio Pro CE6083/CE6087 implications are explained clearly.
  • parsePagingAlignment splits on \s+ and uses includes(), so it handles irregular whitespace and correctly ignores prefix-containing class names (e.g. prefixed-widget-gallery-pagination-left). The dedicated spec file tests all documented edge cases including deterministic resolution when multiple alignment classes are present.
  • The pagingAlignment getter on GalleryRootViewModel is a MobX computed over props.class, so Design-mode edits propagate live without requiring a remount — a clean pattern for design-property reactivity.
  • Editor preview parity is enforced structurally: the same resolveSlots call drives TopControls, Footer, GalleryFooterControls, and GalleryTopBarControls, so runtime and preview cannot diverge by construction.

@yordan-st
yordan-st merged commit 0818e7e into main Sep 10, 2026
14 of 15 checks passed
@yordan-st
yordan-st deleted the fix/WC-3505_gallery-design-properties-not-updated branch September 10, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants