Skip to content

Autocomplete virtualization feature venky april 07 - #656

Merged
agorthi-akamai merged 5 commits into
ACLPManager:aclp_developfrom
agorthi-akamai:autocomplete-virtualization-feature_venky_April_07
Apr 9, 2026
Merged

agorthi-akamai merged 5 commits into
ACLPManager:aclp_developfrom
agorthi-akamai:autocomplete-virtualization-feature_venky_April_07

Conversation

@agorthi-akamai

Copy link
Copy Markdown

Description 📝

Highlight the Pull Request's context and intentions.

Changes 🔄

List any change(s) relevant to the reviewer.

  • ...
  • ...

Scope 🚢

Upon production release, changes in this PR will be visible to:

  • All customers
  • Some customers (e.g. in Beta or Limited Availability)
  • No customers / Not applicable

Target release date 🗓️

Please specify a release date (and environment, if applicable) to guarantee timely review of this PR. If exact date is not known, please approximate and update it as needed.

Preview 📷

Include a screenshot <img src="" /> or video <video src="" /> of the change.

🔒 Use the Mask Sensitive Data setting for security.

💡 For changes requiring multiple steps to validate, prefer a video for clarity.

Before After
📷 📷

How to test 🧪

Prerequisites

(How to setup test environment)

  • ...
  • ...

Reproduction steps

(How to reproduce the issue, if applicable)

  • ...
  • ...

Verification steps

(How to verify changes)

  • ...
  • ...
Author Checklists

As an Author, to speed up the review process, I considered 🤔

Check all that apply

  •  Use React components instead of HTML Tags
  • Proper naming conventions like cameCase for variables & Function & snake_case for constants
  • Use appropriate types & avoid using "any"
  • No type casting & non-null assertions
  • Adding a changeset
  • Providing/Improving test coverage
  • Use sx props to pass styles instead of style prop
  • Add JSDoc comments for interface properties & functions
  • Use strict equality (===) instead of double equal (==)
  • Use of named arguments (interfaces) if function argument list exceeds size 2
  • Destructure the props
  • Keep component size small & move big computing functions to separate utility
  • 📱 Providing mobile support

  • I have read and considered all applicable items listed above.

As an Author, before moving this PR from Draft to Open, I confirmed ✅

  • All tests and CI checks are passing
  • TypeScript compilation succeeded without errors
  • Code passes all linting rules

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds virtualization and delayed-loading UX improvements in CloudPulse, plus updates to related test/fixture data.

Changes:

  • Introduces react-window-based virtualization for large Autocomplete option lists in CloudPulse resources selection.
  • Adds a reusable useDelayedLoadingIndicator hook and uses it to show a “taking longer than expected” message after 10s in CloudPulse loading states.
  • Updates CloudPulse Cypress tests and API response fixtures/validation lists.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new deps for virtualization (react-window, memoize-one) and typings.
packages/manager/package.json Adds react-window and @types/react-window.
packages/manager/src/features/CloudPulse/Utils/useDelayedLoadingIndicator.ts New hook to delay showing a secondary loading indicator.
packages/manager/src/features/CloudPulse/Utils/useDelayedLoadingIndicator.test.ts Unit tests for the delayed loading hook.
packages/manager/src/features/CloudPulse/shared/VirtualizedListBox.tsx New virtualized listbox component for large option sets.
packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx Wires virtualization + custom filtering into CloudPulse resources Autocomplete.
packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardFilterBuilder.tsx Uses delayed indicator to show a message after prolonged loading.
packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.tsx Uses delayed indicator to show a message after prolonged loading.
packages/manager/cypress/e2e/core/cloudpulse/contextual-view/alert-verification.spec.ts Updates expected save button/toast for unified alerts save flow.
packages/manager/cypress/e2e/core/cloudpulse/api/cloudpulse-api-response-validation.spec.ts Updates API validation coverage set (removes linode).
packages/manager/cypress/e2e/core/cloudpulse/api-response/netloadbalancer-dashboard-response.json Adds group_by to NLB dashboard fixture panels.
packages/manager/cypress/e2e/core/cloudpulse/alerts-listing-page.spec.ts Increases loading timeout for stability.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

Comment on lines +25 to +40
<FixedSizeList
className="virtualized-listbox"
height={calculatedHeight}
innerElementType="div"
itemCount={itemCount}
itemData={itemData}
itemSize={36}
outerElementType="ul"
style={{
margin: 0,
}}
width="100%"
>
{({ data, index, style }) => (
<div style={{ ...style, boxSizing: 'border-box' }}>{data[index]}</div>
)}

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

FixedSizeList is currently configured to render an invalid structure (outerElementType="ul" + innerElementType="div", and each row wraps the existing <li> option node inside an extra <div>). This breaks list semantics (UL should contain LI children) and can interfere with MUI Autocomplete option roles/styling. Consider rendering the inner element as ul and cloning each child to apply the style directly to the <li> (or otherwise ensure the UL’s direct children are LI elements).

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +22
export const VirtualizedListbox = React.memo(
(props: VirtualizedListboxProps) => {
const { children } = props;

const itemData = React.Children.toArray(children);
const itemCount = itemData.length;

const calculatedHeight = React.useMemo(
() => Math.min(160, itemCount * 36),
[itemCount]
);

if (itemCount === 0) {
return <ul>{children}</ul>;
}

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

This new virtualized listbox behavior isn’t covered by tests. Since this is a new rendering strategy that can affect option roles/keyboard navigation, please add at least a minimal unit test verifying that children are rendered and virtualization is applied (e.g., correct itemCount/height calculations and that list items remain selectable).

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +178
return React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLElement>
>((props, ref) => {
// Extract children and forward to VirtualizedListbox
const { children, ...otherProps } = props;
return (
<div ref={ref} {...otherProps}>
<VirtualizedListbox>{children}</VirtualizedListbox>
</div>

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

ListboxWrapper forwards the listbox props/ref to an extra wrapping <div>, but the actual scroll container rendered by react-window is inside VirtualizedListbox. This can break MUI Autocomplete behaviors that rely on the listbox ref (scroll-to-highlighted option, focus management) and can also cause className/ARIA props to be applied to the wrong element. Consider wiring the forwarded ref to the FixedSizeList scroll element (e.g., via outerRef) and ensuring the listbox props (role/id/className/onMouseDown/etc.) are applied to the element that acts as the listbox/scroll container.

Suggested change
return React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLElement>
>((props, ref) => {
// Extract children and forward to VirtualizedListbox
const { children, ...otherProps } = props;
return (
<div ref={ref} {...otherProps}>
<VirtualizedListbox>{children}</VirtualizedListbox>
</div>
const VirtualizedListboxComponent =
VirtualizedListbox as React.ComponentType<
React.HTMLAttributes<HTMLElement> & {
children?: React.ReactNode;
ref?: React.Ref<HTMLDivElement>;
}
>;
return React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLElement>
>((props, ref) => {
return (
<VirtualizedListboxComponent ref={ref} {...props}>
{props.children}
</VirtualizedListboxComponent>

Copilot uses AI. Check for mistakes.
Comment on lines +255 to +259
slotProps={{
listbox: {
component: ListboxWrapper,
},
}}

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

slotProps.listbox.component is always provided, even when ListboxWrapper is undefined (for lists <= 100). Passing component: undefined can override the default listbox component depending on how @linode/ui forwards props to MUI. Consider only setting slotProps when ListboxWrapper is defined (or omit the component key when not virtualizing).

Suggested change
slotProps={{
listbox: {
component: ListboxWrapper,
},
}}
slotProps={
ListboxWrapper
? {
listbox: {
component: ListboxWrapper,
},
}
: undefined
}

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +162
// Optimize filtering for large lists - only apply limit when user is actively searching
const filterOptions = React.useMemo(() => {
const baseFilterOptions = createFilterOptions<CloudPulseResources>({
stringify: (resource) => resource.label,
});

return (
options: CloudPulseResources[],
state: FilterOptionsState<CloudPulseResources>
) => {
// Only apply limit when there's search input to improve filtering performance
if (state.inputValue) {
const filtered = baseFilterOptions(options, state);
return filtered.slice(0, 1300);
}
// Show all options when no search text (virtualization handles performance)
return options;
};
}, []);

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

The newly added filtering/virtualization behavior isn’t covered by the existing CloudPulseResourcesSelect tests. Please add a test case that exercises the large-list path (e.g., >100 options) and asserts that virtualization is enabled and filtering still works (including the capped results when inputValue is non-empty).

Copilot uses AI. Check for mistakes.
Comment on lines +563 to +564
The loading time is over 10 seconds. Please wait while the
process completes.

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

The loading message reads awkwardly: “The loading time is over 10 seconds.” Consider updating the copy to something grammatically correct and more user-friendly (e.g., “This is taking longer than expected. Please wait…”), and avoid hard-coding the exact duration in the text unless it’s a product requirement.

Suggested change
The loading time is over 10 seconds. Please wait while the
process completes.
This is taking longer than expected. Please wait...

Copilot uses AI. Check for mistakes.
Comment on lines +423 to +424
The loading time is over 10 seconds. Please wait while the process
completes.

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

The loading message reads awkwardly: “The loading time is over 10 seconds.” Consider updating the copy to something grammatically correct and more user-friendly (e.g., “This is taking longer than expected. Please wait…”), and avoid hard-coding the exact duration in the text unless it’s a product requirement.

Suggested change
The loading time is over 10 seconds. Please wait while the process
completes.
This is taking longer than expected. Please wait...

Copilot uses AI. Check for mistakes.
ui.button.findByTitle('Save').should('be.visible').click();
ui.button.findByTitle('Confirm').should('be.visible').click();
ui.button.findByTitle('Save Alerts').should('be.visible').click();
// ui.button.findByTitle('Confirm').should('be.visible').click();

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

There’s a commented-out interaction left in the test (// ui.button.findByTitle('Confirm')...). Please remove this dead code or replace it with an assertion that matches the current flow; commented test steps tend to hide required behavior changes and make future failures harder to diagnose.

Suggested change
// ui.button.findByTitle('Confirm').should('be.visible').click();
ui.button.findByTitle('Confirm').should('not.exist');

Copilot uses AI. Check for mistakes.
@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

4 similar comments
@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

@agorthi-akamai
agorthi-akamai requested a review from Copilot April 9, 2026 04:58
@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

Copilot AI left a comment

Copy link
Copy Markdown

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 13 out of 14 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

Comment on lines 173 to 191
const services = [
{ type: 'dbaas', id: 1 },
{ type: 'nodebalancer', id: 3 },
{ type: 'firewall', id: 4 },
{ type: 'objectstorage', id: 6 },
{ type: 'linode', id: 2 },
{ type: 'netloadbalancer', id: 5 },
];

// -----------------------------
// Dashboards tests
// -----------------------------
context('Dashboards', () => {
[
'dbaas',
'firewall',
'nodebalancer',
'objectstorage',
'linode',
'netloadbalancer',
].forEach((type) => {

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This test drops linode from the list of service types validated for dashboards/metric-definitions, but the repo still has linode-dashboard-response.json and linode-metric-definition.json templates. Unless the Linode endpoints have been intentionally removed/deprecated, this reduces API validation coverage and leaves those templates unused. Consider re-adding linode here or explicitly skipping it with a documented reason (e.g., endpoint not available in certain envs).

Copilot uses AI. Check for mistakes.
@agorthi-akamai
agorthi-akamai requested a review from Copilot April 9, 2026 09:24
@agorthi-akamai

Copy link
Copy Markdown
Author

run_cypress

Copilot AI left a comment

Copy link
Copy Markdown

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 6 out of 6 changed files in this pull request and generated 4 comments.


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

@@ -218,13 +218,7 @@ describe('Alert Contextual view for linode', () => {
'not.exist'
);
// Navigation to Alerts beta

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This comment no longer matches the test behavior: the spec no longer performs a navigation/click into Alerts (Beta) here. Please update or remove the comment so it accurately reflects what the test is doing.

Suggested change
// Navigation to Alerts beta
// Wait for alert definitions to load

Copilot uses AI. Check for mistakes.
Comment on lines +274 to 275
// ui.button.findByTitle('Confirm').should('be.visible').click();

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

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

Please remove the commented-out "Confirm" click (or replace with an explicit conditional flow if a confirm dialog is expected). Leaving disabled interaction code in the test makes intent unclear and can hide real UI changes/flakiness.

Suggested change
// ui.button.findByTitle('Confirm').should('be.visible').click();
cy.get('body').then(($body) => {
const confirmButton = $body.find('[title="Confirm"]');
if (confirmButton.length > 0) {
ui.button.findByTitle('Confirm').should('be.visible').click();
}
});

Copilot uses AI. Check for mistakes.
Comment on lines 186 to 190
'dbaas',
'firewall',
'nodebalancer',
'objectstorage',
'linode',
'netloadbalancer',

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This API validation suite no longer tests the linode service type (dashboards + metric definitions), but the repository still contains linode-*-response.json fixtures. If the linode endpoints are still supported by the app, please keep this coverage by updating the fixtures/assertions; otherwise, remove the unused linode fixtures to avoid dead test data.

Copilot uses AI. Check for mistakes.
Comment on lines +138 to 142
mockGetUserPreferences({
isAclpMetricsBeta: true,
isAclpMetricsMode: true,
}).as('fetchPreferences');
mockCreateCloudPulseMetrics(serviceType, metricsAPIResponsePayload).as(

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

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

Setting isAclpMetricsMode: true makes the metrics preference toggle render with the label "Switch to legacy Metrics". Later in this spec there is a conditional click on that exact label, which will now likely execute and trigger an unmocked PUT to profile/preferences (and/or switch the page into legacy mode, breaking the CloudPulse assertions). Consider removing that click or adding mockUpdateUserPreferences() so no real network call occurs and the test stays in the intended mode.

Copilot uses AI. Check for mistakes.

@kmuddapo kmuddapo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

@agorthi-akamai
agorthi-akamai merged commit 59d030f into ACLPManager:aclp_develop Apr 9, 2026
37 checks passed
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.

4 participants