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

Conversation

tvijay-akamai
Copy link
Contributor

@tvijay-akamai tvijay-akamai commented May 2, 2025

Description 📝

This PR is to Replaced CM Button component in DBAAS with Akamai CDS button Web Component

Changes 🔄

  • Replaced Buttons in DBAAS with Akamai CDS Button Web Components.
  • Changed only those button component that has no impact outside DBAAS.
  • No changes to functional behavior of the buttons. Button Sizes may differ when compared to production.

Target release date 🗓️

July 15th Release

Preview 📷

Include a screenshot or screen recording of the change.

🔒 Use the Mask Sensitive Data setting for security.

💡 Use <video src="" /> tag when including recordings in table.

Before After
Screenshot 2025-05-02 at 4 12 01 PM Screenshot 2025-05-02 at 4 14 20 PM
Screenshot 2025-05-02 at 4 12 56 PM Screenshot 2025-05-02 at 4 13 58 PM
Screenshot 2025-05-02 at 4 12 41 PM Screenshot 2025-05-02 at 4 13 45 PM

How to test 🧪

  • Test all the buttons in DBAAS for functional and any UX issues.

Verification steps

(How to verify changes)

  • Test all the buttons in DBAAS for functional and any UX alignment issues. There should no functional impact. Size of button may change compared to prod.

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

👀 Doing a self review
❔ Our contribution guidelines
🤏 Splitting feature into small PRs
➕ Adding a changeset
🧪 Providing/improving test coverage
🔐 Removing all sensitive information from the code and PR description
🚩 Using a feature flag to protect the release
👣 Providing comprehensive reproduction steps
📑 Providing or updating our documentation
🕛 Scheduling a pair reviewing session
📱 Providing mobile support
♿ Providing accessibility 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 unit tests are passing
  • TypeScript compilation succeeded without errors
  • Code passes all linting rules

Commit message and pull request title format standards

Note: Remove this section before opening the pull request
Make sure your PR title and commit message on squash and merge are as shown below

<commit type>: [JIRA-ticket-number] - <description>

Commit Types:

  • feat: New feature for the user (not a part of the code, or ci, ...).
  • fix: Bugfix for the user (not a fix to build something, ...).
  • change: Modifying an existing visual UI instance. Such as a component or a feature.
  • refactor: Restructuring existing code without changing its external behavior or visual UI. Typically to improve readability, maintainability, and performance.
  • test: New tests or changes to existing tests. Does not change the production code.
  • upcoming: A new feature that is in progress, not visible to users yet, and usually behind a feature flag.

Example: feat: [M3-1234] - Allow user to view their login history


@tvijay-akamai tvijay-akamai requested a review from a team as a code owner May 2, 2025 11:21
@tvijay-akamai tvijay-akamai requested review from dwiley-akamai and abailly-akamai and removed request for a team May 2, 2025 11:21
@cpathipa cpathipa added the DBaaS Relates to Database as a Service label May 2, 2025
@cpathipa cpathipa requested review from cpathipa and corya-akamai and removed request for dwiley-akamai May 2, 2025 16:28
mockMatchMedia,
renderWithTheme,
getShadowRootButton,
} from 'src/utilities/testHelpers'; // Import the utility
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment not needed


expect(createClusterButton).toBeInTheDocument();
const buttonHost = getByTestId('create-database-cluster'); // Query the host element
Copy link
Contributor

Choose a reason for hiding this comment

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

As long as the getShadowRootButton function has JS docs and comments explaining usage, we don't need the comments in the .test.tsx getByTestId is common so everyone knows what it's doing.

@@ -34,3 +34,9 @@ export const GroupItems = styled('ul')(({ theme }) => ({
},
padding: 0,
}));

export const StyledButtonWrapper = styled('div')(({ theme }) => ({
Copy link
Contributor

Choose a reason for hiding this comment

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

Comments are redundant.

@@ -58,6 +59,10 @@ interface Props {
interface FormValues {
configs: ConfigurationOption[];
}
const StyledAddButtonWrapper = styled('div')(({ theme }) => ({
minWidth: 'auto', // Ensure no default minimum width
Copy link
Contributor

Choose a reason for hiding this comment

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

Department of Redundancy Department :)


await userEvent.click(resizeButton);
await userEvent.click(resizeButton as HTMLButtonElement);
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious if something was complaining without the cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah was getting Argument of type 'HTMLButtonElement | null' is not assignable to parameter of type 'Element'.

data-qa-settings-button={buttonText}
disabled={disabled}
onClick={onClick}
title={buttonText}
title={buttonText} // Title will be passed automatically to dom since it is standard HTML attribute
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment not needed, those are internal impl. details.

hour_of_day: database.updates?.hour_of_day ?? 20,
week_of_month: getInitialWeekOfMonth(),
},
// validationSchema: updateDatabaseSchema,
Copy link
Contributor

Choose a reason for hiding this comment

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

I see this was already commented. Not sure why though.

* @param host - The web component host element.
* @returns A promise that resolves to the button element inside the Shadow DOM, or null if not found.
*/
export const getShadowRootButton = (
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make this function generic getShadowRoot and then you pass in the type you're looking for? Also this needs tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Besides making this more generic, I'd like to find a better approach to test these components in Cloud Manager. setTimeOut feels hacky at best and this requires to make all those tests async for just rendering DOM elements. I am hoping there's better support out there for Web Components which i'd hope could be explored before signing up on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’ve generalized the utility and found a solution to eliminate the use of setTimeout. However, we still need to rely on async handling, as there’s a possibility that the Shadow DOM may not be rendered yet when attempting to access shadowRoot.

Copy link
Contributor

@abailly-akamai abailly-akamai left a comment

Choose a reason for hiding this comment

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

This new component needs to adhere to basic Accessibility standards and to be consistent with its Cloud Manager counterpart. It appears to do it right overall, but I am noticing some discrepancies and lacking a couple needed improvements:

  • the focus state is barely visible when its variant is primary
  • there is no active state

* @param host - The web component host element.
* @returns A promise that resolves to the button element inside the Shadow DOM, or null if not found.
*/
export const getShadowRootButton = (
Copy link
Contributor

Choose a reason for hiding this comment

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

Besides making this more generic, I'd like to find a better approach to test these components in Cloud Manager. setTimeOut feels hacky at best and this requires to make all those tests async for just rendering DOM elements. I am hoping there's better support out there for Web Components which i'd hope could be explored before signing up on this.

disabled={!formTouched || isSubmitting || disabled}
loading={isSubmitting}
processing={isSubmitting}
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't pertinent to this PR as much as the component itself, but I'd argue that if the goal is to show a loading pattern, the processing naming convention is rather unintuitive. Also, considering these components are going to live side by side with Cloud Manager components for a long time, aiming for prop consistency when possible (for which i believe it to be the case here) would go a long way. CC @jaalah-akamai

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

>
Add
</Button>
</StyledAddButtonWrapper>
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 a bit confused here, why using a container to set en element's width? This points to a stylistic limitation of the component itself

Copy link
Contributor

Choose a reason for hiding this comment

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

@tvijay-akamai is this still needed with the changes you made to the components?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this is not needed now after core component change. I have removed it.

Copy link
Contributor

@cpathipa cpathipa left a comment

Choose a reason for hiding this comment

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

Missing changeset.

Copy link
Contributor

@cpathipa cpathipa left a comment

Choose a reason for hiding this comment

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

Overall, LGTM! I left a few nitpicks, nothing blocking, but worth considering. Also, I strongly agree with Alban's feedback. In addition, this PR needs a changeset and conflicts should be resolved before merging.


expect(createClusterButton).toBeInTheDocument();
const buttonHost = getByTestId('create-database-cluster'); // Query the host element
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Consider replacing getByTestId with getByRole for improved accessibility and alignment with React Testing Library best practices.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the suggestion to use getByRole for improved accessibility and alignment with React Testing Library best practices. I completely agree that getByRole is generally preferred as it aligns with accessibility standards and ensures that elements are queried based on their semantic roles.

However, in this specific case, the element we are trying to access resides inside a Shadow DOM. Unfortunately, getByRole does not penetrate the Shadow DOM or have access to elements within the Shadow Root. As a result, we need to rely on getByTestId to query the wrapper element or host element that contains the Shadow DOM. Once we have access to the wrapper, we can use a utility function like getShadowRootElement to query elements inside the Shadow DOM.

This approach ensures that we can still test Shadow DOM elements effectively while working within the constraints of the React Testing Library and the Shadow DOM API.

If there are any alternative suggestions or tools that better support Shadow DOM testing while adhering to accessibility best practices, I’d be happy to explore them further.

@tvijay-akamai
Copy link
Contributor Author

This new component needs to adhere to basic Accessibility standards and to be consistent with its Cloud Manager counterpart. It appears to do it right overall, but I am noticing some discrepancies and lacking a couple needed improvements:

  • the focus state is barely visible when its variant is primary
  • there is no active state

@abailly-akamai , please send me where i can find basic accessibility standards for cloud manager.

@abailly-akamai
Copy link
Contributor

@tvijay-akamai to recap, here's what we should aim to get this PR merged (spoke to Ellen about it)

  1. This is currently breaking our pipeline, which can't access the remote (this is why you are seeing all these failures)
  2. We need to bump the lib version with the following fix for the button:
  • missing focus styles (see ng-akamai-core lib for examples)

@tvijay-akamai
Copy link
Contributor Author

@abailly-akamai I have upgraded the web component library version which has the fix for the button focus styles. Please check and approve if looks good.

Copy link
Contributor

@corya-akamai corya-akamai left a comment

Choose a reason for hiding this comment

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

@tvijay-akamai let's revisit the e2e tests

@github-project-automation github-project-automation bot moved this from Approved to Changes Requested in Cloud Manager May 22, 2025
@tvijay-akamai tvijay-akamai added Do Not Merge and removed Add'tl Approval Needed Waiting on another approval! labels May 27, 2025
@abailly-akamai
Copy link
Contributor

@corya-akamai @tvijay-akamai @stayal712 what is the status of this PR?

We think the e2e issues we were seeing could be related to the button implementation itself, not necessarily warranting attempts to find the perfect workaround in Cypress. Once we confirm or rule that out (we could be quite wrong), we can move forward with either an upstream solution or the proposed implementation. Let us know how you want to proceed!

@spadhi-akamai
Copy link

@abailly-akamai Tarun is currently evaluating the most effective path forward and is exploring several potential approaches. We will keep the group informed once a final decision has been made.

@dmcintyr-akamai
Copy link
Contributor

Stupid question: what does 'CDS' stand for?

and i can't see any difference in the button(s) between the before and after screenshots; is that how it's intended to be?

@tvijay-akamai
Copy link
Contributor Author

Stupid question: what does 'CDS' stand for?

and i can't see any difference in the button(s) between the before and after screenshots; is that how it's intended to be?

Not a stupid question at all! 'CDS' stands for Akamai Core Design System. It's a comprehensive design framework that provides standardized UI components and design guidelines to ensure consistency and efficiency across Akamai's applications and services.

Yes, no difference is intentional. While the visual appearance of the buttons remains the same, the underlying implementation has been updated. Goal is to transition to using a new, framework-agnostic component. This change doesn't affect the look or behavior of the buttons but enhances their flexibility and maintainability across different Akamai platforms.

@tvijay-akamai
Copy link
Contributor Author

@corya-akamai @tvijay-akamai @stayal712 what is the status of this PR?

We think the e2e issues we were seeing could be related to the button implementation itself, not necessarily warranting attempts to find the perfect workaround in Cypress. Once we confirm or rule that out (we could be quite wrong), we can move forward with either an upstream solution or the proposed implementation. Let us know how you want to proceed!

@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.

Copy link
Contributor

@corya-akamai corya-akamai left a comment

Choose a reason for hiding this comment

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

Thanks for looking into the alternatives @tvijay-akamai

const button2 = getByTitle('Save Changes');
const button3 = getByRole('button', { name: 'Manage Access' });

const resetPasswordButtonHost = getByTestId(
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for making the variable names more explicit.

@github-project-automation github-project-automation bot moved this from Changes Requested to Approved in Cloud Manager Jun 23, 2025
Copy link
Contributor

@corya-akamai corya-akamai left a comment

Choose a reason for hiding this comment

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

The styling on the settings tab looks off

Screenshot 2025-06-23 at 5 18 47 PM

@linode-gh-bot
Copy link
Collaborator

Cloud Manager UI test results

🎉 666 passing tests on test run #22 ↗︎

❌ Failing✅ Passing↪️ Skipped🕐 Duration
0 Failing666 Passing4 Skipped138m 45s

@cpathipa cpathipa merged commit 091471a into linode:develop Jun 27, 2025
35 checks passed
@github-project-automation github-project-automation bot moved this from Approved to Merged in Cloud Manager Jun 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DBaaS Relates to Database as a Service
Projects
Status: Merged
Development

Successfully merging this pull request may close these issues.

10 participants