Skip to content

1162 add caching for the outline and structure for the suggestions#23182

Merged
pls78 merged 42 commits intofeature/content-plannerfrom
1162-add-caching-for-the-outline-and-structure-for-the-suggestions
Apr 27, 2026
Merged

1162 add caching for the outline and structure for the suggestions#23182
pls78 merged 42 commits intofeature/content-plannerfrom
1162-add-caching-for-the-outline-and-structure-for-the-suggestions

Conversation

@vraja-pro
Copy link
Copy Markdown
Contributor

@vraja-pro vraja-pro commented Apr 21, 2026

Context

  • This PR adds client-side caching of content outlines so that navigating back to the suggestions list and re-selecting the same suggestion restores from cache instead of re-fetching. It also normalizes the API response shape (snake_case → camelCase) in the store and extracts StructureRow and CategorySection into separate component files.

Summary

This PR can be summarized in the following changelog entry:

  • Adds caching to the content outline and refactor components to separate files.
  • Moves category and structures row to separate components.

Relevant technical choices:

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR can be acceptance tested by following these steps:

  • Create a new post.
  • Click on the get suggestion button in the banner for the content suggestions.
  • Open the network tab in the devtools
  • Select one of the suggestions and check you see a request for get_outline.
  • Check focus is on the close button.
  • Check you see the outline modal and edit the title, focus keyphrasr and meta description.
  • Change the order of the titles.
  • Click on the "back to suggestions" button.
  • With the network tab open, click on the same suggestion and check there is no request for get_outline.
  • Check focus is on the close button.
  • Check the outline modal shows your changes to the title, focus keyphrasr and meta description.
  • Check the order of the headings includes your change.
  • Apply the outline and check it is applies successfuly.
  • Repeat the test but select every suggestion before appling it, check changes are saved to the outlines.

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different browsers
  • Changes should be tested on multisite

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

QA can test this PR by following these steps:

Impact check

This PR affects the following parts of the plugin, which may require extra testing:

Other environments

  • This PR also affects Shopify. I have added a changelog entry starting with [shopify-seo], added test instructions for Shopify and attached the Shopify label to this PR.
  • This PR also affects Yoast SEO for Google Docs. I have added a changelog entry starting with [yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached the Google Docs Add-on label to this PR.

Documentation

  • I have written documentation for this change. For example, comments in the Relevant technical choices, comments in the code, documentation on Confluence / shared Google Drive / Yoast developer portal, or other.

Quality assurance

  • I have tested this code to the best of my abilities.
  • During testing, I had activated all plugins that Yoast SEO provides integrations for.
  • I have added unit tests to verify the code works as intended.
  • If any part of the code is behind a feature flag, my test instructions also cover cases where the feature flag is switched off.
  • I have written this PR in accordance with my team's definition of done.
  • I have checked that the base branch is correctly set.
  • I have run grunt build:images and commited the results, if my PR introduces new images or SVGs.

Innovation

  • No innovation project is applicable for this PR.
  • This PR falls under an innovation project. I have attached the innovation label.
  • I have added my hours to the WBSO document.

Fixes https://github.com/Yoast/reserved-tasks/issues/1162

@vraja-pro vraja-pro added the changelog: non-user-facing Needs to be included in the 'Non-userfacing' category in the changelog label Apr 21, 2026
@coveralls
Copy link
Copy Markdown

coveralls commented Apr 21, 2026

Coverage Report for CI Build 0

Coverage decreased (-0.02%) to 53.484%

Details

  • Coverage decreased (-0.02%) from the base build.
  • Patch coverage: 31 uncovered changes across 6 files (37 of 68 lines covered, 54.41%).
  • 3 coverage regressions across 3 files.

Uncovered Changes

File Changed Covered %
packages/js/src/ai-content-planner/store/content-outline.js 19 1 5.26%
packages/js/src/ai-content-planner/hooks/use-fetch-content-outline.js 7 0 0.0%
packages/js/src/ai-content-planner/hooks/use-apply-outline.js 3 0 0.0%
packages/js/src/ai-content-planner/components/category-section.js 8 7 87.5%
packages/js/src/ai-content-planner/containers/outline-modal-content.js 2 1 50.0%
packages/js/src/ai-content-planner/helpers/build-blocks-from-outline.js 1 0 0.0%

Coverage Regressions

3 previously-covered lines in 3 files lost coverage.

File Lines Losing Coverage Coverage
packages/js/src/ai-content-planner/containers/outline-modal-content.js 1 71.43%
packages/js/src/ai-content-planner/hooks/use-apply-outline.js 1 0.0%
packages/js/src/ai-content-planner/helpers/build-blocks-from-outline.js 1 0.0%

Coverage Stats

Coverage Status
Relevant Lines: 66011
Covered Lines: 35170
Line Coverage: 53.28%
Relevant Branches: 16931
Covered Branches: 9191
Branch Coverage: 54.29%
Branches in Coverage %: Yes
Coverage Strength: 45569.78 hits per line

💛 - Coveralls

@FAMarfuaty FAMarfuaty added the innovation Innovative issue. Relating to performance, memory or data-flow. label Apr 22, 2026
@FAMarfuaty FAMarfuaty requested a review from Copilot April 22, 2026 06:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces outline caching for AI Content Planner suggestions (so revisiting the same suggestion can restore the previously fetched/edited outline without re-requesting it) and refactors the Content Outline modal UI into smaller components.

Changes:

  • Add index to content suggestions and use it as the cache key for storing/restoring outlines.
  • Add outline cache state + actions to the content-outline store and wire cache restore into useFetchContentOutline.
  • Refactor the Content Outline modal by extracting ContentOutlineModalContent, StructureRow, and CategorySection into separate components and updating tests accordingly.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/js/src/ai-content-planner/store/content-suggestions.js Adds index to each suggestion during response validation for stable cache addressing.
packages/js/src/ai-content-planner/store/content-outline.js Adds outline cache state/actions and normalizes outline sections into { heading, id, contentNotes }.
packages/js/src/ai-content-planner/hooks/use-fetch-content-outline.js Restores outline from cache when available, otherwise fetches from API.
packages/js/src/ai-content-planner/hooks/use-draggable-structure.js Updates structure handling to use the normalized outline items directly.
packages/js/src/ai-content-planner/hooks/use-apply-outline.js Switches to applying blocks from the edited structure directly.
packages/js/src/ai-content-planner/helpers/build-blocks-from-outline.js Updates outline-to-blocks builder to use normalized section fields.
packages/js/src/ai-content-planner/containers/content-outline-modal.js Persists outline edits into cache when navigating back to suggestions.
packages/js/src/ai-content-planner/components/content-outline-modal.js Refactors modal to delegate body/footer to ContentOutlineModalContent.
packages/js/src/ai-content-planner/components/content-outline-modal-content.js New component containing the editable form + draggable structure + retry behavior.
packages/js/src/ai-content-planner/components/structure-row.js New extracted draggable structure row component.
packages/js/src/ai-content-planner/components/category-section.js New extracted category toggle/summary component.
packages/js/src/ai-content-planner/constants.js Documents the new Suggestion.index field used for caching.
packages/js/tests/ai-content-planner/store/suggestions.test.js Updates fixtures to include index.
packages/js/tests/ai-content-planner/hooks/use-draggable-structure.test.js Updates tests for normalized outline shape (heading instead of title/subheading_text).
packages/js/tests/ai-content-planner/components/feature-modal.test.js Updates suggestion fixture to include index.
packages/js/tests/ai-content-planner/components/content-outline-modal.test.js Updates mocks/expectations around retry (now calls fetchContentOutline).

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

Comment thread packages/js/src/ai-content-planner/hooks/use-apply-outline.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js Outdated
Comment thread packages/js/src/ai-content-planner/hooks/use-fetch-content-outline.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js Outdated
Comment thread packages/js/tests/ai-content-planner/hooks/use-draggable-structure.test.js Outdated
Comment thread packages/js/src/ai-content-planner/components/content-outline-modal-content.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js
Comment thread packages/js/src/ai-content-planner/components/content-outline-modal-content.js Outdated
Comment thread packages/js/src/ai-content-planner/components/content-outline-modal-content.js Outdated
Comment thread packages/js/src/ai-content-planner/components/content-outline-modal.js Outdated
Comment thread packages/js/src/ai-content-planner/constants.js Outdated
Comment thread packages/js/src/ai-content-planner/store/content-outline.js
@FAMarfuaty
Copy link
Copy Markdown
Contributor

Also, I followed these testing steps:

  • Select one of the suggestions and check you see a request for get_outline.
  • Check you see the outline modal and edit the title and meta description
  • Click on the "back to suggestions" button

On the "Content suggestions" overview, the title and the meta description don't reflect my changes from the previous step. Is this expected?

Screen.Recording.2026-04-22.at.10.36.34.mov

@vraja-pro vraja-pro changed the base branch from feature/content-planner to 1106-content-planner-create-spark-limit-modal-notification April 23, 2026 12:39
@github-actions
Copy link
Copy Markdown

A merge conflict has been detected for the proposed code changes in this PR. Please resolve the conflict by either rebasing the PR or merging in changes from the base branch.

…ion' into 1162-add-caching-for-the-outline-and-structure-for-the-suggestions
@github-actions
Copy link
Copy Markdown

A merge conflict has been detected for the proposed code changes in this PR. Please resolve the conflict by either rebasing the PR or merging in changes from the base branch.

…ion' into 1162-add-caching-for-the-outline-and-structure-for-the-suggestions
Base automatically changed from 1106-content-planner-create-spark-limit-modal-notification to feature/content-planner April 27, 2026 07:58
Copy link
Copy Markdown
Member

@pls78 pls78 left a comment

Choose a reason for hiding this comment

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

I'll leave two Claude's findings that I think are worth looking into:

  1. id: suggestion-${index} is positional → fragile cache key (store/content-suggestions.js:74)

The suggestion ID is just its position in the response. If the user re-fetches suggestions and the new list is shorter, longer, or reordered, "suggestion-0" maps to a different suggestion than what's in the cache. The reset on FETCH_CONTENT_SUGGESTIONS/request mitigates it (cache is wiped on re-fetch), but the safety relies entirely on that one reducer line. A content-derived id (e.g. hash of title + keyphrase) would be far more robust and would also let the cache survive reloads if you ever want that. At minimum, add a comment on the id field stating the invariant.

  1. Focus management regression in OutlineModalContent (components/outline-modal-content.js:127-131)
useEffect( () => {                                                                                                                                                                                                                                                                                
    if ( isLoading && closeButtonRef?.current ) {                     
        closeButtonRef.current.focus();
    }                                                                                                                                                                                                                                                                                             
}, [ isLoading, closeButtonRef ] );

Previously, focus was set to the close button when the panel became active, ensuring SR users got the dialog context. Now focus is set only while loading, meaning when loading transitions to success, focus is not moved to the loaded content — and if a user tabs away during the loading state, focus may not be re-asserted. Was this intentional? If the goal was "focus when the modal first opens," tying it to isLoading only kind-of accomplishes that and breaks the success-direct path (cache hit → never loading → never focused).

I'd give prio to the second one, honestly.

Moreover, there's a mixed used of handleEvent and onEvent for callback names: I'd stick with HandleEvent.

@github-actions
Copy link
Copy Markdown

A merge conflict has been detected for the proposed code changes in this PR. Please resolve the conflict by either rebasing the PR or merging in changes from the base branch.

…62-add-caching-for-the-outline-and-structure-for-the-suggestions
@pls78
Copy link
Copy Markdown
Member

pls78 commented Apr 27, 2026

Acc: ✅
Everything works as expected, but I have a question: are we sure we don't want to show the changes that users make to the outline when they go back to the content suggestions? 🤔

Focus the close button on mount and again when loading completes, so SR users get dialog context immediately after clicking a suggestion and again once the skeleton resolves to real content.
@pls78 pls78 added this to the feature/content-planner milestone Apr 27, 2026
@pls78 pls78 merged commit 520c250 into feature/content-planner Apr 27, 2026
20 checks passed
@pls78 pls78 deleted the 1162-add-caching-for-the-outline-and-structure-for-the-suggestions branch April 27, 2026 10:39
@vraja-pro vraja-pro mentioned this pull request Apr 28, 2026
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog: non-user-facing Needs to be included in the 'Non-userfacing' category in the changelog innovation Innovative issue. Relating to performance, memory or data-flow.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants