Skip to content

refactor: reuse UI5 Event Details instead of generating them #4652

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 6 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
117 changes: 54 additions & 63 deletions packages/main/scripts/create-web-components-wrapper.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import fioriWebComponentsSpec from '@ui5/webcomponents-fiori/dist/api.json' assert { type: 'json' };
import mainWebComponentsSpec from '@ui5/webcomponents/dist/api.json' assert { type: 'json' };
import dedent from 'dedent';
import fs from 'node:fs';
import { spawnSync } from 'node:child_process';
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import prettier from 'prettier';
import PATHS from '../../../config/paths.js';
Expand Down Expand Up @@ -199,7 +200,7 @@ const allWebComponents = [
return true;
});

fs.writeFileSync(
writeFileSync(
path.join(PATHS.root, 'scripts', 'web-component-wrappers', 'interfaces.json'),
JSON.stringify(Array.from(interfaces))
);
Expand Down Expand Up @@ -234,34 +235,45 @@ const replaceTagNameWithModuleName = (description) => {
return parsedDescription;
};

const getEventParameters = (name, parameters) => {
const resolvedEventParameters = parameters.map((property) => {
return {
...property,
...Utils.getTypeDefinitionForProperty(property, { event: true })
};
});
const getEventParameters = (moduleName, eventSpec) => {
const eventTarget = `${moduleName}DomRef`;
if (eventSpec.native === 'true') {
if (eventSpec.name === 'click') {
return {
tsType: `MouseEventHandler<${eventTarget}>`,
importStatements: ["import { MouseEventHandler } from 'react';"]
};
} else if (eventSpec.name === 'drop') {
return {
tsType: `DragEventHandler<${eventTarget}>`,
importStatements: ["import { DragEventHandler } from 'react';"]
};
} else {
console.warn(
`----------------------\n${moduleName}: ${eventSpec.name} event didn't receive its type, please add it to the script! \n----------------------`
);
}
}

const importStatements = [`import type { Ui5CustomEvent } from '../../interfaces/index.js';`];
const importSpecifier = `@ui5/webcomponents${
componentsFromFioriPackage.has(moduleName) ? '-fiori' : ''
}/dist/${moduleName}.js`;

const eventTarget = `${name}DomRef`;
const eventName = `${moduleName}${Utils.capitalizeFirstLetter(Utils.snakeToCamel(eventSpec.name))}EventDetail`;

if (resolvedEventParameters.length === 0) {
const importStatements = [`import type { Ui5CustomEvent } from '../../interfaces/index.js';`];

if ((eventSpec.parameters ?? []).length === 0) {
return {
tsType: `(event: Ui5CustomEvent<${eventTarget}>) => void`,
importStatements
};
}

const detailPayload = resolvedEventParameters.map((parameter) => {
if (parameter.importStatement) {
importStatements.push(parameter.importStatement);
}
return `${parameter.name}: ${parameter.tsType}`;
});
importStatements.unshift(`import type { ${eventName} } from '${importSpecifier}';`);

return {
tsType: `(event: Ui5CustomEvent<${eventTarget}, {${detailPayload.join('; ')}}>) => void`,
tsType: `(event: Ui5CustomEvent<${eventTarget}, ${eventName}>) => void`,
importStatements
};
};
Expand Down Expand Up @@ -442,7 +454,7 @@ const resolveInheritedAttributes = (componentSpec) => {

`;

fs.writeFileSync(path.join(ENUMS_DIR, `${spec.basename}.ts`), prettier.format(template, Utils.prettierConfig));
writeFileSync(path.join(ENUMS_DIR, `${spec.basename}.ts`), prettier.format(template, Utils.prettierConfig));
});

const propDescription = (componentSpec, property) => {
Expand Down Expand Up @@ -549,27 +561,7 @@ allWebComponents
(componentSpec.events || [])
.filter((eventSpec) => eventSpec.visibility === 'public')
.forEach((eventSpec) => {
let eventParameters;
if (eventSpec.native === 'true') {
const eventTarget = `${componentSpec.module}DomRef`;
if (eventSpec.name === 'click') {
eventParameters = {
tsType: `MouseEventHandler<${eventTarget}>`,
importStatements: ["import { MouseEventHandler } from 'react';"]
};
} else if (eventSpec.name === 'drop') {
eventParameters = {
tsType: `DragEventHandler<${eventTarget}>`,
importStatements: ["import { DragEventHandler } from 'react';"]
};
} else {
console.warn(
`----------------------\n${componentSpec.module}: ${eventSpec.name} event didn't receive its type, please add it to the script! \n----------------------`
);
}
} else {
eventParameters = getEventParameters(componentSpec.module, eventSpec.parameters || []);
}
const eventParameters = getEventParameters(componentSpec.module, eventSpec);
importStatements.push(...eventParameters.importStatements);
let onChangeDescription;
if (INPUT_COMPONENTS.has(componentSpec.module) && eventSpec.name === 'change') {
Expand Down Expand Up @@ -616,14 +608,14 @@ allWebComponents

// check if folder exists and create it if necessary
const webComponentFolderPath = path.join(WEB_COMPONENTS_ROOT_DIR, componentSpec.module);
if (!fs.existsSync(webComponentFolderPath)) {
fs.mkdirSync(webComponentFolderPath);
if (!existsSync(webComponentFolderPath)) {
mkdirSync(webComponentFolderPath);
}

// create empty index file for eslint
const webComponentWrapperPath = path.join(webComponentFolderPath, 'index.tsx');
if (!fs.existsSync(webComponentWrapperPath)) {
fs.writeFileSync(webComponentWrapperPath, '');
if (!existsSync(webComponentWrapperPath)) {
writeFileSync(webComponentWrapperPath, '');
}

// fill index
Expand All @@ -650,12 +642,12 @@ allWebComponents
(componentSpec.slots || []).filter(filterNonPublicAttributes).map(({ name }) => name),
(componentSpec.events || []).filter(filterNonPublicAttributes).map(({ name }) => name)
);
fs.writeFileSync(webComponentWrapperPath, webComponentWrapper);
writeFileSync(webComponentWrapperPath, webComponentWrapper);

// create test
if (!fs.existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.cy.tsx`))) {
if (!existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.cy.tsx`))) {
const webComponentTest = renderTest({ name: componentSpec.module, tagname: componentSpec.tagname });
fs.writeFileSync(path.join(webComponentFolderPath, `${componentSpec.module}.cy.tsx`), webComponentTest);
writeFileSync(path.join(webComponentFolderPath, `${componentSpec.module}.cy.tsx`), webComponentTest);
}

// create demo
Expand All @@ -670,7 +662,7 @@ allWebComponents
componentSpec,
false
)}\n${formatDemoDescription(description, componentSpec, false)}`;
fs.writeFileSync(
writeFileSync(
path.join(webComponentFolderPath, `${componentSpec.module}Description.md`),
subComponentDescription
);
Expand All @@ -679,7 +671,7 @@ allWebComponents
const formattedDescription = formatDemoDescription(description, componentSpec);
// create component description
if (formattedDescription) {
fs.writeFileSync(
writeFileSync(
path.join(webComponentFolderPath, `${componentSpec.module}Description.md`),
formattedDescription
);
Expand All @@ -692,22 +684,20 @@ allWebComponents
];

if (publicProperties.length) {
fs.writeFileSync(
writeFileSync(
path.join(webComponentFolderPath, `${componentSpec.module}DomRef.json`),
prettier.format(JSON.stringify(publicProperties), {
...Utils.prettierConfig,
parser: 'json'
})
);
let hasMethodsTable = false;
if (fs.existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.mdx`))) {
hasMethodsTable = fs
.readFileSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.mdx`))
if (existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.mdx`))) {
hasMethodsTable = readFileSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.mdx`))
.toString()
.includes(`<DomRefTable rows={${componentSpec.module}DomRef.json} />`);
} else if (fs.existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.mdx`))) {
hasMethodsTable = fs
.readFileSync(path.join(webComponentFolderPath, `${componentSpec.module}.mdx`))
} else if (existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.mdx`))) {
hasMethodsTable = readFileSync(path.join(webComponentFolderPath, `${componentSpec.module}.mdx`))
.toString()
.includes(`<DomRefTable rows={${componentSpec.module}DomRef.json} />`);
}
Expand All @@ -721,8 +711,8 @@ allWebComponents

if (
CREATE_SINGLE_COMPONENT === componentSpec.module ||
(!fs.existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.mdx`)) &&
!fs.existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.tsx`)))
(!existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.mdx`)) &&
!existsSync(path.join(webComponentFolderPath, `${componentSpec.module}.stories.tsx`)))
) {
await createStory(componentSpec, allComponentProperties);
await createDocumentation(componentSpec, allComponentProperties, description);
Expand All @@ -732,11 +722,12 @@ allWebComponents
});

// create index file for exporting all web components
fs.writeFileSync(
writeFileSync(
path.join(WEB_COMPONENTS_ROOT_DIR, 'index.ts'),
fs
.readdirSync(WEB_COMPONENTS_ROOT_DIR)
.filter((f) => fs.statSync(path.join(WEB_COMPONENTS_ROOT_DIR, f)).isDirectory())
readdirSync(WEB_COMPONENTS_ROOT_DIR)
.filter((f) => statSync(path.join(WEB_COMPONENTS_ROOT_DIR, f)).isDirectory())
.map((folder) => `export * from './${folder}/index.js';`)
.join('\n')
);

spawnSync('prettier', [WEB_COMPONENTS_ROOT_DIR, '--write'], { stdio: [0, 1, 2] });
7 changes: 3 additions & 4 deletions packages/main/src/webComponents/AvatarGroup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';

import '@ui5/webcomponents/dist/AvatarGroup.js';
import type { AvatarGroupClickEventDetail } from '@ui5/webcomponents/dist/AvatarGroup.js';
import type { ReactNode } from 'react';
import type { AvatarColorScheme } from '../../enums/index.js';
import { AvatarGroupType } from '../../enums/index.js';
import type { AvatarColorScheme } from '../../enums/index.js';
import type { Ui5CustomEvent, CommonProps, Ui5DomRef } from '../../interfaces/index.js';
import { withWebComponent } from '../../internal/withWebComponent.js';
import type { UI5WCSlotsNode } from '../../types/index.js';
Expand Down Expand Up @@ -53,9 +54,7 @@ export interface AvatarGroupPropTypes extends AvatarGroupAttributes, Omit<Common
/**
* Fired when the component is activated either with a click/tap or by using the Enter or Space key.
*/
onClick?: (
event: Ui5CustomEvent<AvatarGroupDomRef, { targetRef: HTMLElement; overflowButtonClicked: boolean }>
) => void;
onClick?: (event: Ui5CustomEvent<AvatarGroupDomRef, AvatarGroupClickEventDetail>) => void;
/**
* Fired when the count of visible `Avatar` elements in the component has changed
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
'use client';

import '@ui5/webcomponents-fiori/dist/BarcodeScannerDialog.js';
import type {
BarcodeScannerDialogScanErrorEventDetail,
BarcodeScannerDialogScanSuccessEventDetail
} from '@ui5/webcomponents-fiori/dist/BarcodeScannerDialog.js';
import type { Ui5CustomEvent, CommonProps, Ui5DomRef } from '../../interfaces/index.js';
import { withWebComponent } from '../../internal/withWebComponent.js';

Expand All @@ -21,12 +25,12 @@ export interface BarcodeScannerDialogPropTypes extends BarcodeScannerDialogAttri
/**
* Fires when the scan fails with error.
*/
onScanError?: (event: Ui5CustomEvent<BarcodeScannerDialogDomRef, { message: string }>) => void;
onScanError?: (event: Ui5CustomEvent<BarcodeScannerDialogDomRef, BarcodeScannerDialogScanErrorEventDetail>) => void;
/**
* Fires when the scan is completed successfuuly.
*/
onScanSuccess?: (
event: Ui5CustomEvent<BarcodeScannerDialogDomRef, { text: string; rawBytes: Record<string, unknown> }>
event: Ui5CustomEvent<BarcodeScannerDialogDomRef, BarcodeScannerDialogScanSuccessEventDetail>
) => void;
}

Expand Down
8 changes: 2 additions & 6 deletions packages/main/src/webComponents/Breadcrumbs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import '@ui5/webcomponents/dist/Breadcrumbs.js';
import type { BreadcrumbsItemClickEventDetail } from '@ui5/webcomponents/dist/Breadcrumbs.js';
import type { ReactNode } from 'react';
import { BreadcrumbsDesign, BreadcrumbsSeparatorStyle } from '../../enums/index.js';
import type { Ui5CustomEvent, CommonProps, Ui5DomRef } from '../../interfaces/index.js';
Expand Down Expand Up @@ -40,12 +41,7 @@ export interface BreadcrumbsPropTypes extends BreadcrumbsAttributes, CommonProps
/**
* Fires when a `BreadcrumbsItem` is clicked. **Note:** You can prevent browser location change by calling `event.preventDefault()`.
*/
onItemClick?: (
event: Ui5CustomEvent<
BreadcrumbsDomRef,
{ item: HTMLElement; altKey: boolean; ctrlKey: boolean; metaKey: boolean; shiftKey: boolean }
>
) => void;
onItemClick?: (event: Ui5CustomEvent<BreadcrumbsDomRef, BreadcrumbsItemClickEventDetail>) => void;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/webComponents/Calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import '@ui5/webcomponents/dist/Calendar.js';
import type { CalendarSelectedDatesChangeEventDetail } from '@ui5/webcomponents/dist/Calendar.js';
import type { ReactNode } from 'react';
import type { CalendarType } from '../../enums/index.js';
import { CalendarSelectionMode } from '../../enums/index.js';
Expand Down Expand Up @@ -54,7 +55,7 @@ export interface CalendarPropTypes extends CalendarAttributes, CommonProps {
/**
* Fired when the selected dates change. **Note:** If you call `preventDefault()` for this event, the component will not create instances of `CalendarDate` for the newly selected dates. In that case you should do this manually.
*/
onSelectedDatesChange?: (event: Ui5CustomEvent<CalendarDomRef, { values: unknown[]; dates: unknown[] }>) => void;
onSelectedDatesChange?: (event: Ui5CustomEvent<CalendarDomRef, CalendarSelectedDatesChangeEventDetail>) => void;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/webComponents/Carousel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import '@ui5/webcomponents/dist/Carousel.js';
import type { CarouselNavigateEventDetail } from '@ui5/webcomponents/dist/Carousel.js';
import type { ReactNode } from 'react';
import {
CarouselArrowsPlacement,
Expand Down Expand Up @@ -88,7 +89,7 @@ export interface CarouselPropTypes extends CarouselAttributes, CommonProps {
/**
* Fired whenever the page changes due to user interaction, when the user clicks on the navigation arrows or while resizing, based on the `items-per-page-l`, `items-per-page-m` and `items-per-page-s` properties.
*/
onNavigate?: (event: Ui5CustomEvent<CarouselDomRef, { selectedIndex: number }>) => void;
onNavigate?: (event: Ui5CustomEvent<CarouselDomRef, CarouselNavigateEventDetail>) => void;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/webComponents/ColorPalette/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import '@ui5/webcomponents/dist/ColorPalette.js';
import type { ColorPaletteItemClickEventDetail } from '@ui5/webcomponents/dist/ColorPalette.js';
import type { ReactNode } from 'react';
import type { Ui5CustomEvent, CommonProps, Ui5DomRef } from '../../interfaces/index.js';
import { withWebComponent } from '../../internal/withWebComponent.js';
Expand All @@ -17,7 +18,7 @@ export interface ColorPalettePropTypes extends ColorPaletteAttributes, CommonPro
/**
* Fired when the user selects a color.
*/
onItemClick?: (event: Ui5CustomEvent<ColorPaletteDomRef, { color: string }>) => void;
onItemClick?: (event: Ui5CustomEvent<ColorPaletteDomRef, ColorPaletteItemClickEventDetail>) => void;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import '@ui5/webcomponents/dist/ColorPalettePopover.js';
import type { ColorPalettePopoverItemClickEventDetail } from '@ui5/webcomponents/dist/ColorPalettePopover.js';
import type { CSSProperties, ReactNode } from 'react';
import type { Ui5CustomEvent, CommonProps, Ui5DomRef } from '../../interfaces/index.js';
import { withWebComponent } from '../../internal/withWebComponent.js';
Expand Down Expand Up @@ -46,7 +47,7 @@ export interface ColorPalettePopoverPropTypes extends ColorPalettePopoverAttribu
/**
* Fired when the user selects a color.
*/
onItemClick?: (event: Ui5CustomEvent<ColorPalettePopoverDomRef, { color: string }>) => void;
onItemClick?: (event: Ui5CustomEvent<ColorPalettePopoverDomRef, ColorPalettePopoverItemClickEventDetail>) => void;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/webComponents/ComboBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import '@ui5/webcomponents/dist/ComboBox.js';
import type { ComboBoxSelectionChangeEventDetail } from '@ui5/webcomponents/dist/ComboBox.js';
import type { ReactNode } from 'react';
import { ComboBoxFilter, ValueState } from '../../enums/index.js';
import type { Ui5CustomEvent, CommonProps, Ui5DomRef } from '../../interfaces/index.js';
Expand Down Expand Up @@ -107,7 +108,7 @@ export interface ComboBoxPropTypes extends ComboBoxAttributes, Omit<CommonProps,
/**
* Fired when selection is changed by user interaction
*/
onSelectionChange?: (event: Ui5CustomEvent<ComboBoxDomRef, { item: HTMLElement }>) => void;
onSelectionChange?: (event: Ui5CustomEvent<ComboBoxDomRef, ComboBoxSelectionChangeEventDetail>) => void;
}

/**
Expand Down
Loading