Skip to content

Added infinite scroll support to filter options#27606

Open
kevinansfield wants to merge 1 commit intomainfrom
codex/filter-options-infinite-scroll
Open

Added infinite scroll support to filter options#27606
kevinansfield wants to merge 1 commit intomainfrom
codex/filter-options-infinite-scroll

Conversation

@kevinansfield
Copy link
Copy Markdown
Member

@kevinansfield kevinansfield commented Apr 28, 2026

ref https://linear.app/ghost/issue/BER-3334/

Summary

  • Added a shared useFilterOptionsInfiniteScroll hook for filter option lists.
  • Wired the filter dropdowns, combobox, and label picker fallback load-more rows into the shared hook.
  • Added focused Shade tests and members E2E coverage for loading additional remote label pages.

Stack

Stacked on #27605.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

Walkthrough

The pull request implements infinite scrolling for filter options across multiple components. A new useFilterOptionsInfiniteScroll hook is introduced to manage pagination through an IntersectionObserver that triggers loadMore() when a sentinel element becomes visible. The LabelPicker component gains an optionSource prop to support incremental loading. The createCombinedValueSource hook is modified to change load-more control flow so only one source loads per invocation. UI components in the filter and multi-select-combobox are refactored to use the new pagination mechanism. Tests validate the combined value source behavior and infinite scroll triggering. E2E test helpers are introduced to support pagination testing.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding infinite scroll support to filter options, which is the primary focus across all modified files.
Description check ✅ Passed The description is directly related to the changeset, outlining the shared hook implementation, integration points, and test coverage added by the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/filter-options-infinite-scroll

Review rate limit: 3/5 reviews remaining, refill in 23 minutes and 7 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@kevinansfield kevinansfield force-pushed the codex/filter-option-source-refactor branch from 0db9884 to 0435e09 Compare April 28, 2026 18:11
@kevinansfield kevinansfield force-pushed the codex/filter-options-infinite-scroll branch 5 times, most recently from fb602c2 to 64e034f Compare April 29, 2026 10:08
@kevinansfield kevinansfield force-pushed the codex/filter-option-source-refactor branch from 0435e09 to 9c5cec2 Compare April 29, 2026 10:17
@kevinansfield kevinansfield force-pushed the codex/filter-options-infinite-scroll branch from 64e034f to 09fef38 Compare April 29, 2026 10:18
Base automatically changed from codex/filter-option-source-refactor to main April 30, 2026 16:46
no issue

Large filter option lists now share one guarded load-more trigger, while retaining the manual fallback button and covering remote label pagination in E2E.
@kevinansfield kevinansfield force-pushed the codex/filter-options-infinite-scroll branch from 09fef38 to 3b807c1 Compare April 30, 2026 16:51
@kevinansfield kevinansfield marked this pull request as ready for review April 30, 2026 16:56
@kevinansfield kevinansfield requested a review from 9larsons as a code owner April 30, 2026 16:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/shade/src/components/ui/filters.tsx (1)

1123-1130: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Hide the empty state while more pages are available.

CommandEmpty still renders whenever the current page is empty, even if optionSource.hasMore is true. That produces a misleading “No results found” message alongside a Load more control on paginated sources. Please gate this on !optionSource.hasMore here, and mirror the same check in the combobox renderer.

♻️ Proposed fix
-            ) : (
-                <CommandEmpty>{context.i18n.noResultsFound}</CommandEmpty>
-            )}
+            ) : !optionSource.hasMore ? (
+                <CommandEmpty>{context.i18n.noResultsFound}</CommandEmpty>
+            ) : null}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/shade/src/components/ui/filters.tsx` around lines 1123 - 1130, The
empty-state rendering shows CommandEmpty even when more pages exist; update the
conditional in the list renderer (the block using optionSource.isInitialLoad and
CommandEmpty) to only render <CommandEmpty> when optionSource.hasMore is false
(i.e., !optionSource.hasMore), and apply the same gating in the combobox
renderer where the empty message is displayed so that the “No results found”
message is suppressed while optionSource.hasMore is true; locate references to
optionSource.isInitialLoad, optionSource.hasMore, CommandEmpty and the combobox
renderer to apply the change.
apps/shade/src/components/ui/multi-select-combobox.tsx (1)

327-335: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Hide the empty state while more pages remain.

This path still renders CommandEmpty whenever the current page is empty, even if source.hasMore is true. That shows “No results found” alongside a Load more control for paginated sources. Please gate this on !source.hasMore here too.

♻️ Proposed fix
-                    ) : (
-                        <CommandEmpty>{i18n.noResultsFound}</CommandEmpty>
-                    )}
+                    ) : !source.hasMore ? (
+                        <CommandEmpty>{i18n.noResultsFound}</CommandEmpty>
+                    ) : null}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/shade/src/components/ui/multi-select-combobox.tsx` around lines 327 -
335, The empty-state rendering shows CommandEmpty even when pagination indicates
more pages; update the conditional inside CommandList to render CommandEmpty
only when not initial load AND source.hasMore is false: check
source.isInitialLoad first, otherwise render CommandEmpty only if
!source.hasMore, using the existing symbols (source.isInitialLoad,
source.hasMore, CommandEmpty, i18n.noResultsFound, CommandList) so the "No
results found" message is suppressed while more pages remain.
🧹 Nitpick comments (1)
e2e/tests/admin/members/multiselect-filters.test.ts (1)

170-184: ⚡ Quick win

Exercise the sentinel path here, not only the fallback button.

This test still clicks the “Load more” button, so it would pass even if the new infinite-scroll wiring stopped requesting page 2 automatically. Please trigger the scroll sentinel and assert the next page arrives without a manual click.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/admin/members/multiselect-filters.test.ts` around lines 170 - 184,
Replace the manual "Load more" click with exercising the infinite-scroll
sentinel: after membersPage.openFilterValue('Label'), locate the scroll sentinel
element (e.g. the element used for infinite scroll — look for a test id or class
used by the filter value list, such as a data-testid like
"infinite-scroll-sentinel" or the last list item) and call
scrollIntoView/scrollIntoViewIfNeeded on it so the next page is requested
automatically; then assert requestedLabelPages.has(2) and that
membersPage.getFilterOption(/Infinite Label 105/) is visible. Keep references to
mockPaginatedLabels, seedMembersAndNavigate, membersPage.openFilterField,
membersPage.getFilterOption and membersPage.openFilterValue to find the right
place to modify the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/shade/src/components/ui/filters.tsx`:
- Around line 1123-1130: The empty-state rendering shows CommandEmpty even when
more pages exist; update the conditional in the list renderer (the block using
optionSource.isInitialLoad and CommandEmpty) to only render <CommandEmpty> when
optionSource.hasMore is false (i.e., !optionSource.hasMore), and apply the same
gating in the combobox renderer where the empty message is displayed so that the
“No results found” message is suppressed while optionSource.hasMore is true;
locate references to optionSource.isInitialLoad, optionSource.hasMore,
CommandEmpty and the combobox renderer to apply the change.

In `@apps/shade/src/components/ui/multi-select-combobox.tsx`:
- Around line 327-335: The empty-state rendering shows CommandEmpty even when
pagination indicates more pages; update the conditional inside CommandList to
render CommandEmpty only when not initial load AND source.hasMore is false:
check source.isInitialLoad first, otherwise render CommandEmpty only if
!source.hasMore, using the existing symbols (source.isInitialLoad,
source.hasMore, CommandEmpty, i18n.noResultsFound, CommandList) so the "No
results found" message is suppressed while more pages remain.

---

Nitpick comments:
In `@e2e/tests/admin/members/multiselect-filters.test.ts`:
- Around line 170-184: Replace the manual "Load more" click with exercising the
infinite-scroll sentinel: after membersPage.openFilterValue('Label'), locate the
scroll sentinel element (e.g. the element used for infinite scroll — look for a
test id or class used by the filter value list, such as a data-testid like
"infinite-scroll-sentinel" or the last list item) and call
scrollIntoView/scrollIntoViewIfNeeded on it so the next page is requested
automatically; then assert requestedLabelPages.has(2) and that
membersPage.getFilterOption(/Infinite Label 105/) is visible. Keep references to
mockPaginatedLabels, seedMembersAndNavigate, membersPage.openFilterField,
membersPage.getFilterOption and membersPage.openFilterValue to find the right
place to modify the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 02ef933e-1b81-4edc-be7b-d4265e8cee83

📥 Commits

Reviewing files that changed from the base of the PR and between 97b0454 and 3b807c1.

📒 Files selected for processing (10)
  • apps/posts/src/components/label-picker/label-picker.tsx
  • apps/posts/src/hooks/filter-sources/create-combined-value-source.ts
  • apps/posts/test/unit/hooks/create-combined-value-source.test.tsx
  • apps/shade/src/components.ts
  • apps/shade/src/components/ui/filters.tsx
  • apps/shade/src/components/ui/multi-select-combobox.tsx
  • apps/shade/src/components/ui/use-filter-options-infinite-scroll.ts
  • apps/shade/test/unit/components/ui/filters.test.tsx
  • e2e/helpers/pages/admin/members/members-list-page.ts
  • e2e/tests/admin/members/multiselect-filters.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant