Skip to content

change: [UIE-8743] - Replaced Button component in DBAAS with Akamai CDS button Web Component #12148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
76e0e37
change: [UIE-8743] - Replaced Button component in DBAAS with CDS butt…
tvijay-akamai May 13, 2025
8c21f66
change: [UIE-8743] - upgrade web component library version which will…
tvijay-akamai May 14, 2025
61cad37
change: [UIE-8743] - resolved merge conflicts
tvijay-akamai May 16, 2025
1508b46
change: [UIE-8743] - added test id to some buttons as requested by qa
tvijay-akamai May 16, 2025
3afdade
change: [UIE-8743] - added test id to some buttons as requested by qa…
tvijay-akamai May 19, 2025
5f59acf
Merge branch 'develop' into feature/UIE-8743
tvijay-akamai May 21, 2025
9f32bb7
change: [UIE-8743] - upgraded cds version to fix the button size issue
tvijay-akamai May 21, 2025
5ff2e49
change: [UIE-8743] - Adding fixed cypress e2e tests provided and as r…
tvijay-akamai May 22, 2025
fc7702c
change: [UIE-8743] - fixed cypress e2e test
tvijay-akamai May 22, 2025
d86d553
Merge branch 'develop' into feature/UIE-8743
tvijay-akamai May 22, 2025
3dacb66
Merge branch 'develop' into feature/UIE-8743
cpathipa May 22, 2025
6de8a33
change: [UIE-8743] - cypress e2e tests fix from sakshi
tvijay-akamai May 22, 2025
7924fd7
change: [UIE-8743] - cypress e2e tests fix from sakshi
tvijay-akamai May 23, 2025
1b63b8c
UIE-8743 Resolved merge conflicts
tvijay-akamai Jun 20, 2025
7d6d096
UIE-8743 Resolved merge conflicts
tvijay-akamai Jun 20, 2025
bbc2ce1
Merge branch 'develop' into feature/UIE-8743
tvijay-akamai Jun 20, 2025
ad04d1a
Resolved merge conflicts
tvijay-akamai Jun 20, 2025
1c9945d
fixed cypress e2e tests
stayal712 Jun 25, 2025
de2e634
Merge branch 'develop' into feature/UIE-8743
tvijay-akamai Jun 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Changed
---

Replace the button component under DBAAS with Akamai CDS button web component ([#12148](https://github.com/linode/manager/pull/12148))
Original file line number Diff line number Diff line change
Expand Up @@ -385,18 +385,21 @@
ui.tabList.findTabByTitle('Resize').click();

// Confirm that "Resize Database Cluster" button is disabled
ui.button
.findByTitle('Resize Database Cluster')
ui.cdsButton
.findButtonByTitle('Resize Database Cluster')
.should('be.visible')
.should('be.disabled');

// Navigate to "Settings" tab
ui.tabList.findTabByTitle('Settings').click();

// Confirm that "Manage Access" button is disabled
cy.get('[data-testid="button-access-control"]')
.should('be.visible')
.should('be.disabled');
cy.get('[data-testid="button-access-control"]').within(() => {

Check warning on line 397 in packages/manager/cypress/e2e/core/account/restricted-user-details-pages.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":397,"column":69,"nodeType":null,"endLine":397,"endColumn":71}
ui.cdsButton
.findButtonByTitle('Manage Access')
.should('be.visible')
.should('be.disabled');
});

// Confirm that "Remove" button is disabled
ui.button
Expand All @@ -405,20 +408,20 @@
.should('be.disabled');

// Confirm that "Reset Root Password" button is disabled
ui.button
.findByTitle('Reset Root Password')
ui.cdsButton
.findButtonByTitle('Reset Root Password')
.should('be.visible')
.should('be.disabled');

// Confirm that "Delete Cluster" button is disabled
ui.button
.findByTitle('Delete Cluster')
ui.cdsButton
.findButtonByTitle('Delete Cluster')
.should('be.visible')
.should('be.disabled');

// Confirm that "Save Changes" button is disabled
ui.button
.findByTitle('Save Changes')
ui.cdsButton
.findButtonByTitle('Save Changes')
.should('be.visible')
.should('be.disabled');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,13 @@ const addConfigsToUI = (

cy.contains(flatKey).should('be.visible').click();

ui.button.findByTitle('Add').click();
ui.cdsButton.findButtonByTitle('Add').then((btn) => {
btn[0].click(); // Native DOM click
});

// Type value for non-boolean configs
if (value.type !== 'boolean') {
cy.get(`[name="${flatKey}"]`).scrollIntoView();
cy.get(`[name="${flatKey}"]`).should('be.visible').clear();
cy.get(`[name="${flatKey}"]`).type(additionalConfigs[flatKey]);
}
Expand Down Expand Up @@ -208,17 +211,17 @@ describe('Update database clusters', () => {
cy.findByText(defaultConfig).should('be.visible');
});

// Confirms all teh buttons are in the initial state - enabled/disabled
ui.button
.findByTitle('Configure')
// Confirms all the buttons are in the initial state - enabled/disabled
ui.cdsButton
.findButtonByTitle('Configure')
.should('be.visible')
.should('be.enabled')
.click();

ui.drawer.findByTitle('Advanced Configuration').should('be.visible');
ui.button
.findByTitle('Add')
.should('be.visible')
ui.cdsButton
.findButtonByTitle('Add')
.should('exist')
.should('be.disabled');
ui.button
.findByTitle('Save')
Expand All @@ -233,11 +236,12 @@ describe('Update database clusters', () => {
.should('be.enabled')
.click();

ui.button
.findByTitle('Configure')
ui.cdsButton
.findButtonByTitle('Configure')
.should('be.visible')
.should('be.enabled')
.click();

ui.drawer.findByTitle('Advanced Configuration').should('be.visible');
cy.get('[aria-label="Close drawer"]')
.should('be.visible')
Expand Down Expand Up @@ -289,8 +293,8 @@ describe('Update database clusters', () => {
cy.wait(['@getDatabase', '@getDatabaseTypes']);

// Expand configure drawer to add configs
ui.button
.findByTitle('Configure')
ui.cdsButton
.findButtonByTitle('Configure')
.should('be.visible')
.should('be.enabled')
.click();
Expand Down Expand Up @@ -377,8 +381,8 @@ describe('Update database clusters', () => {
cy.wait(['@getDatabase', '@getDatabaseTypes']);

// Expand configure drawer to add configs
ui.button
.findByTitle('Configure')
ui.cdsButton
.findButtonByTitle('Configure')
.should('be.visible')
.should('be.enabled')
.click();
Expand Down Expand Up @@ -462,8 +466,8 @@ describe('Update database clusters', () => {
cy.wait(['@getDatabase', '@getDatabaseTypes']);

// Expand configure drawer to add configs
ui.button
.findByTitle('Configure')
ui.cdsButton
.findButtonByTitle('Configure')
.should('be.visible')
.should('be.enabled')
.click();
Expand Down Expand Up @@ -495,9 +499,12 @@ describe('Update database clusters', () => {

cy.contains(flatKey).should('be.visible').click();

ui.button.findByTitle('Add').click();
ui.cdsButton.findButtonByTitle('Add').then((btn) => {
btn[0].click(); // Native DOM click
});

// Validate value for inline minimum limit
cy.get(`[name="${flatKey}"]`).scrollIntoView();
cy.get(`[name="${flatKey}"]`).should('be.visible').clear();
cy.get(`[name="${flatKey}"]`).type(`${value.minimum - 1}`);
cy.get(`[name="${flatKey}"]`).blur();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@
cy.findAllByTestId('currentSummary').should('be.visible');

// Create database, confirm redirect, and that new instance is listed.
cy.findByText('Create Database Cluster').should('be.visible').click();
ui.cdsButton
.findButtonByTitle('Create Database Cluster')

Check warning on line 198 in packages/manager/cypress/e2e/core/databases/create-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":198,"column":30,"nodeType":"Literal","endLine":198,"endColumn":55}
.then((btn) => {
btn[0].click(); // Native DOM click
});
Comment on lines +199 to +201
Copy link
Contributor

Choose a reason for hiding this comment

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

@stayal712 and @tvijay-akamai, do you have an explanation for why this workaround is necessary?

In my testing, I'm able to implement a simple (non-CDS) button in a shadow root, set up an event handler, and trigger it by clicking via cy.click(). Why doesn't this work with the CDS button? Why aren't we running into any of these same issues with the text field component?

I'm willing to resort to hacky workarounds like this if they're necessary, but we should at least understand why. This is a lot of work to click on a button, and the fact that this is only an issue with the CDS button makes it sound like a bug. I want to rule that out, and I especially want to avoid implementing more hacks like this as we introduce more CDS components.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am going to try to offer a potential fix, without confidence it'll work. Bear in mind, my knowledge of web components is limited, however I'd still like to explore this before moving forward with the workaround proposed, which seem to be pointing at a potential limitation of the component.

https://git.source.akamai.com/projects/FEE/repos/cds-web-components/browse/packages/cds-web-components/src/components/button/button.element.ts

Custom elements sometimes don’t work well with React’s synthetic event system. onClick in React won’t fire unless the component dispatches a standard DOM event from the host. This may be the issue we've been noticing with Cypress.

Currently, this listens to @click internally on the native button, but we don't re-emit the event from the custom element. Which could perhaps be done this way:

private _handleClick(e: MouseEvent) {
  if (this.type === 'submit') {
    const form = this.closest('form');
    if (form) {
      e.preventDefault();
      form.requestSubmit();
    }
  }

  // Re-emit the click event from the custom element host
  this.dispatchEvent(new MouseEvent('click', {
    bubbles: true,
    composed: true,
    cancelable: true,
  }));
}

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent

Again, not sure this will fix it, but before approving I'd like to explore this because it would also have important implications for other Web Components making their way to Cloud Manager. It could even be expanded further from what I could read, but not sure how helpful that would be.

this.dispatchEvent(new MouseEvent('click', {
    bubbles: true,
    composed: true,
    cancelable: true,
    detail: e.detail,
    screenX: e.screenX,
    screenY: e.screenY,
    clientX: e.clientX,
    clientY: e.clientY,
    ctrlKey: e.ctrlKey,
    shiftKey: e.shiftKey,
    altKey: e.altKey,
    metaKey: e.metaKey,
    button: e.button,
    relatedTarget: e.relatedTarget,
  }));

Copy link

@spadhi-akamai spadhi-akamai May 22, 2025

Choose a reason for hiding this comment

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

@abailly-akamai
Thank you for your suggestion. Tarun has started looking into your suggestion and will also explore alternative solutions with QA team to find the best way out to access shadow dom elements in cypress, in the next release. Meanwhile, request you to move forward with this PR for the current release.

Copy link
Contributor

Choose a reason for hiding this comment

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

Approved. It would be greatly appreciated if the next PR for a web component takes in consideration all the problems raised in this one, and the compatibility with our codebase not to be an afterthought. Merging this as-is gives us no guaranty there will be a follow up. The branch cut is in a week by the way.

Copy link
Contributor Author

@tvijay-akamai tvijay-akamai Jun 20, 2025

Choose a reason for hiding this comment

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

@abailly-akamai We explored multiple approaches including the one you previously suggested to address the Cypress implementation preference specifically related to buttons of type "submit" in our web component. There are only 2 button type submit. (For other button types there is no concern as discussed with Sakshi.)

While we did identify one solution that resolves the issue, the team decided not to proceed with it. The team's consensus is that introducing code changes solely to accommodate Cypress’s implementation preferences is not optimal, especially since there is an alternative way to simulate and test submit-type button behavior in our e2e test cases. For all other button types, our current approach works as expected.

Let us know if you’d like more details on the alternatives we considered. we can separately sync-up on the same.

cy.wait('@createDatabase');

// TODO Update assertions upon completion of M3-7030.
Expand Down Expand Up @@ -327,11 +331,10 @@
// table present for restricted user but its inputs will be disabled
cy.get('table[aria-label="List of Linode Plans"]').should('exist');
// Assert that Create Database button is visible and disabled
ui.button
.findByTitle('Create Database Cluster')
ui.cdsButton
.findButtonByTitle('Create Database Cluster')
.should('be.visible')
.and('be.disabled')
.trigger('mouseover');
.should('be.disabled');

// Info message is visible
cy.findByText(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@
cy.wait(['@getAccount', '@getDatabase', '@getDatabaseTypes']);

// Click "Delete Cluster" button.
ui.button
.findByAttribute('data-qa-settings-button', 'Delete Cluster')
ui.cdsButton
.findButtonByTitle('Delete Cluster')

Check warning on line 57 in packages/manager/cypress/e2e/core/databases/delete-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 4 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 4 times.","line":57,"column":32,"nodeType":"Literal","endLine":57,"endColumn":48}
.should('be.visible')
.click();

Expand Down Expand Up @@ -116,8 +116,8 @@
cy.wait(['@getAccount', '@getDatabase', '@getDatabaseTypes']);

// Click "Delete Cluster" button.
ui.button
.findByAttribute('data-qa-settings-button', 'Delete Cluster')
ui.cdsButton
.findButtonByTitle('Delete Cluster')
.should('be.visible')
.click();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
*/

const resizeDatabase = (initialLabel: string) => {
ui.button
.findByTitle('Resize Database Cluster')
ui.cdsButton
.findButtonByTitle('Resize Database Cluster')

Check warning on line 36 in packages/manager/cypress/e2e/core/databases/resize-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":36,"column":24,"nodeType":"Literal","endLine":36,"endColumn":49}
.should('be.visible')
.should('be.enabled')
.click();
Expand Down Expand Up @@ -98,8 +98,8 @@
cy.get('[data-reach-tab-list]').within(() => {
cy.findByText('Resize').should('be.visible').click();
});
ui.button
.findByTitle('Resize Database Cluster')
ui.cdsButton
.findButtonByTitle('Resize Database Cluster')
.should('be.visible')
.should('be.disabled');

Expand Down Expand Up @@ -246,8 +246,9 @@
cy.get('[data-reach-tab-list]').within(() => {
cy.findByText('Resize').should('be.visible').click();
});
ui.button
.findByTitle('Resize Database Cluster')

ui.cdsButton
.findButtonByTitle('Resize Database Cluster')
.should('be.visible')
.should('be.disabled');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@
* @param existingIps - The number of existing IPs. Optional, default is `0`.
*/
const manageAccessControl = (allowedIps: string[], existingIps: number = 0) => {
cy.findByTestId('button-access-control').click();
cy.get('[data-testid="button-access-control"]').within(() => {
ui.cdsButton.findButtonByTitle('Manage Access').click();

Check warning on line 103 in packages/manager/cypress/e2e/core/databases/update-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":103,"column":36,"nodeType":"Literal","endLine":103,"endColumn":51}
});

ui.drawer
.findByTitle('Manage Access')
Expand Down Expand Up @@ -132,8 +134,8 @@
* made on the result of the root password reset attempt.
*/
const resetRootPassword = () => {
ui.button
.findByAttribute('data-qa-settings-button', 'Reset Root Password')
ui.cdsButton
.findButtonByTitle('Reset Root Password')

Check warning on line 138 in packages/manager/cypress/e2e/core/databases/update-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 5 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 5 times.","line":138,"column":24,"nodeType":"Literal","endLine":138,"endColumn":45}
.should('be.visible')
.click();

Expand Down Expand Up @@ -165,7 +167,7 @@
cy.findByText('Maintenance');
cy.findByText('Version');
cy.findByText(`${dbEngine} v${version}`);
ui.button.findByTitle('Upgrade Version').should('be.visible');
ui.cdsButton.findButtonByTitle('Upgrade Version').should('be.visible');
});
};

Expand All @@ -180,11 +182,16 @@
*/
const modifyMaintenanceWindow = (label: string, windowValue: string) => {
cy.findByText('Set a Weekly Maintenance Window');
cy.findByTitle('Save Changes').should('be.visible').should('be.disabled');
ui.cdsButton
.findButtonByTitle('Save Changes')
.should('be.visible')
.should('be.disabled');

Check warning on line 188 in packages/manager/cypress/e2e/core/databases/update-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 5 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 5 times.","line":188,"column":13,"nodeType":"Literal","endLine":188,"endColumn":26}

ui.autocomplete.findByLabel(label).should('be.visible').type(windowValue);
cy.contains(windowValue).should('be.visible').click();
ui.button.findByTitle('Save Changes').should('be.visible').click();
ui.cdsButton.findButtonByTitle('Save Changes').then((btn) => {
btn[0].click(); // Native DOM click
});
};

/**
Expand Down Expand Up @@ -253,7 +260,7 @@
cy.findByText(hostnameRegex).should('be.visible');

// DBaaS passwords cannot be revealed when database/cluster is suspended or resuming.
ui.button.findByTitle('Show').should('be.visible').should('be.enabled');
ui.cdsButton.findButtonByTitle('Show').should('be.enabled');

// Navigate to "Settings" tab.
ui.tabList.findTabByTitle('Settings').click();
Expand Down Expand Up @@ -393,18 +400,14 @@

cy.findByText('Connection Details');
// "Show" button should be enabled to reveal password when DB is active.
ui.button
.findByTitle('Show')
.should('be.visible')
.should('be.enabled')
.click();
ui.cdsButton.findButtonByTitle('Show').should('be.enabled').click();

cy.wait('@getCredentials');
cy.findByText(`${initialPassword}`);

// "Hide" button should be enabled to hide password when password is revealed.
ui.button
.findByTitle('Hide')
ui.cdsButton
.findButtonByTitle('Hide')
.should('be.visible')
.should('be.enabled')
.click();
Expand Down Expand Up @@ -523,8 +526,8 @@

cy.findByText('Connection Details');
// DBaaS passwords cannot be revealed until database/cluster has provisioned.
ui.button
.findByTitle('Show')
ui.cdsButton
.findButtonByTitle('Show')
.should('be.visible')
.should('be.disabled');

Expand All @@ -542,6 +545,14 @@
// Navigate to "Settings" tab.
ui.tabList.findTabByTitle('Settings').click();

cy.get('[data-testid="settings-button-Suspend Cluster"]').within(
() => {

Check warning on line 549 in packages/manager/cypress/e2e/core/databases/update-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":549,"column":16,"nodeType":null,"endLine":549,"endColumn":18}
ui.cdsButton
.findButtonByTitle('Suspend Cluster')
.should('be.disabled');
}
);

// Reset root password.
resetRootPassword();
cy.wait('@resetRootPassword');
Expand Down Expand Up @@ -576,10 +587,6 @@
'Maintenance Window settings saved successfully.'
);

cy.get('[data-qa-settings-button="Suspend Cluster"]').should(
'be.disabled'
);

// Navigate to "Networking" tab.
ui.tabList.findTabByTitle('Networking').click();

Expand Down Expand Up @@ -670,7 +677,15 @@
ui.tabList.findTabByTitle('Settings').click();

// Suspend an active cluster
cy.get('[data-qa-settings-button="Suspend Cluster"]').click();
cy.get('[data-testid="settings-button-Suspend Cluster"]').within(
() => {

Check warning on line 681 in packages/manager/cypress/e2e/core/databases/update-database.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":681,"column":16,"nodeType":null,"endLine":681,"endColumn":18}
ui.cdsButton
.findButtonByTitle('Suspend Cluster')
.should('be.visible')
.should('be.enabled')
.click();
}
);
suspendCluster(initialLabel);
cy.wait('@suspendDatabase');

Expand Down
Loading
Loading