Open the meta query builder in a modal with condition cards, nested groups, and a placeholder reference - #179
ryanwelcher wants to merge 15 commits into
Conversation
Add a generic QueryBuilderModal shell (trigger button plus core Modal) and render the existing meta builder content inside it unchanged. Edits still write to block attributes live. The trigger keeps its accessible name so existing e2e specs pass untouched.
Each condition shows key, compare, and type on one row with the value below; the Advanced mode toggle and its forced-on logic are removed. The relation control moves to the top of the modal as an All/Any toggle, Add and Reset live in a sticky footer provided by the shell, and the sidebar shows a condition count summary while the modal is closed.
List every registered dynamic placeholder with its label and description beside the conditions, filterable by text. Clicking an entry writes its token into the most recently focused Meta Value field; with no field focused it explains what to do instead. The column is hidden when no placeholders are registered.
Hide text-only operators (LIKE, REGEXP, RLIKE and their negations) for numeric and date types while keeping any operator a saved block already uses. EXISTS and NOT EXISTS hide the value field and clear the stored value. IN and BETWEEN show input hints, and DATE, DATETIME, and TIME types offer an inline picker that writes the format WP_Query expects. Also overlay the token field suggestion lists instead of pushing content down: an inline list collapsing on blur shifted the controls below it between mousedown and mouseup and swallowed the click.
An entry in meta_query.queries may now be a group with its own relation and nested queries. The Meta_Query trait recurses through groups and emits the nested clauses WP_Query expects; flat arrays produce the same output as before, and groups left without conditions are dropped. In the editor a group renders as a bordered card with its own All/Any toggle, its conditions, and an Add condition action. Groups are added from the modal footer only, capping nesting at two levels. The sidebar summary counts conditions across groups, and placeholder insertion finds the focused condition at any depth. Also hide a token field's suggestion overlay once it holds its single token, so the overlay no longer covers the controls beneath it. Fixes #100.
Compare and type only matter when they leave the defaults, so they sit behind a per-condition Advanced mode toggle again. A condition already using non-default values shows them and locks the toggle on, so nothing saved is ever hidden. The Remove query button moves to a footer row under the value field alongside the toggle.
8cbb3f6 to
871c4bb
Compare
The meta query modal's placeholder column was a sticky sidebar with a search box and click-to-insert entries. With only two dozen placeholders the search added little, and the Meta Value field already offers the same list as suggestions, so click-to-insert duplicated it. Replace the column with an info button above the conditions that toggles a reference-only panel. The panel lists every registered placeholder with its label, literal token, and description in a responsive grid, and closes from the button or its own close control. It is hidden by default and its state resets each time the modal opens. Remove the insert plumbing this makes redundant: the focused-condition tracking, the insert notice, and the onValueFocus prop threaded through the condition list and card.
Users pick placeholders by label from the Meta Value field and only see
the {aql:} form in the saved markup, so listing it added noise.
Split the placeholder reference into three PanelBody sections, all collapsed when the panel opens: Content & users, Current date & time, and Relative dates. Each title carries its item count. The grouping is an editor-only lookup keyed by placeholder name; anything not in it, such as third-party placeholders, falls into an Other section that renders only when it has entries. No PHP or API changes.
Move the info button into the sticky footer, right-aligned opposite the Add/Reset actions, and show the reference in a Popover anchored to it. The popover floats over the conditions instead of pushing them down, and closes on Escape, an outside click, or its own close button without closing the modal.
…at a time Anchor the popover below the modal header, centered, with flipping off so its top edge stays put and it grows downward as sections expand. The grouped sections are now controlled and behave as an accordion: opening one closes the others.
There was a problem hiding this comment.
🟡 Changes recommended
There are a couple of confirmed runtime/API issues in the new modal builder code paths (spreading possibly-undefined meta_query and passing unsupported props to DatePicker) that should be fixed before merge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR moves the Post Meta Query builder UI into a dedicated modal and reworks the meta-query editing experience (condition cards, advanced controls, placeholder reference popover), while extending server-side meta query generation (and PHPUnit coverage) to support nested meta query groups.
Changes:
- Introduces a reusable
QueryBuilderModalshell and migrates the meta query builder into it with a sticky footer. - Adds a meta-query entry tree model (conditions + nested groups) and a recursive UI renderer for cards/groups, plus a placeholder reference popover.
- Updates the PHP
Meta_Querytrait to recursively translate nested groups into WP_Query-compatiblemeta_queryclauses and adds unit/e2e coverage for the new behavior.
File summaries
| File | Description |
|---|---|
| tests/unit/Meta_Query_Tests.php | Adds PHPUnit coverage for nested meta_query groups, empty-group dropping, and parity cases. |
| tests/e2e/tests/meta-query-modal.spec.ts | Adds Playwright coverage for the modal UI, cards, placeholder reference, operator/type guidance, and groups. |
| src/variations/editor.scss | Adds styling for modal layout, condition cards/groups, placeholder reference popover, and token-field suggestion overlay behavior. |
| src/utils/meta-query-tree.js | Introduces utilities for representing and manipulating a condition/group meta-query tree. |
| src/components/query-builder-modal.js | Adds the generic modal shell wrapper for query builders. |
| src/components/post-meta-query-controls.js | Replaces the flyout dropdown meta builder with the modal-based builder and footer actions. |
| src/components/post-meta-control.js | Refactors a single condition into a “card” UI with Advanced mode, operator gating, and date picker integration. |
| src/components/placeholder-text-control.js | Adds optional onFocus and help plumbing to support improved guidance in the modal. |
| src/components/placeholder-reference.js | Adds the placeholder reference-only popover panel UI and grouping. |
| src/components/meta-value-picker.js | Adds inline DATE/DATETIME/TIME pickers that write values in WP_Query-compatible formats. |
| src/components/meta-condition-list.js | Adds recursive renderer for conditions and one-level nested groups with relation toggles. |
| includes/Traits/Meta_Query.php | Adds recursive parsing for nested group entries into WP_Query meta_query clauses. |
Review details
Suppressed comments (1)
src/components/post-meta-query-controls.js:82
attributes.query.meta_querycan beundefinedinitially, so spreading it here can throw before any meta query has been created. Use a nullish fallback when spreading.
meta_query: {
...attributes.query.meta_query,
queries: newQueries,
},
- Files reviewed: 12/12 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| { items.map( ( { name, label, description } ) => ( | ||
| <div key={ name } className="aql-placeholder-reference__item"> | ||
| <dt className="aql-placeholder-reference__label">{ label }</dt> | ||
| { description && <dd>{ description }</dd> } | ||
| </div> |
… open The accordion assertions added in c266b94 collapse the Content & users group before the older assertion that expects that group's description text to be visible, so the test contradicted itself and failed on CI.
Render the DATE, DATETIME and TIME pickers explicitly so is12Hour is only passed to the pickers that accept it, and store an empty string instead of undefined when the Meta Key token field is cleared.
There was a problem hiding this comment.
🔵 Needs a closer look
There is a confirmed runtime-crash risk in the updated meta query controls when meta_query is absent (e.g., after reset) due to spreading attributes.query.meta_query without a null-safe fallback.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
tests/e2e/tests/meta-query-modal.spec.ts:671
- Fixed
waitForTimeoutsleeps tend to make Playwright specs flaky across environments; it’s more reliable to wait on a specific editor state (e.g. poll until the derived ID set reaches the expected length).
src/components/post-meta-query-controls.js:67
attributes.query.meta_querycan beundefined(e.g. after MetaQueryGroupControls deletes it on reset), so spreading it here will throw when adding conditions or changing relation. Use a nullish-coalesced spread so the builder works from a clean slate.
const setRelation = ( newRelation ) => {
setAttributes( {
query: {
...attributes.query,
meta_query: {
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Lite
…d query e2e test Spreading an undefined meta_query is legal, but the nullish fallback makes the clean-slate case explicit. The nested query test now polls the editor preview for the expected post count instead of a fixed sleep, and the ten-condition layout test is marked slow for constrained runners.
The test builds three conditions, publishes and loads the frontend, which ran past Playwright's 30s default on a slow runner. Once the test timed out, the fixture tore Playground down, so the frontend request surfaced as a connection refusal rather than the timeout.
Summary
Moves the Meta Query builder out of the inspector flyout and into a full modal, then reworks the builder inside it.
Stacked on
feature/dynamic-placeholdersso this diff only contains the modal work. The placeholder branch should land first.QueryBuilderModalcomponent (trigger button + coreModalwith a sticky footer). The trigger keeps its existing accessible name so prior e2e specs pass untouched.meta_query.queriesmay now be a group with its own relation and nested queries. TheMeta_Querytrait recurses and emits the nested clauses WP_Query expects. Flat arrays produce identical output to before, and groups left without conditions are dropped. Nesting is capped at two levels in the UI. Fixes Allow nested meta_query with relation setting #100.Testing
npm run test:unit— 209 tests, 344 assertions, all passing. New coverage intests/unit/Meta_Query_Tests.phpfor nested groups, empty-group dropping and flat-array parity.npm run lint:jsandnpm run build— clean.tests/e2e/tests/meta-query-modal.spec.tscovers the modal, cards, placeholder column, operator gating, groups and Advanced mode. Not run in this session (needs a wp-env instance).