Skip to content

feat(web_components): implement basic web components and catalog adapters - #2074

Draft
josemontespg wants to merge 2 commits into
a2ui-project:mainfrom
josemontespg:agent/wt_8e39d767
Draft

feat(web_components): implement basic web components and catalog adapters#2074
josemontespg wants to merge 2 commits into
a2ui-project:mainfrom
josemontespg:agent/wt_8e39d767

Conversation

@josemontespg

Copy link
Copy Markdown
Collaborator

What

Implement basic web components (a2ui-card, a2ui-button, a2ui-text-field, a2ui-select, a2ui-switch, a2ui-dialog, a2ui-toast) and catalog adapters for Angular and React.

Why

Consolidates duplicated framework-specific templates into portable Lit Custom Elements, reducing maintenance overhead and ensuring cross-framework interoperability.

Effect

Implements the approved A2UI Task Analysis design for Web Components.

…ters

Implement basic web components (a2ui-card, a2ui-button, a2ui-text-field, a2ui-select, a2ui-switch, a2ui-dialog, a2ui-toast) and catalog adapters according to the approved A2UI Task Analysis design.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces portable Web Components for Select, Switch, Dialog, and Toast in the Lit renderer, along with their corresponding schemas and tests in web_core. It also adds catalog adapter helpers to map component APIs to custom element tag names. The review feedback highlights several issues: the Select component prevents change events when the initial value is undefined; the Dialog component defaults to open when the property is omitted, and its ::backdrop styling will not render correctly under declarative usage; the adapter helpers generate incorrect tag names for multi-word components by not using kebab-case; and finally, the new Lit components lack unit tests, violating the repository style guide.

Comment on lines +61 to +70
if (this.controller.props?.value) {
// Dispatch custom event for binding update
this.dispatchEvent(
new CustomEvent('a2uichange', {
detail: {value: target.value},
bubbles: true,
composed: true,
}),
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The if (this.controller.props?.value) check prevents the a2uichange event from firing if the initial value is empty ("") or undefined. Since we want to allow users to select an option even if no value was previously set, this check should be removed so the event is always dispatched on change.

    // Dispatch custom event for binding update
    this.dispatchEvent(
      new CustomEvent('a2uichange', {
        detail: {value: target.value},
        bubbles: true,
        composed: true,
      }),
    );

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Removed the check.

const props = this.controller.props;
if (!props) return nothing;

const isOpen = props.open !== false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using props.open !== false means that if props.open is undefined, isOpen will evaluate to true. However, the DialogApi schema defines open with a default of false. To respect this default and prevent the dialog from being open by default when the property is omitted, use Boolean(props.open) instead.

Suggested change
const isOpen = props.open !== false;
const isOpen = Boolean(props.open);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Used Boolean(props.open).

Comment on lines +39 to +42
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(2px);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The dialog::backdrop pseudo-element is only rendered by browsers when the dialog is opened using the native .showModal() method. Since this component renders <dialog open> declaratively, the native backdrop will not be displayed. Consider rendering a custom backdrop element in the template or managing the dialog opening/closing imperatively using .showModal() and .close() in the Lit lifecycle.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Managed open/close imperatively using showModal() and close() in updated lifecycle to ensure native backdrop rendering.

Comment on lines +44 to +47
for (const [name, comp] of catalog.components.entries()) {
const tagName = tagMap[name] || `a2ui-${name.toLowerCase()}`;
adaptedComponents.push(createComponentAdapter(comp, tagName));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using name.toLowerCase() directly will produce incorrect tag names for multi-word components (e.g., a2ui-textfield instead of a2ui-text-field, or a2ui-datetimeinput instead of a2ui-date-time-input). Convert the PascalCase/camelCase component names to kebab-case to ensure consistent and correct tag names.

  for (const [name, comp] of catalog.components.entries()) {
    const kebabName = name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
    const tagName = tagMap[name] || ('a2ui-' + kebabName);
    adaptedComponents.push(createComponentAdapter(comp, tagName));
  }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Added toKebabCase helper and used it.

Comment on lines +64 to +67
for (const [name, comp] of catalog.components.entries()) {
const tagName = tagMap[name] || `a2ui-${name.toLowerCase()}`;
adaptedComponents.push(createComponentAdapter(comp, tagName));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using name.toLowerCase() directly will produce incorrect tag names for multi-word components (e.g., a2ui-textfield instead of a2ui-text-field, or a2ui-datetimeinput instead of a2ui-date-time-input). Convert the PascalCase/camelCase component names to kebab-case to ensure consistent and correct tag names.

  for (const [name, comp] of catalog.components.entries()) {
    const kebabName = name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
    const tagName = tagMap[name] || ('a2ui-' + kebabName);
    adaptedComponents.push(createComponentAdapter(comp, tagName));
  }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Added toKebabCase helper and used it.

BASIC_FUNCTIONS,
);

export {A2uiSelect, A2uiSwitch, A2uiDialog, A2uiToast};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

According to the repository style guide, code changes should have tests. The newly added Lit components (Select, Switch, Dialog, Toast) currently lack unit/component tests in renderers/lit. Please add corresponding tests to verify their rendering and behavior.

References
  1. If there are code changes, code should have tests. (link)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Added unit tests for Select, Switch, Dialog, and Toast in renderers/lit.

@josemontespg
josemontespg marked this pull request as draft July 22, 2026 19:43
…ropagation, fix Dialog backdrop and default open values, fix kebab-case tag name construction for multi-word components, and add component tests for Select, Switch, Dialog, and Toast.
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.

1 participant