Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [[0.1.19](https://github.com/multiversx/mx-sdk-dapp-ui/pull/302)] - 2026-01-29

- [Fix duplicated `data-testid` on `mvx-button` host and inner `<button>`](https://github.com/multiversx/mx-sdk-dapp-ui/pull/302)

## [[0.1.18](https://github.com/multiversx/mx-sdk-dapp-ui/pull/301)] - 2026-01-29

- [Refactor AddressTable styles to use min-width instead of width for better responsiveness](https://github.com/multiversx/mx-sdk-dapp-ui/pull/300)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@multiversx/sdk-dapp-ui",
"version": "0.1.18",
"version": "0.1.19",
"description": "A library to hold UI components for a dApp on the MultiversX blockchain",
"author": "MultiversX",
"license": "MIT",
Expand Down
17 changes: 16 additions & 1 deletion src/components/visual/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { EventEmitter } from '@stencil/core';
import { Component, Event, h, Prop } from '@stencil/core';
import { Component, Element, Event, h, Prop } from '@stencil/core';
import { Button as ButtonComponent } from 'common/Button/Button';

import type { ButtonSizeEnum, ButtonVariantEnum } from '../../../common/Button/button.types';
Expand All @@ -10,6 +10,8 @@ import type { ButtonSizeEnum, ButtonVariantEnum } from '../../../common/Button/b
shadow: true,
})
export class Button {
@Element() hostElement!: HTMLElement;

@Event() buttonClick: EventEmitter<MouseEvent>;

@Prop() class?: string = '';
Expand All @@ -18,6 +20,19 @@ export class Button {
@Prop() size?: `${ButtonSizeEnum}` = 'large';
@Prop() variant?: `${ButtonVariantEnum}` = 'primary';

/**
* Stencil reflects `data-testid` to the host element based on the Prop decorator above.
* That causes two DOM nodes with the same `data-testid`: the `<mvx-button>` host and
* the inner `<button>`, which breaks Playwright strict mode. We keep using the
* attribute to populate `dataTestId`, but remove it from the host after render so that
* only the inner `<button>` keeps the `data-testid`.
*/
componentDidRender() {
if (this.hostElement.hasAttribute('data-testid')) {
this.hostElement.removeAttribute('data-testid');
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

While this approach works, a more declarative and idiomatic way to achieve this in Stencil is to prevent the prop from being reflected to the host element's attributes in the first place. You can do this by setting reflect: false on the @Prop decorator for dataTestId.

This would prevent the attribute from being added to the host element, eliminating the need for this lifecycle hook to remove it manually.

For example:

// If the attribute is `data-test-id` (from `dataTestId` prop)
@Prop({ reflect: false }) dataTestId?: string = '';

// If the attribute is `data-testid`
@Prop({ attribute: 'data-testid', reflect: false }) dataTestId?: string = '';

This change would make the component's logic cleaner and avoid manual DOM manipulation.


private handleClick = (event: MouseEvent) => {
this.buttonClick.emit(event);
};
Expand Down