Skip to content

Commit 129e850

Browse files
committed
feat(FR-1859): Create a BAIUserSelect (#5055)
resolves #4931 (FR-1859) This PR improves the Relay Infinite Scroll Select skill documentation and implementation by: 1. Introducing a clearer decision tree based on dynamic query parameter needs 2. Adding a new Pattern B (Dynamic) that provides full control over query parameters 3. Implementing `BAIUserSelect` component as a reference for email-based selection with dynamic parameters 4. Adding dynamic `first` parameter to ensure all selected values are fetched efficiently 5. Updating `BAIVFolderSelect` to use dynamic `first` parameter for better data completeness 6. Providing detailed comparison between Simple and Dynamic patterns 7. Adding comprehensive examples and best practices for both patterns The key innovation is the dynamic `first` parameter that calculates exactly how many items to fetch based on selection count, preventing both over-fetching and under-fetching of data. **Checklist:** - [x] Documentation - [ ] Minium required manager version - [ ] Specific setting for review (eg., KB link, endpoint or how to setup) - [ ] Minimum requirements to check during review - [ ] Test case(s) to demonstrate the difference of before/after
1 parent f6ce207 commit 129e850

30 files changed

Lines changed: 1824 additions & 58 deletions

.claude/skills/relay-infinite-scroll-select/SKILL.md

Lines changed: 471 additions & 41 deletions
Large diffs are not rendered by default.

.claude/skills/relay-infinite-scroll-select/references/patterns/BAIAdminResourceGroupSelect.md

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import TotalFooter from '../TotalFooter';
3131
import { Skeleton } from 'antd';
3232
import { GetRef } from 'antd/lib';
3333
import _ from 'lodash';
34-
import { useRef } from 'react';
34+
import { useOptimistic, useRef, useState } from 'react';
3535
import { useTranslation } from 'react-i18next';
3636
import { usePaginationFragment } from 'react-relay';
3737
import { graphql } from 'relay-runtime';
@@ -48,6 +48,9 @@ const BAIAdminResourceGroupSelect = ({
4848
}: BAIAdminResourceGroupSelectProps) => {
4949
const { t } = useTranslation();
5050
const selectRef = useRef<GetRef<typeof BAISelect>>(null);
51+
const [searchStr, setSearchStr] = useState<string>();
52+
const [optimisticSearchStr, setOptimisticSearchStr] =
53+
useOptimistic(searchStr);
5154

5255
const { data, loadNext, isLoadingNext, refetch, hasNext } =
5356
usePaginationFragment<
@@ -86,14 +89,24 @@ const BAIAdminResourceGroupSelect = ({
8689
<BAISelect
8790
ref={selectRef}
8891
placeholder={t('comp:BAIAdminResourceGroupSelect.PlaceHolder')}
89-
showSearch={{
90-
autoClearSearchValue: true,
91-
filterOption: false,
92-
}}
92+
showSearch={
93+
selectPropsWithoutLoading.showSearch === false
94+
? false
95+
: {
96+
searchValue: optimisticSearchStr,
97+
autoClearSearchValue: true,
98+
filterOption: false,
99+
...(_.isObject(selectPropsWithoutLoading.showSearch)
100+
? _.omit(selectPropsWithoutLoading.showSearch, ['searchValue'])
101+
: {}),
102+
}
103+
}
93104
loading={loading}
94105
options={selectOptions}
95106
{...selectPropsWithoutLoading}
96107
searchAction={async (value) => {
108+
setOptimisticSearchStr(value);
109+
setSearchStr(value);
97110
selectRef.current?.scrollTo(0);
98111
refetch({
99112
filter: value
@@ -171,8 +184,35 @@ const selectOptions = _.map(data.allScalingGroupsV2.edges, (item) => ({
171184
Simple mapping from edges to options. **No labelInValue needed** since value and label are the same.
172185

173186
### 3. Search Implementation
187+
188+
**State Management:**
189+
```typescript
190+
const [searchStr, setSearchStr] = useState<string>();
191+
const [optimisticSearchStr, setOptimisticSearchStr] =
192+
useOptimistic(searchStr);
193+
```
194+
195+
**showSearch Configuration:**
196+
```typescript
197+
showSearch={
198+
selectPropsWithoutLoading.showSearch === false
199+
? false
200+
: {
201+
searchValue: optimisticSearchStr,
202+
autoClearSearchValue: true,
203+
filterOption: false,
204+
...(_.isObject(selectPropsWithoutLoading.showSearch)
205+
? _.omit(selectPropsWithoutLoading.showSearch, ['searchValue'])
206+
: {}),
207+
}
208+
}
209+
```
210+
211+
**searchAction:**
174212
```typescript
175213
searchAction={async (value) => {
214+
setOptimisticSearchStr(value); // Immediate UI feedback
215+
setSearchStr(value); // Actual state for query
176216
selectRef.current?.scrollTo(0); // Reset scroll position
177217
refetch({
178218
filter: value
@@ -187,9 +227,13 @@ searchAction={async (value) => {
187227
}}
188228
```
189229

190-
- Scroll to top on search for better UX
191-
- Use `refetch` to reload data with new filter
192-
- Filter structure matches GraphQL schema
230+
**Key Points:**
231+
-`useOptimistic` for immediate search input feedback
232+
-Conditional `showSearch` allows disabling with `showSearch={false}`
233+
-Merge user-provided `showSearch` config (except `searchValue`)
234+
-Scroll to top on search for better UX
235+
-Use `refetch` to reload data with new filter
236+
-Filter structure matches GraphQL schema
193237

194238
### 4. Infinite Scroll
195239
```typescript

0 commit comments

Comments
 (0)