diff --git a/.gitignore b/.gitignore index 1f232cc5..37606d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.bundle.* lib/ +!packages/react-components/lib/ node_modules/ *.log .eslintcache @@ -30,11 +31,10 @@ __pycache__/ .Python build/ develop-eggs/ -dist/ +**/dist/* downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ diff --git a/.prettierignore b/.prettierignore index 194eb186..c89a4c8f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -27,3 +27,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* tests-out/ + +!packages/react-components/lib \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f17da59..982bc01d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,6 +30,24 @@ cd packages/components yarn start ``` +### React components + +The react components wrapper are initialized using [custom-element-react-wrappers](https://github.com/break-stuff/cem-tools/tree/main/packages/react-wrappers). +The initialization can be reproduced by running in `packages/components` the script `yarn run build:react`. +Unfortunately the initial code needs lots of changes to be usable in this case: + +- Changing the typing to inherit from `React.AllHTMLAttributes` - minus some event handlers that needs to be overridden. +- Changing `useImperativeHandle` to expose the full HTMLElement using: + +```js +useImperativeHandle(forwardedRef, () => ref.current, [ref.current]); +``` + +- Remove internal fast-specific properties like `$presentation`, `styles` and `template`. +- The tag name, the properties and the events are extracted from the doc string of + the element class definition in `packages/components`. You may need to add some + new doc tags to get all the properties and events. + ### JupyterLab demo extension To test locally the JupyterLab demo extension, using `conda` package manager: diff --git a/packages/components/custom-elements-manifest.config.mjs b/packages/components/custom-elements-manifest.config.mjs new file mode 100644 index 00000000..3dcdce80 --- /dev/null +++ b/packages/components/custom-elements-manifest.config.mjs @@ -0,0 +1,43 @@ +/* global process */ +import { customElementReactWrapperPlugin } from 'custom-element-react-wrappers'; + +/** + * Custom element manifest plugin to remove the + * Jupyter prefix in the element class name to + * get a better naming for the generated react components. + */ +function renameClassElement() { + return { + name: 'rename-class-element-plugin', + packageLinkPhase({ customElementsManifest, context }) { + customElementsManifest.modules.forEach(module => { + module.declarations.forEach(declaration => { + // Remove the prefix `Jupyter` for simpler naming + if (declaration.tagName && declaration.name.startsWith('Jupyter')) { + declaration.name = declaration.name.slice(7); + } + }); + }); + } + }; +} + +const plugins = [renameClassElement()]; + +if (process.env.BUILD_REACT) { + plugins.push( + customElementReactWrapperPlugin({ + outdir: '../react-components/lib', + modulePath: (className, tagName) => '@jupyter/web-components' + }) + ); +} + +export default { + fast: true, + outdir: 'dist', + dependencies: true, + globs: ['src/**/index.ts'], + exclude: ['src/index.ts', 'src/styles', 'src/utilities'], + plugins +}; diff --git a/packages/components/package.json b/packages/components/package.json index 36a8453c..73e3c243 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -25,8 +25,9 @@ "scripts": { "start": "storybook dev -p 6006", "start:ci": "storybook dev -p 6006 --ci --quiet", - "build": "rollup -c && tsc -p ./tsconfig.json", + "build": "rollup -c && tsc -p ./tsconfig.json && custom-elements-manifest analyze", "build:docs": "storybook build", + "build:react": "BUILD_REACT=1 custom-elements-manifest analyze", "clean": "yarn clean:lib && yarn clean:test", "clean:lib": "rimraf dist", "clean:test": "rimraf test-results tests-out/**/*.js tests-out/**/*.js.map tsconfig.playwright.tsbuildinfo", @@ -49,6 +50,7 @@ "devDependencies": { "@babel/core": "^7.22.5", "@babel/preset-env": "^7.22.5", + "@custom-elements-manifest/analyzer": "^0.10.2", "@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/free-solid-svg-icons": "^5.15.4", "@microsoft/api-extractor": "^7.36.0", @@ -69,6 +71,7 @@ "@types/node": "^18.0.0", "@types/webpack-env": "^1.15.2", "@typescript-eslint/eslint-plugin": "^5.60.1", + "custom-element-react-wrappers": "^1.6.0", "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.25.2", @@ -98,5 +101,6 @@ }, "publishConfig": { "access": "public" - } + }, + "customElements": "dist/custom-elements.json" } diff --git a/packages/components/src/accordion-item/index.ts b/packages/components/src/accordion-item/index.ts index e3a5e51f..6e3a6337 100644 --- a/packages/components/src/accordion-item/index.ts +++ b/packages/components/src/accordion-item/index.ts @@ -9,6 +9,14 @@ import { } from '@microsoft/fast-foundation'; import { accordionItemStyles as styles } from './accordion-item.styles.js'; +/** + * Accordion item class + * + * @public + * @tagname jp-accordion-item + */ +class JupyterAccordionItem extends AccordionItem {} + /** * A function that returns a {@link @microsoft/fast-foundation#AccordionItem} registration for configuring the component with a DesignSystem. * Implements {@link @microsoft/fast-foundation#accordionItemTemplate} @@ -18,11 +26,13 @@ import { accordionItemStyles as styles } from './accordion-item.styles.js'; * @remarks * Generates HTML Element: `` */ -export const jpAccordionItem = AccordionItem.compose({ - baseName: 'accordion-item', - template, - styles, - collapsedIcon: /* html */ ` +export const jpAccordionItem = + JupyterAccordionItem.compose({ + baseName: 'accordion-item', + baseClass: AccordionItem, + template, + styles, + collapsedIcon: /* html */ ` ({ /> `, - expandedIcon: /* html */ ` + expandedIcon: /* html */ ` ({ /> ` -}); + }); -/** - * Base class for Accordion item - * @public - */ -export { AccordionItem }; +export { JupyterAccordionItem as AccordionItem }; export { styles as accordionItemStyles }; diff --git a/packages/components/src/accordion/index.ts b/packages/components/src/accordion/index.ts index 0c86cdbd..eefe526c 100644 --- a/packages/components/src/accordion/index.ts +++ b/packages/components/src/accordion/index.ts @@ -10,6 +10,14 @@ import { accordionStyles as styles } from './accordion.styles.js'; export * from '../accordion-item/index.js'; +/** + * Accordion class + * + * @public + * @tagname jp-accordion + */ +class JupyterAccordion extends Accordion {} + /** * A function that returns a {@link @microsoft/fast-foundation#Accordion} registration for configuring the component with a DesignSystem. * Implements {@link @microsoft/fast-foundation#accordionTemplate} @@ -19,16 +27,13 @@ export * from '../accordion-item/index.js'; * @remarks * Generates HTML Element: `` */ -export const jpAccordion = Accordion.compose({ +export const jpAccordion = JupyterAccordion.compose({ baseName: 'accordion', + baseClass: Accordion, template, styles }); -/** - * Base class for Accordion - * @public - */ -export { Accordion }; +export { JupyterAccordion as Accordion }; export { styles as accordionStyles }; diff --git a/packages/components/src/anchor/index.ts b/packages/components/src/anchor/index.ts index 7a02b78f..0156b549 100644 --- a/packages/components/src/anchor/index.ts +++ b/packages/components/src/anchor/index.ts @@ -3,10 +3,7 @@ // Distributed under the terms of the Modified BSD License. import { attr } from '@microsoft/fast-element'; -import { - Anchor as FoundationAnchor, - anchorTemplate as template -} from '@microsoft/fast-foundation'; +import { Anchor, anchorTemplate as template } from '@microsoft/fast-foundation'; import { ButtonAppearance } from '../button/index.js'; import { anchorStyles as styles } from './anchor.styles.js'; @@ -17,10 +14,11 @@ import { anchorStyles as styles } from './anchor.styles.js'; export type AnchorAppearance = ButtonAppearance | 'hypertext'; /** - * Base class for Anchor + * Anchor class * @public + * @tagname jp-anchor */ -export class Anchor extends FoundationAnchor { +class JupyterAnchor extends Anchor { /** * The appearance the anchor should have. * @@ -80,9 +78,9 @@ export class Anchor extends FoundationAnchor { * * {@link https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/delegatesFocus | delegatesFocus} */ -export const jpAnchor = Anchor.compose({ +export const jpAnchor = JupyterAnchor.compose({ baseName: 'anchor', - baseClass: FoundationAnchor, + baseClass: Anchor, template, styles, shadowOptions: { @@ -90,4 +88,6 @@ export const jpAnchor = Anchor.compose({ } }); +export { JupyterAnchor as Anchor }; + export { styles as anchorStyles }; diff --git a/packages/components/src/anchored-region/index.ts b/packages/components/src/anchored-region/index.ts index 78fad4d8..55f9af00 100644 --- a/packages/components/src/anchored-region/index.ts +++ b/packages/components/src/anchored-region/index.ts @@ -8,6 +8,14 @@ import { } from '@microsoft/fast-foundation'; import { anchoredRegionStyles as styles } from './anchored-region.styles.js'; +/** + * Anchored region class + * + * @public + * @tagname jp-anchored-region + */ +class JupyterAnchoredRegion extends AnchoredRegion {} + /** * A function that returns a {@link @microsoft/fast-foundation#AnchoredRegion} registration for configuring the component with a DesignSystem. * Implements {@link @microsoft/fast-foundation#anchoredRegionTemplate} @@ -17,16 +25,13 @@ import { anchoredRegionStyles as styles } from './anchored-region.styles.js'; * @remarks * Generates HTML Element: `` */ -export const jpAnchoredRegion = AnchoredRegion.compose({ +export const jpAnchoredRegion = JupyterAnchoredRegion.compose({ baseName: 'anchored-region', + baseClass: AnchoredRegion, template, styles }); -/** - * Base class for AnchoredRegion - * @public - */ -export { AnchoredRegion }; +export { JupyterAnchoredRegion as AnchoredRegion }; export { styles as anchoredRegionStyles }; diff --git a/packages/components/src/avatar/index.ts b/packages/components/src/avatar/index.ts index 47b4f6bd..50fe3784 100644 --- a/packages/components/src/avatar/index.ts +++ b/packages/components/src/avatar/index.ts @@ -5,17 +5,18 @@ import { attr, html, when } from '@microsoft/fast-element'; import { AvatarOptions, - Avatar as FoundationAvatar, + Avatar, avatarTemplate as template } from '@microsoft/fast-foundation'; import { avatarStyles as styles } from './avatar.styles.js'; /** - * The Jupyter Avatar Class - * @public + * Jupyter Avatar Class * + * @public + * @tagname jp-avatar */ -export class Avatar extends FoundationAvatar { +class JupyterAvatar extends Avatar { /** * Indicates the Avatar should have an image source * @@ -41,7 +42,7 @@ export class Avatar extends FoundationAvatar { * @public * */ -export const imgTemplate = html` +export const imgTemplate = html` ${when( x => x.imgSrc, html` @@ -65,9 +66,9 @@ export const imgTemplate = html` * @remarks * Generates HTML Element: `` */ -export const jpAvatar = Avatar.compose({ +export const jpAvatar = JupyterAvatar.compose({ baseName: 'avatar', - baseClass: FoundationAvatar, + baseClass: Avatar, template, styles, media: imgTemplate, @@ -76,4 +77,6 @@ export const jpAvatar = Avatar.compose({ } }); +export { JupyterAvatar as Avatar }; + export { styles as avatarStyles }; diff --git a/packages/components/src/badge/index.ts b/packages/components/src/badge/index.ts index dd8f306b..371bf9b7 100644 --- a/packages/components/src/badge/index.ts +++ b/packages/components/src/badge/index.ts @@ -5,6 +5,14 @@ import { Badge, badgeTemplate as template } from '@microsoft/fast-foundation'; import { badgeStyles as styles } from './badge.styles.js'; +/** + * Badge class + * + * @public + * @tagname jp-badge + */ +class JupyterBadge extends Badge {} + /** * A function that returns a {@link @microsoft/fast-foundation#Badge} registration for configuring the component with a DesignSystem. * Implements {@link @microsoft/fast-foundation#badgeTemplate} @@ -14,16 +22,13 @@ import { badgeStyles as styles } from './badge.styles.js'; * @remarks * Generates HTML Element: `` */ -export const jpBadge = Badge.compose({ +export const jpBadge = JupyterBadge.compose({ baseName: 'badge', + baseClass: Badge, template, styles }); -/** - * Base class for Badge - * @public - */ -export { Badge }; +export { JupyterBadge as Badge }; export { styles as badgeStyles }; diff --git a/packages/components/src/breadcrumb-item/index.ts b/packages/components/src/breadcrumb-item/index.ts index 94d6790c..bb95c23c 100644 --- a/packages/components/src/breadcrumb-item/index.ts +++ b/packages/components/src/breadcrumb-item/index.ts @@ -9,6 +9,14 @@ import { } from '@microsoft/fast-foundation'; import { breadcrumbItemStyles as styles } from './breadcrumb-item.styles.js'; +/** + * Breadcrumb item class + * + * @public + * @tagname jp-breadcrumb-item + */ +class JupyterBreadcrumbItem extends BreadcrumbItem {} + /** * A function that returns a {@link @microsoft/fast-foundation#BreadcrumbItem} registration for configuring the component with a DesignSystem. * Implements {@link @microsoft/fast-foundation#breadcrumbItemTemplate} @@ -18,20 +26,18 @@ import { breadcrumbItemStyles as styles } from './breadcrumb-item.styles.js'; * @remarks * Generates HTML Element: `` */ -export const jpBreadcrumbItem = BreadcrumbItem.compose({ - baseName: 'breadcrumb-item', - template, - styles, - separator: '/', - shadowOptions: { - delegatesFocus: true - } -}); +export const jpBreadcrumbItem = + JupyterBreadcrumbItem.compose({ + baseName: 'breadcrumb-item', + baseClass: BreadcrumbItem, + template, + styles, + separator: '/', + shadowOptions: { + delegatesFocus: true + } + }); -/** - * Base class for BreadcrumbItem - * @public - */ -export { BreadcrumbItem }; +export { JupyterBreadcrumbItem as BreadcrumbItem }; export { styles as breadcrumbItemStyles }; diff --git a/packages/components/src/breadcrumb/index.ts b/packages/components/src/breadcrumb/index.ts index 0ec60bb7..f086ab1f 100644 --- a/packages/components/src/breadcrumb/index.ts +++ b/packages/components/src/breadcrumb/index.ts @@ -8,6 +8,14 @@ import { } from '@microsoft/fast-foundation'; import { breadcrumbStyles as styles } from './breadcrumb.styles.js'; +/** + * Breadcrumb class + * + * @public + * @tagname jp-breadcrumb + */ +class JupyterBreadcrumb extends Breadcrumb {} + /** * A function that returns a {@link @microsoft/fast-foundation#Breadcrumb} registration for configuring the component with a DesignSystem. * Implements {@link @microsoft/fast-foundation#breadcrumbTemplate} @@ -17,16 +25,13 @@ import { breadcrumbStyles as styles } from './breadcrumb.styles.js'; * @remarks * Generates HTML Element: `` */ -export const jpBreadcrumb = Breadcrumb.compose({ +export const jpBreadcrumb = JupyterBreadcrumb.compose({ baseName: 'breadcrumb', + baseClass: Breadcrumb, template, styles }); -/** - * Base class for Breadcrumb - * @public - */ -export { Breadcrumb }; +export { JupyterBreadcrumb as Breadcrumb }; export { styles as breadcrumbStyles }; diff --git a/packages/components/src/button/index.ts b/packages/components/src/button/index.ts index be74bcd4..ff0d4ab3 100644 --- a/packages/components/src/button/index.ts +++ b/packages/components/src/button/index.ts @@ -3,10 +3,7 @@ // Distributed under the terms of the Modified BSD License. import { attr } from '@microsoft/fast-element'; -import { - Button as FoundationButton, - buttonTemplate as template -} from '@microsoft/fast-foundation'; +import { Button, buttonTemplate as template } from '@microsoft/fast-foundation'; import { buttonStyles as styles } from './button.styles.js'; /** @@ -22,9 +19,12 @@ export type ButtonAppearance = | 'stealth'; /** - * @internal + * Button class + * + * @public + * @tagname jp-button */ -export class Button extends FoundationButton { +class JupyterButton extends Button { /** * The appearance the button should have. * @@ -82,9 +82,9 @@ export class Button extends FoundationButton { * * {@link https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/delegatesFocus | delegatesFocus} */ -export const jpButton = Button.compose({ +export const jpButton = JupyterButton.compose({ baseName: 'button', - baseClass: FoundationButton, + baseClass: Button, template, styles, shadowOptions: { @@ -92,4 +92,6 @@ export const jpButton = Button.compose({ } }); +export { JupyterButton as Button }; + export { styles as buttonStyles }; diff --git a/packages/components/src/card/index.ts b/packages/components/src/card/index.ts index 5cc3908e..c0202445 100644 --- a/packages/components/src/card/index.ts +++ b/packages/components/src/card/index.ts @@ -4,7 +4,7 @@ import { composedParent, - Card as FoundationCard, + Card, cardTemplate as template } from '@microsoft/fast-foundation'; import { Swatch } from '../color/swatch.js'; @@ -12,9 +12,12 @@ import { fillColor, neutralFillLayerRecipe } from '../design-tokens.js'; import { cardStyles as styles } from './card.styles.js'; /** - * @internal + * Card class + * + * @public + * @tagname jp-card */ -export class Card extends FoundationCard { +class JupyterCard extends Card { connectedCallback() { super.connectedCallback(); @@ -41,11 +44,13 @@ export class Card extends FoundationCard { * @remarks * Generates HTML Element: `` */ -export const jpCard = Card.compose({ +export const jpCard = JupyterCard.compose({ baseName: 'card', - baseClass: FoundationCard, + baseClass: Card, template, styles }); +export { JupyterCard as Card }; + export { styles as cardStyles }; diff --git a/packages/components/src/checkbox/index.ts b/packages/components/src/checkbox/index.ts index f4963bc0..bcafe3fd 100644 --- a/packages/components/src/checkbox/index.ts +++ b/packages/components/src/checkbox/index.ts @@ -3,7 +3,7 @@ // Distributed under the terms of the Modified BSD License. import { - Checkbox as BaseCheckbox, + Checkbox, CheckboxOptions, FoundationElementTemplate } from '@microsoft/fast-foundation'; @@ -15,7 +15,7 @@ import { ViewTemplate, html, slotted } from '@microsoft/fast-element'; * @public */ export const checkboxTemplate: FoundationElementTemplate< - ViewTemplate, + ViewTemplate, CheckboxOptions > = (context, definition) => html`