chore(ui): migrate data asset select list to untitled #27697
chore(ui): migrate data asset select list to untitled #27697chirag-madlani wants to merge 36 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates parts of the OpenMetadata UI to align with newer component patterns (notably the UI-core Autocomplete) and adjusts scheduler cron handling to use null rather than undefined in some flows.
Changes:
- Refactors
DataAssetAsyncSelectListfrom Ant DesignSelectto@openmetadata/ui-core-componentsAutocomplete, adding load-more behavior and new prop types. - Updates scheduler interval state to use
cron: nullin some places, and replaces globalisNaNwithNumber.isNaNin scheduler utils. - Exports
GridItemfrom the UI core grid component.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| openmetadata-ui/src/main/resources/ui/src/utils/SchedularUtils.tsx | Uses Number.isNaN for safer NaN checks when normalizing cron fields. |
| openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/Steps/ScheduleIntervalV1.tsx | Switches “On Demand” cron handling to null and adjusts prop typing for onChange. |
| openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/Steps/ScheduleInterval.interface.ts | Changes cron typing to string | null (non-optional) for workflow config and state. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.tsx | Migrates async select UI to core Autocomplete, reworks selection state and load-more mechanics. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.test.tsx | Updates tests for new props (multiple) but still assumes old AntD DOM behaviors. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.interface.ts | Updates option/props types to use UI-core SelectItemType and replaces mode with multiple. |
| openmetadata-ui-core-components/src/main/resources/ui/src/components/base/grid/grid.tsx | Exports GridItem and tweaks start/span clamping logic formatting. |
| const renderCustomTag = useCallback( | ||
| (item: SelectItemType, onRemove: () => void) => { | ||
| const dataAssetItem = item as DataAssetOption; | ||
| if (renderTag) { | ||
| return renderTag(item, onRemove); | ||
| } | ||
|
|
||
| if ( | ||
| searchIndex === SearchIndex.USER || | ||
| searchIndex === SearchIndex.TEAM || | ||
| dataAssetItem.reference?.type === EntityType.USER || | ||
| dataAssetItem.reference?.type === EntityType.TEAM | ||
| ) { | ||
| return null; | ||
| } | ||
|
|
||
| const dropdownRender = (menu: React.ReactElement) => ( | ||
| <> | ||
| {menu} | ||
| {hasContentLoading ? <Loader size="small" /> : null} | ||
| </> | ||
| return null; | ||
| }, | ||
| [renderTag, searchIndex] |
| useEffect(() => { | ||
| const popoverElement = popoverRef.current; | ||
| if (popoverElement) { | ||
| popoverElement.addEventListener( | ||
| 'scroll', | ||
| handlePopoverScroll as unknown as EventListener | ||
| ); | ||
|
|
||
| selectedDataAssetsRef.current = selectedOptions; | ||
| onChange?.(selectedOptions); | ||
| } else { | ||
| onChange?.(options as DataAssetOption); | ||
| return () => { | ||
| popoverElement.removeEventListener( | ||
| 'scroll', | ||
| handlePopoverScroll as unknown as EventListener | ||
| ); | ||
| }; | ||
| } | ||
| }; | ||
|
|
||
| const handleBlur = useCallback(() => { | ||
| setCurrentPage(1); | ||
| setSearchValue(''); | ||
| setOptions([]); | ||
| }, []); | ||
| return () => {}; | ||
| }, [handlePopoverScroll]); |
| (key: string) => { | ||
| const updatedSelection = selectedItems.filter((item) => item.id !== key); | ||
| setSelectedItems(updatedSelection); | ||
| onChange?.(multiple ? updatedSelection : updatedSelection[0]); |
| export interface WorkflowExtraConfig { | ||
| cron?: string; | ||
| cron: string | null; | ||
| enableDebugLog?: boolean; | ||
| } | ||
|
|
||
| export interface IngestionExtraConfig { | ||
| retries?: number; | ||
| raiseOnError?: boolean; | ||
| } | ||
|
|
||
| export interface Combination { | ||
| hour: RegExp; | ||
| day: RegExp; | ||
| week: RegExp; | ||
| month: RegExp; | ||
| } | ||
| export interface StateValue { | ||
| selectedPeriod: string; | ||
| hour: string; | ||
| min: string; | ||
| dow: string; | ||
| dom: string; | ||
| cron?: string; | ||
| cron: string | null; | ||
| } |
| @@ -44,17 +53,19 @@ | |||
| value: selectedValue, | |||
| filterFqns = [], | |||
| queryFilter, | |||
| renderTag, | |||
| ...props | |||
| }) => { | |||
| const [selectedItems, setSelectedItems] = useState<DataAssetOption[]>( | ||
| initialOptions ?? [] | ||
| ); |
| @@ -39,4 +43,5 @@ export interface DataAssetAsyncSelectListProps { | |||
| onChange?: (option: DataAssetOption | DataAssetOption[]) => void; | |||
| filterFqns?: string[]; | |||
| queryFilter?: Record<string, unknown>; | |||
| renderTag?: (item: SelectItemType, onRemove: () => void) => ReactNode; | |||
| import { StateValue } from './ScheduleInterval.interface'; | ||
|
|
||
| export interface ScheduleIntervalV1Props { | ||
| value?: string; |
| if (isString(selectedValue) || isArray(selectedValue)) { | ||
| const values = isArray(selectedValue) ? selectedValue : [selectedValue]; | ||
| const items = values | ||
| .map((val) => filteredOptions.find((opt) => opt.value === val)) | ||
| .filter(Boolean) as DataAssetOption[]; | ||
| setSelectedItems(items); | ||
| } else if (selectedValue) { | ||
| setSelectedItems( | ||
| isArray(selectedValue) ? selectedValue : [selectedValue] | ||
| ); |
| const { container } = render( | ||
| <DataAssetAsyncSelectList | ||
| mode="multiple" | ||
| searchIndex={SearchIndex.USER} | ||
| /> | ||
| <DataAssetAsyncSelectList multiple searchIndex={SearchIndex.USER} /> | ||
| ); |
🔴 Playwright Results — 1 failure(s), 8 flaky✅ 3453 passed · ❌ 1 failed · 🟡 8 flaky · ⏭️ 76 skipped
Genuine Failures (failed on all attempts)❌
|
There was a problem hiding this comment.
Pull request overview
This PR updates parts of the OpenMetadata UI to align selection controls with @openmetadata/ui-core-components and refines scheduler/grid behaviors.
Changes:
- Migrates
DataAssetAsyncSelectListfrom AntDSelectto UI coreAutocomplete, updating call sites to use amultipleboolean. - Updates scheduler interval types/behavior to represent “on demand” schedules via
nullrather thanundefined. - Adjusts core grid item start/span clamping logic and exports
GridItem.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| openmetadata-ui/src/main/resources/ui/src/utils/SchedularUtils.tsx | Uses Number.isNaN for cron parsing guards. |
| openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx | Updates DataAssetAsyncSelectList usage from mode to multiple. |
| openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/Steps/ScheduleIntervalV1.tsx | Switches “on demand” schedule handling to null and adjusts cron input binding. |
| openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/Steps/ScheduleInterval.interface.ts | Changes cron typing for scheduler state/extra config. |
| openmetadata-ui/src/main/resources/ui/src/components/Metric/RelatedMetrics/RelatedMetricsForm.tsx | Updates DataAssetAsyncSelectList usage to multiple. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.tsx | Replaces AntD Select implementation with UI core Autocomplete and adds async loading + custom item rendering. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.test.tsx | Updates tests to the new multiple prop and option shape. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.interface.ts | Updates option/props types to match UI core SelectItemType and replaces mode with multiple. |
| openmetadata-ui-core-components/src/main/resources/ui/src/components/base/grid/grid.tsx | Exports GridItem and refines start/span clamping logic. |
Comments suppressed due to low confidence (1)
openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.test.tsx:153
- This test still interacts with AntD
Selectinternals (e.g., querying.ant-select-selector), butDataAssetAsyncSelectListis now based on@openmetadata/ui-core-componentsAutocomplete, so these selectors/click paths won’t work and the test will fail. Update the test to open the Autocomplete popover via its actual input/button (query by role/testid) and select items using the renderedAutocomplete.Itemelements; also align mocked search hits with the new code path (it reads_source.typerather than_source.entityType).
const { container } = render(
<DataAssetAsyncSelectList multiple searchIndex={SearchIndex.USER} />
);
await act(async () => {
const inputBox = container.querySelector('.ant-select-selector');
inputBox && fireEvent.click(inputBox);
});
| import { StateValue } from './ScheduleInterval.interface'; | ||
|
|
||
| export interface ScheduleIntervalV1Props { | ||
| value?: string; |
|
|
||
| export interface WorkflowExtraConfig { | ||
| cron?: string; | ||
| cron: string | null; |
| const items = values | ||
| .map((val) => filteredOptions.find((opt) => opt.value === val)) | ||
| .filter(Boolean) as DataAssetOption[]; | ||
| setSelectedItems(items); | ||
| } else if (selectedValue) { | ||
| setSelectedItems( | ||
| isArray(selectedValue) ? selectedValue : [selectedValue] | ||
| ); |
| const [selectedItems, setSelectedItems] = useState<string[]>( | ||
| initialOptions?.map((options) => options.value) ?? [] |
| (key: Key) => { | ||
| const updatedSelection = selectedItems.filter((item) => item.id !== key); | ||
| setSelectedItems(updatedSelection); | ||
| onChange?.(multiple ? updatedSelection : updatedSelection[0]); |
| useEffect(() => { | ||
| const popoverElement = popoverRef.current; | ||
| if (popoverElement) { | ||
| popoverElement.addEventListener( | ||
| 'scroll', | ||
| handlePopoverScroll as unknown as EventListener | ||
| ); | ||
|
|
||
| return () => { | ||
| popoverElement.removeEventListener( | ||
| 'scroll', | ||
| handlePopoverScroll as unknown as EventListener | ||
| ); | ||
| }; | ||
| } | ||
| const selectedOption = selectedValue as DataAssetOption; | ||
|
|
||
| return selectedOption?.value as string; | ||
| }, [mode, selectedValue]); | ||
| return () => {}; | ||
| }, [handlePopoverScroll]); |
| <DataAssetAsyncSelectList | ||
| initialOptions={initialOptions} | ||
| mode={mode} | ||
| multiple={multiple} | ||
| placeholder={ |
| dow: string; | ||
| dom: string; | ||
| cron?: string; | ||
| cron: string | null; |
| import { HintText } from '@/components/base/input/hint-text'; | ||
| import { Label } from '@/components/base/input/label'; | ||
| import { Popover } from '@/components/base/select/popover'; | ||
| import type { Popover, PopoverProps } from '@/components/base/select/popover'; |
| {hasContentLoading ? <Loader size="small" /> : null} | ||
| </> | ||
| if (multiple) { | ||
| setSelectedItems((prev) => { |
| const updatedSelection = selectedItems.filter((item) => item.id !== key); | ||
| setSelectedItems(updatedSelection); | ||
| if (multiple) { | ||
| onChange?.(updatedSelection); | ||
| } else { | ||
| onChange?.(updatedSelection[0] ?? null); | ||
| } | ||
| }, | ||
| [selectedItems, multiple, onChange] |
| if (isString(arr[0])) { | ||
| // Array of FQN strings | ||
| const items = (arr as string[]) | ||
| .map((val) => knownOptionsRef.current.get(val)) | ||
| .filter(Boolean) as DataAssetOption[]; | ||
| if (items.length > 0) { | ||
| setSelectedItems(items); |
| } else if (isString(selectedValue)) { | ||
| // Single FQN string | ||
| const item = knownOptionsRef.current.get(selectedValue); | ||
| if (item) { | ||
| setSelectedItems([item]); | ||
| } |
| return ( | ||
| <DataAssetAsyncSelectList | ||
| defaultValue={props?.value?.fullyQualifiedName ?? ''} | ||
| placeholder={schema.placeholder ?? ''} | ||
| searchIndex={schema?.autoCompleteType ?? SearchIndex.TABLE} | ||
| value={props?.value?.fullyQualifiedName ?? undefined} | ||
| onChange={handleChange} | ||
| /> |
| placeholder={props.placeholder} | ||
| popoverClassName={customPopoverClassName} | ||
| popoverProps={popoverProps} | ||
| selectedItems={selectedItems} | ||
| onItemCleared={handleItemCleared} | ||
| onItemInserted={handleItemInserted} | ||
| onSearchChange={handleSearchChange} | ||
| {...props}> |
|
@copilot apply changes based on the comments in this thread |
…tional updates, props spread, initialOptions, cron null handling Agent-Logs-Url: https://github.com/open-metadata/OpenMetadata/sessions/57a8b4a8-1bed-44ec-a404-7e71697033d9 Co-authored-by: chirag-madlani <12962843+chirag-madlani@users.noreply.github.com>
… fallback, compose scroll handlers Agent-Logs-Url: https://github.com/open-metadata/OpenMetadata/sessions/57a8b4a8-1bed-44ec-a404-7e71697033d9 Co-authored-by: chirag-madlani <12962843+chirag-madlani@users.noreply.github.com>
Applied all changes from the review thread in commits
|
1 similar comment
Applied all changes from the review thread in commits
|
Code Review
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Gitar
|
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
| multiple={multiple} | ||
| placeholder={props.placeholder} | ||
| popoverClassName={customPopoverClassName} |
There was a problem hiding this comment.
placeholder is already spread via {...props} on the line above, so this explicit prop is redundant. Both point to the same value, but it can mislead readers into thinking a different override is happening.
| multiple={multiple} | |
| placeholder={props.placeholder} | |
| popoverClassName={customPopoverClassName} | |
| multiple={multiple} | |
| popoverClassName={customPopoverClassName} |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| useEffect(() => { | ||
| if (!hasInitiallyLoaded.current) { | ||
| hasInitiallyLoaded.current = true; | ||
| loadOptions(''); | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
Missing
loadOptions in useEffect dependency array
loadOptions is captured from the outer scope but is not listed in the deps array []. ESLint's react-hooks/exhaustive-deps rule will flag this, and CI's lint-src check may fail. The hasInitiallyLoaded guard is intentional, but the correct pattern to satisfy the linter without changing behaviour is to list loadOptions in the deps — the ref guard still prevents re-execution.
- AsyncSelectWidget: use relative import for getEntityName instead of the unresolvable `src/utils/EntityNameUtils` absolute path (broke the Vite production build and the FormBuilder/ApplicationConfiguration Jest suites) - AsyncSelectWidget: pass resolvedFqn (id fallback) as value so a pre-populated entity without a fullyQualifiedName isn't cleared - DataAssetAsyncSelectList.interface: declare the popoverProps prop the component already consumes - RelatedDataAssetsForm: migrate the renamed mode="multiple" prop to multiple Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # openmetadata-ui-core-components/src/main/resources/ui/src/components/base/autocomplete/autocomplete.tsx # openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/Steps/ScheduleIntervalV1.tsx
Code Review
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Gitar
|



Describe your changes:
Fixes https://github.com/open-metadata/openmetadata-collate/issues/4464
I worked on ... because ...
Type of change:
Checklist:
Fixes <issue-number>: <short explanation>Summary by Gitar
antdSelect toAutocompletewith native scroll pagination and improved selection persistence viaknownOptionsRef.modeprop withmultipleboolean and updatedonChangeto support nullability.AsyncSelectWidgetandPropertyValueto handlenullvalues correctly and support properentityRefresolution.ScheduleIntervalV1to handle null/undefined cron values and fixedGridItemcolumn spanning calculation.app-worker.jscache filter logic and resolved CSS formatting issues inknowledge-center-layout.KnowledgeCenter,metric, andcustomPropertytests to accommodate theAutocompleteUI changes.DataAssetAsyncSelectListcomponent tests to mockAutocompleteinteractions.This will update automatically on new commits.
------|----------|
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.tsx | Core migration from antd Select to Autocomplete; introduces native scroll pagination, knownOptionsRef for selection persistence, and controlled selectedItems state — but has a redundant placeholder prop and a missing useEffect dependency that may fail CI lint. |
| openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/AsyncSelectWidget.tsx | Updated to handle null onChange and build initialOptions from entityRef, but the value prop doesn't fall back to entityRef.id when fullyQualifiedName is absent, causing pre-populated values to be cleared by the selectedValue effect. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.interface.ts | Replaces antd DefaultOptionType with SelectItemType, adds required id/value fields, renames mode to multiple, and exposes popoverClassName — clean interface update. |
| openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx | Switches from mode to multiple prop, adds id to initialOptions, and handles null entityReference in onFinish — straightforward and correct. |
| openmetadata-ui-core-components/src/main/resources/ui/src/components/base/autocomplete/autocomplete.tsx | Adds optional popoverProps to AutocompleteProps and spreads it before fixed className/size overrides — safe, well-typed addition using Omit to prevent size/className conflicts. |
| openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.test.tsx | Comprehensive test rewrite replacing antd selector queries with Autocomplete mock interactions; covers selection, multi-select, placeholder, and pagination scenarios. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant U as User participant DA as DataAssetAsyncSelectList participant AC as Autocomplete participant API as Search API U->>AC: Type in search input AC->>DA: onSearchChange(value) DA->>DA: debouncedSearch(value) DA->>API: "fetchOptions(value, page=1)" API-->>DA: "{ data, paging }" DA->>DA: setOptions / knownOptionsRef.set() DA->>AC: "items={filteredOptions}" U->>AC: Scroll near bottom of dropdown AC->>DA: popoverProps.onScroll(event) DA->>DA: handleNativeScroll → loadMoreOptions() DA->>API: fetchOptions(searchValue, currentPage+1) API-->>DA: "{ data, paging }" DA->>DA: setOptions(prev + data) U->>AC: Click item AC->>DA: onItemInserted(key) DA->>DA: handleItemInserted → setSelectedItems DA->>DA: onChange(selected) U->>AC: Remove chip AC->>DA: onItemCleared(key) DA->>DA: handleItemCleared → setSelectedItems DA->>DA: "onChange(updated | null)"%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant U as User participant DA as DataAssetAsyncSelectList participant AC as Autocomplete participant API as Search API U->>AC: Type in search input AC->>DA: onSearchChange(value) DA->>DA: debouncedSearch(value) DA->>API: "fetchOptions(value, page=1)" API-->>DA: "{ data, paging }" DA->>DA: setOptions / knownOptionsRef.set() DA->>AC: "items={filteredOptions}" U->>AC: Scroll near bottom of dropdown AC->>DA: popoverProps.onScroll(event) DA->>DA: handleNativeScroll → loadMoreOptions() DA->>API: fetchOptions(searchValue, currentPage+1) API-->>DA: "{ data, paging }" DA->>DA: setOptions(prev + data) U->>AC: Click item AC->>DA: onItemInserted(key) DA->>DA: handleItemInserted → setSelectedItems DA->>DA: onChange(selected) U->>AC: Remove chip AC->>DA: onItemCleared(key) DA->>DA: handleItemCleared → setSelectedItems DA->>DA: "onChange(updated | null)"Reviews (1): Last reviewed commit: "Merge branch 'main' into feat-ai-automat..." | Re-trigger Greptile
Greptile Summary
This PR migrates
DataAssetAsyncSelectListfrom Ant Design'sSelectto the internalAutocompletecomponent, replacing themodeprop withmultiple, implementing native scroll-based pagination viaknownOptionsRef, and updating all call sites and tests accordingly.DataAssetAsyncSelectList.tsx): Replaces antd Select withAutocomplete; introducesknownOptionsReffor selection persistence across option-list refreshes andhandleNativeScroll/loadMoreOptionsfor scroll-triggered pagination. TheloadMoreOptionsguard does not handle the uninitializedpaging.totalcase (see inline comment).PropertyValue.tsx,RelatedDataAssetsForm.tsx,RelatedMetricsForm.tsx, andAsyncSelectWidget.tsxare all updated to the newmultipleboolean prop and null-awareonChangesignature; placeholder labels inPropertyValue.tsxare also corrected (previously inverted singular/plural).DataAssetAsyncSelectList.test.tsxis rewritten to mockAutocompleteinteractions; Playwright locators inKnowledgeCenter.ts,metric.ts, andcustomProperty.tsare updated to remove Ant Design class-based selectors.Confidence Score: 4/5
Safe to merge once the loadMoreOptions pagination guard is fixed; all other changes are correct and well-tested.
The scroll-triggered loadMoreOptions can race against the initial loadOptions fetch because the paging.total === undefined case is not guarded. A user who opens the dropdown and scrolls before the first page arrives will trigger a page-2 fetch whose results are then silently discarded when page 1 completes. All other changes — the Autocomplete migration, prop renaming, null-handling, and test rewrites — look correct.
openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.tsx — specifically the loadMoreOptions guard.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant U as User participant DA as DataAssetAsyncSelectList participant AC as Autocomplete participant API as Search API Note over DA: Mount: selectedItems = initialOptions DA->>API: loadOptions('') [page 1] Note over DA: selectedValue effect runs U->>AC: Open dropdown / scroll near bottom AC->>DA: handleNativeScroll Note over DA: ⚠ paging.total=undefined, guard fails DA->>API: loadMoreOptions() [page 2] race API-->>DA: page 2 data appended to options API-->>DA: page 1 completes → setOptions(page1) overwrites! U->>AC: Type in search input AC->>DA: onSearchChange(value) DA->>DA: debouncedSearch(value) DA->>API: "loadOptions(value, page=1)" API-->>DA: data + paging DA->>DA: setOptions / knownOptionsRef.set() U->>AC: Click item AC->>DA: onItemInserted(key) DA->>DA: find in filteredOptions → setSelectedItems DA->>DA: onChange(selected) U->>AC: Remove chip AC->>DA: onItemCleared(key) DA->>DA: "filter selectedItems → onChange(updated | null)"%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant U as User participant DA as DataAssetAsyncSelectList participant AC as Autocomplete participant API as Search API Note over DA: Mount: selectedItems = initialOptions DA->>API: loadOptions('') [page 1] Note over DA: selectedValue effect runs U->>AC: Open dropdown / scroll near bottom AC->>DA: handleNativeScroll Note over DA: ⚠ paging.total=undefined, guard fails DA->>API: loadMoreOptions() [page 2] race API-->>DA: page 2 data appended to options API-->>DA: page 1 completes → setOptions(page1) overwrites! U->>AC: Type in search input AC->>DA: onSearchChange(value) DA->>DA: debouncedSearch(value) DA->>API: "loadOptions(value, page=1)" API-->>DA: data + paging DA->>DA: setOptions / knownOptionsRef.set() U->>AC: Click item AC->>DA: onItemInserted(key) DA->>DA: find in filteredOptions → setSelectedItems DA->>DA: onChange(selected) U->>AC: Remove chip AC->>DA: onItemCleared(key) DA->>DA: "filter selectedItems → onChange(updated | null)"Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile