Skip to content

feat: support objects and references as Web Component props #5957

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 3 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 5 additions & 5 deletions packages/cli/src/scripts/create-wrappers/AttributesRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ function mapWebComponentTypeToTsType(type: string) {
return primitive;
}
switch (type) {
case 'HTMLElement | string | undefined':
case 'HTMLElement | string':
// opener props only accept strings as prop types
return 'string';
// case 'HTMLElement | string | undefined':
// case 'HTMLElement | string':
// // opener props only accept strings as prop types
// return 'string';

default:
if (!loggedTypes.has(type)) {
Expand Down Expand Up @@ -70,7 +70,7 @@ export class AttributesRenderer extends AbstractRenderer {
type = mapWebComponentTypeToTsType(type);

const references = attribute.type?.references;
const isEnum = references != null && references?.length > 0;
const isEnum = references != null && references?.length > 0 && attribute._ui5validator !== 'Object';

if (isEnum) {
type += ` | keyof typeof ${type}`;
Expand Down
8 changes: 1 addition & 7 deletions packages/cli/src/scripts/create-wrappers/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ import { WebComponentWrapper } from './WebComponentWrapper.js';
const WITH_WEB_COMPONENT_IMPORT_PATH = process.env.WITH_WEB_COMPONENT_IMPORT_PATH ?? '@ui5/webcomponents-react';

function filterAttributes(member: CEM.ClassField | CEM.ClassMethod): member is CEM.ClassField {
return (
member.kind === 'field' &&
member.privacy === 'public' &&
!member.readonly &&
!member.static &&
member._ui5validator !== 'Object'
);
return member.kind === 'field' && member.privacy === 'public' && !member.readonly && !member.static;
}

interface Options {
Expand Down
14 changes: 12 additions & 2 deletions packages/main/src/internal/withWebComponent.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {
setCustomElementsScopingSuffix,
setCustomElementsScopingRules
setCustomElementsScopingRules,
setCustomElementsScopingSuffix
} from '@ui5/webcomponents-base/dist/CustomElementsScope.js';
import { useReducer, useState } from 'react';
import type { ButtonDomRef } from '../webComponents/index.js';
import { Bar, Button, Switch } from '../webComponents/index.js';

describe('withWebComponent', () => {
Expand Down Expand Up @@ -172,4 +173,13 @@ describe('withWebComponent', () => {
cy.get('ui5-button').should('be.visible');
cy.get('ui5-button-ui5-wcr').should('not.exist');
});

it('pass objects as props', () => {
cy.mount(<Button accessibilityAttributes={{ expanded: 'true' }}>Test</Button>);
cy.findByText('Test').should('have.attr', 'ui5-button');
cy.wait(500);
cy.contains<ButtonDomRef>('Test').then(([$button]) => {
expect($button.accessibilityAttributes).to.deep.equal({ expanded: 'true' });
});
});
});
20 changes: 19 additions & 1 deletion packages/main/src/internal/withWebComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { camelToKebabCase, capitalizeFirstLetter, kebabToCamelCase } from './uti

const createEventPropName = (eventName: string) => `on${capitalizeFirstLetter(kebabToCamelCase(eventName))}`;

const isPrimitiveAttribute = (value: unknown): boolean => {
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
};

type EventHandler = (event: CustomEvent<unknown>) => void;

export interface WithWebComponentPropTypes {
Expand Down Expand Up @@ -49,7 +53,7 @@ export const withWebComponent = <Props extends Record<string, any>, RefType = Ui

// regular props (no booleans, no slots and no events)
const regularProps = regularProperties.reduce((acc, name) => {
if (rest.hasOwnProperty(name)) {
if (rest.hasOwnProperty(name) && isPrimitiveAttribute(rest[name])) {
return { ...acc, [camelToKebabCase(name)]: rest[name] };
}
return acc;
Expand Down Expand Up @@ -158,6 +162,20 @@ export const withWebComponent = <Props extends Record<string, any>, RefType = Ui
});
}
}, [Component, waitForDefine, isDefined]);

const propsToApply = regularProperties.map((prop) => ({ name: prop, value: props[prop] }));
useEffect(() => {
customElements.whenDefined(Component as unknown as string).then(() => {
for (const prop of propsToApply) {
if (prop.value != null && !isPrimitiveAttribute(prop.value)) {
if (ref.current) {
ref.current[prop.name] = prop.value;
}
}
}
});
}, [Component, ...propsToApply]);

if (waitForDefine && !isDefined) {
return null;
}
Expand Down
27 changes: 14 additions & 13 deletions packages/main/src/webComponents/Avatar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ import { withWebComponent } from '../../internal/withWebComponent.js';
import type { CommonProps, Ui5DomRef, UI5WCSlotsNode } from '../../types/index.js';

interface AvatarAttributes {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following field is supported:
*
* - **hasPopup**: Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by the button.
* Accepts the following string values: `dialog`, `grid`, `listbox`, `menu` or `tree`.
*
* **Note:** Available since [v2.0.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v2.0.0) of **@ui5/webcomponents**.
* @default {}
*/
accessibilityAttributes?: AvatarAccessibilityAttributes;

/**
* Defines the text alternative of the component.
* If not provided a default text alternative will be set, if present.
Expand Down Expand Up @@ -95,18 +107,7 @@ interface AvatarAttributes {
size?: AvatarSize | keyof typeof AvatarSize;
}

interface AvatarDomRef extends Required<AvatarAttributes>, Ui5DomRef {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following field is supported:
*
* - **hasPopup**: Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by the button.
* Accepts the following string values: `dialog`, `grid`, `listbox`, `menu` or `tree`.
*
* **Note:** Available since [v2.0.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v2.0.0) of **@ui5/webcomponents**.
*/
accessibilityAttributes: AvatarAccessibilityAttributes;
}
interface AvatarDomRef extends Required<AvatarAttributes>, Ui5DomRef {}

interface AvatarPropTypes extends AvatarAttributes, Omit<CommonProps, keyof AvatarAttributes | 'badge' | 'children'> {
/**
Expand Down Expand Up @@ -152,7 +153,7 @@ interface AvatarPropTypes extends AvatarAttributes, Omit<CommonProps, keyof Avat
*/
const Avatar = withWebComponent<AvatarPropTypes, AvatarDomRef>(
'ui5-avatar',
['accessibleName', 'colorScheme', 'fallbackIcon', 'icon', 'initials', 'shape', 'size'],
['accessibilityAttributes', 'accessibleName', 'colorScheme', 'fallbackIcon', 'icon', 'initials', 'shape', 'size'],
['disabled', 'interactive'],
['badge'],
[],
Expand Down
21 changes: 11 additions & 10 deletions packages/main/src/webComponents/AvatarGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@ import { withWebComponent } from '../../internal/withWebComponent.js';
import type { CommonProps, Ui5CustomEvent, Ui5DomRef, UI5WCSlotsNode } from '../../types/index.js';

interface AvatarGroupAttributes {
/**
* Defines the mode of the `AvatarGroup`.
* @default "Group"
*/
type?: AvatarGroupType | keyof typeof AvatarGroupType;
}

interface AvatarGroupDomRef extends Required<AvatarGroupAttributes>, Ui5DomRef {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following field is supported:
Expand All @@ -29,9 +21,18 @@ interface AvatarGroupDomRef extends Required<AvatarGroupAttributes>, Ui5DomRef {
* Accepts the following string values: `dialog`, `grid`, `listbox`, `menu` or `tree`.
*
* **Note:** Available since [v2.0.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v2.0.0) of **@ui5/webcomponents**.
* @default {}
*/
accessibilityAttributes: AvatarGroupAccessibilityAttributes;
accessibilityAttributes?: AvatarGroupAccessibilityAttributes;

/**
* Defines the mode of the `AvatarGroup`.
* @default "Group"
*/
type?: AvatarGroupType | keyof typeof AvatarGroupType;
}

interface AvatarGroupDomRef extends Required<AvatarGroupAttributes>, Ui5DomRef {
/**
* Returns an array containing the `AvatarColorScheme` values that correspond to the avatars in the component.
*/
Expand Down Expand Up @@ -140,7 +141,7 @@ interface AvatarGroupPropTypes
*/
const AvatarGroup = withWebComponent<AvatarGroupPropTypes, AvatarGroupDomRef>(
'ui5-avatar-group',
['type'],
['accessibilityAttributes', 'type'],
[],
['overflowButton'],
['click', 'overflow'],
Expand Down
49 changes: 30 additions & 19 deletions packages/main/src/webComponents/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ import { withWebComponent } from '../../internal/withWebComponent.js';
import type { CommonProps, Ui5DomRef } from '../../types/index.js';

interface ButtonAttributes {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following fields are supported:
*
* - **expanded**: Indicates whether the button, or another grouping element it controls, is currently expanded or collapsed.
* Accepts the following string values: `true` or `false`
*
* - **hasPopup**: Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by the button.
* Accepts the following string values: `dialog`, `grid`, `listbox`, `menu` or `tree`.
*
* - **controls**: Identifies the element (or elements) whose contents or presence are controlled by the button element.
* Accepts a lowercase string value.
*
* **Note:** Available since [v1.2.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v1.2.0) of **@ui5/webcomponents**.
* @default {}
*/
accessibilityAttributes?: ButtonAccessibilityAttributes;

/**
* Defines the accessible ARIA name of the component.
* @default undefined
Expand Down Expand Up @@ -99,24 +117,7 @@ interface ButtonAttributes {
type?: ButtonType | keyof typeof ButtonType;
}

interface ButtonDomRef extends Required<ButtonAttributes>, Ui5DomRef {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following fields are supported:
*
* - **expanded**: Indicates whether the button, or another grouping element it controls, is currently expanded or collapsed.
* Accepts the following string values: `true` or `false`
*
* - **hasPopup**: Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by the button.
* Accepts the following string values: `dialog`, `grid`, `listbox`, `menu` or `tree`.
*
* - **controls**: Identifies the element (or elements) whose contents or presence are controlled by the button element.
* Accepts a lowercase string value.
*
* **Note:** Available since [v1.2.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v1.2.0) of **@ui5/webcomponents**.
*/
accessibilityAttributes: ButtonAccessibilityAttributes;
}
interface ButtonDomRef extends Required<ButtonAttributes>, Ui5DomRef {}

interface ButtonPropTypes extends ButtonAttributes, Omit<CommonProps, keyof ButtonAttributes | 'children' | 'onClick'> {
/**
Expand Down Expand Up @@ -159,7 +160,17 @@ interface ButtonPropTypes extends ButtonAttributes, Omit<CommonProps, keyof Butt
*/
const Button = withWebComponent<ButtonPropTypes, ButtonDomRef>(
'ui5-button',
['accessibleName', 'accessibleNameRef', 'accessibleRole', 'design', 'endIcon', 'icon', 'tooltip', 'type'],
[
'accessibilityAttributes',
'accessibleName',
'accessibleNameRef',
'accessibleRole',
'design',
'endIcon',
'icon',
'tooltip',
'type'
],
['disabled', 'submits'],
[],
['click'],
Expand Down
13 changes: 2 additions & 11 deletions packages/main/src/webComponents/ColorPalettePopover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface ColorPalettePopoverAttributes {
* **Note:** Available since [v1.21.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v1.21.0) of **@ui5/webcomponents**.
* @default undefined
*/
opener?: string;
opener?: HTMLElement | string | undefined;

/**
* Defines whether the user can choose the default color from a button.
Expand All @@ -54,16 +54,7 @@ interface ColorPalettePopoverAttributes {
showRecentColors?: boolean;
}

interface ColorPalettePopoverDomRef extends Omit<Required<ColorPalettePopoverAttributes>, 'opener'>, Ui5DomRef {
/**
* Defines the ID or DOM Reference of the element that the popover is shown at.
* When using this attribute in a declarative way, you must only use the `id` (as a string) of the element at which you want to show the popover.
* You can only set the `opener` attribute to a DOM Reference when using JavaScript.
*
* **Note:** Available since [v1.21.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v1.21.0) of **@ui5/webcomponents**.
*/
opener: HTMLElement | string | undefined;
}
interface ColorPalettePopoverDomRef extends Required<ColorPalettePopoverAttributes>, Ui5DomRef {}

interface ColorPalettePopoverPropTypes
extends ColorPalettePopoverAttributes,
Expand Down
33 changes: 17 additions & 16 deletions packages/main/src/webComponents/CustomListItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ import { withWebComponent } from '../../internal/withWebComponent.js';
import type { CommonProps, Ui5CustomEvent, Ui5DomRef, UI5WCSlotsNode } from '../../types/index.js';

interface CustomListItemAttributes {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following fields are supported:
*
* - **ariaSetsize**: Defines the number of items in the current set when not all items in the set are present in the DOM.
* **Note:** The value is an integer reflecting the number of items in the complete set. If the size of the entire set is unknown, set `-1`.
*
* - **ariaPosinset**: Defines an element's number or position in the current set when not all items are present in the DOM.
* **Note:** The value is an integer greater than or equal to 1, and less than or equal to the size of the set when that size is known.
*
* **Note:** Available since [v1.15.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v1.15.0) of **@ui5/webcomponents**.
* @default {}
*/
accessibilityAttributes?: ListItemAccessibilityAttributes;

/**
* Defines the text alternative of the component.
*
Expand Down Expand Up @@ -66,21 +81,7 @@ interface CustomListItemAttributes {
type?: ListItemType | keyof typeof ListItemType;
}

interface CustomListItemDomRef extends Required<CustomListItemAttributes>, Ui5DomRef {
/**
* Defines the additional accessibility attributes that will be applied to the component.
* The following fields are supported:
*
* - **ariaSetsize**: Defines the number of items in the current set when not all items in the set are present in the DOM.
* **Note:** The value is an integer reflecting the number of items in the complete set. If the size of the entire set is unknown, set `-1`.
*
* - **ariaPosinset**: Defines an element's number or position in the current set when not all items are present in the DOM.
* **Note:** The value is an integer greater than or equal to 1, and less than or equal to the size of the set when that size is known.
*
* **Note:** Available since [v1.15.0](https://github.com/SAP/ui5-webcomponents/releases/tag/v1.15.0) of **@ui5/webcomponents**.
*/
accessibilityAttributes: ListItemAccessibilityAttributes;
}
interface CustomListItemDomRef extends Required<CustomListItemAttributes>, Ui5DomRef {}

interface CustomListItemPropTypes
extends CustomListItemAttributes,
Expand Down Expand Up @@ -121,7 +122,7 @@ interface CustomListItemPropTypes
*/
const CustomListItem = withWebComponent<CustomListItemPropTypes, CustomListItemDomRef>(
'ui5-li-custom',
['accessibleName', 'highlight', 'tooltip', 'type'],
['accessibilityAttributes', 'accessibleName', 'highlight', 'tooltip', 'type'],
['movable', 'navigated', 'selected'],
['deleteButton'],
['detail-click'],
Expand Down
Loading
Loading