Skip to content

Closes #5156: Fix Update Preview and Add Another Item Reorder Issue for AZ Ranking - #5309

Closed
kevdevlu wants to merge 13 commits into
mainfrom
issue/5156
Closed

Closes #5156: Fix Update Preview and Add Another Item Reorder Issue for AZ Ranking#5309
kevdevlu wants to merge 13 commits into
mainfrom
issue/5156

Conversation

@kevdevlu

@kevdevlu kevdevlu commented Feb 13, 2026

Copy link
Copy Markdown
Member

Description

Closes #5156 - ranking widget previews getting out of sync with their form fields after drag-and-drop reordering and clicking ajax buttons: "Update Preview", "Add Another Item", or "Remove".

The Core Problem

In formElement(), we set both details (form elements like textfields and selects) and preview_wrapper (that renders the preview for the ranking item). The KEY ISSUE is: after formElement() returns, Drupal's Form API treats these two children differently:

  • details contains standard form elements (#type => textfield, select, etc.). The Form API automatically populates their #value from user input, keyed by the original form delta. This means after drag-and-drop reorder, the fields always show the correct data for their row.

  • preview_wrapper is a plain render array (#theme => az_ranking). The Form API does not touch render arrays — whatever values are set in formElement() remain as-is. The preview was originally built from $items[$delta], which could be in a different order than user input during AJAX rebuilds.

The Fix

If the fields are ALWAYS in the right order after ajax requests, why not build previews using the same Drupal form processing pipeline as the fields?! So instead of fidgeting / playing around with widgetState and trying to hack the deltas / original deltas using some sort of mapping: WE JUST BUILD THE PREVIEW AFTER the fields have been built.

  • Use #after_build callback:
    $element['#after_build'][] = [$this, 'afterBuildRebuildPreview'];
    This builds the preview from the same Form API-populated #value sources as the form fields. We use this to replace the current function that builds from $item (buildRankingPreview()).
  • The media field required special treatment...since the media library widget's #value structure differs from simple form elements. We just read from $form_state->getUserInput() to ensure it matches the same delta order as text fields.
  • Table-drag handles visual row reordering on the client by moving entire table rows (preview + fields together), so no server-side delta remapping is needed (yay!)

Release notes

Related issues

How to test

Make sure az_paragraphs_rankings is enabled

  • Create a ranking paragraph with 4+ ranking items (mix of "standard" and "image_only" types)
  • Verify previews render correctly on initial page load
  • Drag-and-drop to reorder items once or multiple times, then click "Update Preview" or "Add Another Item" — verify previews match their corresponding fields
  • Specifically test that image_only rankings keep their images on the correct card after reorder
  • Test collapse/expand of "Edit Ranking" details panels after reorder
  • Test the "Remove" button (on new items and existing items) after reorder
  • Test removing an existing item, and then creating a new item
  • Save the form and verify the saved order is correct

Types of changes

Arizona Quickstart (install profile, custom modules, custom theme)

  • Patch release changes
    • Bug fix
    • Accessibility, performance, or security improvement
    • Critical institutional link or brand change
    • Adding experimental module
    • Update experimental module
  • Minor release changes
    • New feature
    • Breaking or visual change to existing behavior
    • Upgrade experimental module to stable
    • Enable existing module by default or database update
    • Non-critical brand change
    • New internal API or API improvement with backwards compatibility
    • Risky or disruptive cleanup to comply with coding standards
    • High-risk or disruptive change (requires upgrade path, risks regression, etc.)
  • Other or unknown
    • Other or unknown

Drupal core

  • Patch release changes
    • Security update
    • Patch level release (non-security bug-fix release)
    • Patch removal that's no longer necessary
  • Minor release changes
    • Major or minor level update
  • Other or unknown
    • Other or unknown

Drupal contrib projects

  • Patch release changes
    • Security update
    • Patch or minor level update
    • Add new module
    • Patch removal that's no longer necessary
  • Minor release changes
    • Major level update
  • Other or unknown
    • Other or unknown

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • My change requires release notes.

@kevdevlu
kevdevlu requested review from a team as code owners February 13, 2026 05:33
@kevdevlu kevdevlu self-assigned this Feb 13, 2026
@kevdevlu kevdevlu added the bug Something isn't working label Feb 13, 2026
@tadean
tadean self-requested a review February 13, 2026 16:45
@kevdevlu

kevdevlu commented Feb 13, 2026

Copy link
Copy Markdown
Member Author

Test Cases

A working fix will be able to solve all 3 of these issues:

  1. Removal of existing ranking card causing errors after removal. How to reproduce:
    Make 4 rankings.
    a. Initial order is: A B C D
    b. Move A to position 3, so it': B C A D
    c. Move D to position 2, so it's B D C A
    d. Remove the last ranking
    e. Clicking on Update Preview and Add Another Item return Error: Call to a member function isEmpty() on null in Drupal\az_ranking\Plugin\Field\FieldWidget\AZRankingWidget->formElement() (line 144) errors
    Screen record: https://emailarizona-my.sharepoint.com/:v:/g/personal/kevinwlu_arizona_edu/IQAJNntRWcyKRKMBEXAF1sAcAexwDhgTWak_FsjsHouNyTM?e=6dJ18Q

  2. On main branch, do the same steps as above:
    Make 4 rankings.
    a. Initial order is: A B C D
    b. Move A to position 3, so it': B C A D
    c. Move D to position 2, so it's B D C A
    d. Remove the last ranking
    e. Add a new ranking produces an empty New Ranking Card with no fields (this is an issue with the deltas messing with the unique id's used by the form API states).
    Screen record: https://emailarizona-my.sharepoint.com/:v:/g/personal/kevinwlu_arizona_edu/IQBBbJQwHP8OQqwxvieLpCWnAZJBf6WzWFLEOP84h9brPeo?e=KbDXSx

  3. After all fixes, ensure the initial problem case from the az_ranking re-order preview issue #5156 are fixed. How to reproduce: same steps as 1 above:
    a. Initial order is: A B C D
    b. Move A to position 3, so it': B C A D
    c. Move D to position 2, so it's B D C A
    d. Click on Update Preview. Make sure previews and fields are in B D C A order.
    e. Click on Add Another Item. Make sure previews and fields are in B D C A order.
    f. Click on Remove on the newly added item. Make sure previews and fields are in B D C A order.

The #after_build callback builds previews from Form API-populated values (which are always in the correct order after re-ordering). Our method before was messy and unreliable: trying to use widget_state and deltas to guess where the preview should go.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request fixes a bug where ranking widget previews become out of sync with their corresponding form fields after drag-and-drop reordering and triggering AJAX actions (Update Preview, Add Another Item, or Remove buttons). The core issue was that previews were built from $items which could be in a different order than user input during AJAX rebuilds, while form fields were automatically populated by Drupal's Form API in the correct order.

Changes:

  • Replaced preview building logic from $items to use Form API-populated field values via an #after_build callback
  • Removed the buildRankingPreview() method that built previews from $items in the wrong order
  • Added afterBuildRebuildPreview() static method that builds previews from form field #value properties after the Form API has processed them

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread modules/custom/az_ranking/src/Plugin/Field/FieldWidget/AZRankingWidget.php Outdated
Comment thread modules/custom/az_ranking/src/Plugin/Field/FieldWidget/AZRankingWidget.php Outdated
kevdevlu and others added 2 commits February 18, 2026 08:36
…vior

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kevdevlu
kevdevlu requested a review from Copilot February 18, 2026 15:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread modules/custom/az_ranking/src/Plugin/Field/FieldWidget/AZRankingWidget.php Outdated
Comment thread modules/custom/az_ranking/src/Plugin/Field/FieldWidget/AZRankingWidget.php Outdated

Copilot AI commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

@kevdevlu I've opened a new pull request, #5322, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

@kevdevlu I've opened a new pull request, #5323, to work on those changes. Once the pull request is ready, I'll request review from you.

- Use $media_input === NULL instead of !$form_state->isRebuilding()
  to detect initial load, covering validation errors and other
  non-rebuilding states where user input is present.
- Build CSS classes as an array using explode/array_filter/array_merge
  instead of string concatenation to avoid duplicate spaces.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

kevdevlu added a commit that referenced this pull request Jul 27, 2026
AZRankingComponentBuilder becomes the single source of truth mapping
ranking item values to az_quickstart:ranking/ranking-image render
arrays, shared by AZRankingDefaultFormatter and AZRankingWidget's live
edit-form preview - previously the widget preview still rendered
through the legacy #theme => 'az_ranking' template/hook_theme path
(az-ranking.html.twig, az_ranking_theme(), plus its own now-dead
az-ranking.css/az-ranking-image.css/az-ranking-focal-point-calc.js),
duplicating the formatter's class-mapping logic and free to drift out
of sync with what actually publishes. Both callers now build a plain
values array (AZRankingComponentBuilder::extractItemValues()) instead
of passing an AZRankingItem directly, since the widget's preview needs
to build that same array from Form API values instead of a field item
(see below) - keeping the builder item-agnostic is what lets it work
for both.

Also fixes az_quickstart#5156: after drag-and-drop reordering a
paragraph's ranking items, clicking "Update Preview"/"Add Another
Item"/"Remove" could leave previews showing stale data for the wrong
row. Root cause: the preview was built from $items[$delta] (stored
array order), while its sibling text/select fields are repopulated by
the Form API from #value (always correct for the current row, since
that's tree-position-based, not stored-order-based). Fixed the way
az_quickstart/az_quickstart#5309 fixed it upstream: build the preview
from the same Form API-populated #value the fields use, instead of the
item, via a new custom Form API element (AZRankingItemElement) whose
#process builds the fields and #after_build rebuilds the preview once
its siblings have resolved - a real, reusable Element plugin instead
of #after_build glue bolted onto Field API's own per-delta wrapper, so
the preview keeps direct access to its sibling fields (an element
scoped to the preview alone would lose that, since #after_build only
sees its own descendants). Also drops the old original_deltas widget-
state remap, which #5309 found actively breaks reorder rather than
protecting it - table-drag already moves whole rows (fields + preview
together) client-side, so no server-side delta remapping is needed.

Verified via a real Drupal form-build cycle (\Drupal::formBuilder()
->doBuildForm()) against real paragraph data, not just isolated calls:
confirmed #process and #after_build both fire in the correct order and
the resulting preview matches the real stored values exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZ6mWfeVEr5qdPLXAEmtqP
@kevdevlu

kevdevlu commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

A better version of this is implemented in #5834 , taking into consideration Troy's recommendation to use a custom form api element for Rankings instead of using an #after-build callback (this PR).

@kevdevlu kevdevlu closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

az_ranking re-order preview issue

3 participants