diff --git a/web-components/packages/carbon-web-components/.eslintrc.js b/web-components/packages/carbon-web-components/.eslintrc.js
index 3ab5cc35c399..0e667c2a58b0 100644
--- a/web-components/packages/carbon-web-components/.eslintrc.js
+++ b/web-components/packages/carbon-web-components/.eslintrc.js
@@ -57,56 +57,12 @@ module.exports = {
'no-undef': 0,
},
},
- {
- files: ['**/*.tsx', '**/components-react/**/*-container.ts'],
- parser: '@typescript-eslint/parser',
- plugins: ['@typescript-eslint', 'react'],
- rules: {
- 'no-unused-vars': 0,
- // TODO: See why the ESLint plugin does not work with `.tsx`
- '@carbon/react-prop-type-comments/require-proptype-comment': 0,
- '@typescript-eslint/no-unused-vars': 2,
- // 'import/no-unresolved': [
- // 2,
- // {
- // ignore: ['^./'],
- // },
- // ],
- 'jsdoc/require-param-type': 0,
- 'jsdoc/require-returns-type': 0,
- 'react/jsx-uses-react': 2,
- 'react/jsx-uses-vars': 2,
- },
- },
{
files: ['**/defs.ts'],
rules: {
'import/prefer-default-export': 0,
},
},
- {
- files: ['**/*.stories.react.tsx'],
- parser: '@typescript-eslint/parser',
- plugins: ['@typescript-eslint', 'react'],
- rules: {
- 'no-unused-vars': 0,
- '@typescript-eslint/no-unused-vars': 2,
- // 'import/no-extraneous-dependencies': 0,
- // 'import/no-unresolved': [
- // 2,
- // {
- // ignore: [
- // '^carbon-web-components/es/(components-react|icons)/',
- // '^@carbon/ibmdotcom-web-components/es/(components-react|icons)/',
- // '/components-react/',
- // ],
- // },
- // ],
- 'react/jsx-uses-react': 2,
- 'react/jsx-uses-vars': 2,
- 'react/prop-types': 0,
- },
- },
{
files: ['examples/codesandbox/**/*.js', 'examples/codesandbox/**/*.ts'],
parserOptions: {
@@ -128,14 +84,6 @@ module.exports = {
// 'import/no-unresolved': 0,
},
},
- {
- files: ['examples/codesandbox/{react,form/redux-form}/**/*.js'],
- plugins: ['react'],
- rules: {
- 'react/jsx-uses-react': 2,
- 'react/jsx-uses-vars': 2,
- },
- },
{
files: [
'examples/codesandbox/**/*.config.js',
diff --git a/web-components/packages/carbon-web-components/README.md b/web-components/packages/carbon-web-components/README.md
index 78f8566288f8..151b75269c8e 100644
--- a/web-components/packages/carbon-web-components/README.md
+++ b/web-components/packages/carbon-web-components/README.md
@@ -260,15 +260,9 @@ same manner as native HTML tags, for example:
## JavaScript framework support
-In addition to the available Web Component versions of Carbon components, this
-library also supports usage with JavaScript frameworks like Angular, React, and
-Vue if the desire is to use instead of the pure framework versions of Carbon
-components. Specifically for React, this library comes with a wrapper
-implementation around the Carbon Web Components for more seamless integration
-with your React application.
-
-This is achievable since Web Components is the modern browser standard, and
-works well with other front-end frameworks that exist in the application. In
+This package can also be used within other JavaScript frameworks such as Angular and
+Vue. This is achievable since web components are the modern browser standard, and
+work well with other front-end frameworks that exist in the application. In
turn, this also comes with the benefits of encapsulation within the Shadow DOM:
### Angular
@@ -306,48 +300,6 @@ application can use those `.d.ts` files:
as follows:
`window.__importDefault = mod => (mod?.__esModule ? mod : { default: mod })`
-### React
-
-[](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/feat/cwc-v2/packages/carbon-web-components/examples/codesandbox/react)
-
-You can use wrapper React components in
-`@carbon/web-components/es/components-react` generated
-[automatically from the custom elements](./src/globals/wrappers/createReactCustomElementType.ts)
-which allows you to use our components seamlessly in your React code. Here's an
-example:
-
-```javascript
-import React from 'react';
-import { render } from 'react-dom';
-import CDSDropdown from '@carbon/web-components/es/components-react/dropdown/dropdown.js';
-import CDSDropdownItem from '@carbon/web-components/es/components-react/dropdown/dropdown-item.js';
-
-const App = () => (
-
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
-
-);
-
-render(, document.getElementById('root'));
-```
-
-Note: Using the React wrapper requires an additional dependency,
-[`prop-types`](https://www.npmjs.com/package/prop-types).
-
-To run the wrapper React components in SSR environment requires Node `12.16.3`
-or above that supports
-["conditional mapping" feature](https://github.com/jkrems/proposal-pkg-exports#2-conditional-mapping):
-
-[](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/feat/cwc-v2/packages/carbon-web-components/examples/codesandbox/react-ssr)
-
-Same Node version requirement applies to Next.js:
-
-[](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/feat/cwc-v2/packages/carbon-web-components/examples/codesandbox/next)
-
### Vue
[](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/feat/cwc-v2/packages/carbon-web-components/examples/codesandbox/vue)
diff --git a/web-components/packages/carbon-web-components/docs/form.md b/web-components/packages/carbon-web-components/docs/form.md
index 20c83256458d..c016bab86e86 100644
--- a/web-components/packages/carbon-web-components/docs/form.md
+++ b/web-components/packages/carbon-web-components/docs/form.md
@@ -26,38 +26,3 @@ button.addEventListener('click', () => {
```
[](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/feat/cwc-v2/packages/carbon-web-components/examples/codesandbox/form/basic)
-
-## Redux Form
-
-You can use our form components with Redux Form by creating a React component that wraps our form components:
-
-```javascript
-import { Field } from 'redux-form';
-import CDSFormItem from '@carbon/web-components/es/components-react/form/form-item';
-import CDSTextInput from '@carbon/web-components/es/components-react/text-input/text-input';
-
-...
-
-// A React component that wraps form components from `@carbon/web-components`
-const FieldImpl = ({ input, label, type, meta: { touched, error } }) => {
- const validityMessage = !touched ? undefined : error;
- return (
-
-
-
- );
-};
-
-...
-
-
-```
-
-[](https://codesandbox.io/s/github/carbon-design-system/carbon-web-components/tree/feat/cwc-v2/examples/codesandbox/form/redux-form)
diff --git a/web-components/packages/carbon-web-components/docs/form.mdx b/web-components/packages/carbon-web-components/docs/form.mdx
index 350b626cbaea..900a89bd375a 100644
--- a/web-components/packages/carbon-web-components/docs/form.mdx
+++ b/web-components/packages/carbon-web-components/docs/form.mdx
@@ -34,7 +34,3 @@ button.addEventListener('click', () => {
style={{ width: '100%', height: '500px', border: 'solid rgba(0,0,0,0.1) 1px', boxShadow: 'rgba(0,0,0,0.1) 0 1px 3px 0' }}
title="carbon-web-components-getting-started"
sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin">
-
-## Framework-specific approaches of form participation
-
-- [Redux form](https://web-components.carbondesignsystem.com/react/?path=/story/introduction-form-paticipation--page)
diff --git a/web-components/packages/carbon-web-components/docs/welcome.mdx b/web-components/packages/carbon-web-components/docs/welcome.mdx
index 58b43b75fade..6168812ee7b8 100644
--- a/web-components/packages/carbon-web-components/docs/welcome.mdx
+++ b/web-components/packages/carbon-web-components/docs/welcome.mdx
@@ -111,16 +111,10 @@ An alternative to using a bundler are CDN artifacts which can be added client si
### JavaScript framework integration
-In addition to the available Web Component versions of Carbon components, this
-library also supports usage with JavaScript frameworks like Angular, React,
-and Vue if the desire is to use instead of the pure framework versions of
-Carbon components. Specifically for React, this library comes with a wrapper
-implementation around the Carbon Web Components for more seamless integration
-with your React application.
-
-This is achievable since Web Components is the modern browser standard, and
-works well with other front-end frameworks that exist in the application. In
-turn, this also comes with the benefits of encapsulation within the Shadow DOM.
+This package can also be used within other JavaScript frameworks such as Angular and
+Vue. This is achievable since web components are the modern browser standard, and
+work well with other front-end frameworks that exist in the application. In
+turn, this also comes with the benefits of encapsulation within the Shadow DOM:
### Other usage guides
diff --git a/web-components/packages/carbon-web-components/gulp-tasks/build/modules.js b/web-components/packages/carbon-web-components/gulp-tasks/build/modules.js
index 6ac7898d2b0e..950ae30798ad 100644
--- a/web-components/packages/carbon-web-components/gulp-tasks/build/modules.js
+++ b/web-components/packages/carbon-web-components/gulp-tasks/build/modules.js
@@ -11,9 +11,6 @@ const gulp = require('gulp');
require('./modules/css');
require('./modules/icon-types');
require('./modules/icons');
-require('./modules/react');
-require('./modules/react-defs');
-require('./modules/react-types');
require('./modules/scripts');
require('./modules/scripts-node');
require('./modules/types');
@@ -24,9 +21,6 @@ gulp.task(
gulp.task('build:modules:css'),
gulp.task('build:modules:icon-types'),
gulp.task('build:modules:icons'),
- gulp.task('build:modules:react'),
- gulp.task('build:modules:react-defs'),
- gulp.task('build:modules:react-types'),
gulp.task('build:modules:scripts'),
gulp.task('build:modules:scripts-node'),
gulp.task('build:modules:types')
diff --git a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react-defs.js b/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react-defs.js
deleted file mode 100644
index 8ff8dfccee21..000000000000
--- a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react-defs.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2022
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const asyncDone = require('async-done');
-const babel = require('gulp-babel');
-const gulp = require('gulp');
-const header = require('gulp-header');
-const path = require('path');
-const prettier = require('gulp-prettier');
-const replaceExtension = require('replace-ext');
-const through2 = require('through2');
-const { readFile } = require('fs');
-const { promisify } = require('util');
-
-const config = require('../../config');
-
-const readFileAsync = promisify(readFile);
-const promisifyStream = promisify(asyncDone);
-
-/**
- * Builds enums for React.
- *
- * @param {object} options The build options.
- * @param {string} options.banner The banner content.
- * @param {string} [options.targetEnv=browser] The target environment.
- * @private
- */
-const buildModulesReactDefs = ({ banner, targetEnv = 'browser' }) => {
- const destDir = {
- browser: `${config.jsDestDir}/components-react`,
- node: `${config.cjsDestDir}/components-react-node`,
- }[targetEnv];
-
- const componentDestDir = {
- browser: `${config.jsDestDir}/components`,
- node: `${config.cjsDestDir}/components`,
- }[targetEnv];
-
- let stream = gulp.src([`${config.srcDir}/components/**/defs.ts`]).pipe(
- through2.obj((file, enc, done) => {
- const importSource = replaceExtension(
- path.relative(
- path.dirname(path.resolve(__dirname, '..', destDir, file.relative)),
- path.resolve(__dirname, '..', componentDestDir, file.relative)
- ),
- '.js'
- );
- file.contents = Buffer.from(`export * from ${JSON.stringify(importSource)}`);
- file.path = replaceExtension(file.path, '.js');
- done(null, file);
- })
- );
-
- if (targetEnv === 'node') {
- stream = stream.pipe(
- babel({
- babelrc: false,
- plugins: ['@babel/plugin-transform-modules-commonjs'],
- })
- );
- }
-
- return stream.pipe(prettier()).pipe(header(banner)).pipe(gulp.dest(destDir));
-};
-
-/**
- * Builds the React defs
- *
- * @returns {Promise} Gulp stream
- */
-async function reactDefs() {
- const banner = await readFileAsync(path.resolve(__dirname, '../../../tools/license.js'), 'utf8');
- await Promise.all([
- promisifyStream(() => buildModulesReactDefs({ banner })),
- promisifyStream(() => buildModulesReactDefs({ banner, targetEnv: 'node' })),
- ]);
-}
-
-gulp.task('build:modules:react-defs', reactDefs);
diff --git a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react-types.js b/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react-types.js
deleted file mode 100644
index 3d173c32ff47..000000000000
--- a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react-types.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const asyncDone = require('async-done');
-const babel = require('gulp-babel');
-const gulp = require('gulp');
-const header = require('gulp-header');
-const path = require('path');
-const prettier = require('gulp-prettier');
-const rename = require('gulp-rename');
-const { readFile } = require('fs');
-const { promisify } = require('util');
-
-const config = require('../../config');
-const babelPluginCreateReactCustomElementTypeDef = require('../../../tools/babel-plugin-create-react-custom-element-type-def');
-
-const readFileAsync = promisify(readFile);
-const promisifyStream = promisify(asyncDone);
-
-/**
- * Builds the React types
- *
- * @returns {Promise} Gulp stream
- */
-async function reactTypes() {
- const banner = await readFileAsync(path.resolve(__dirname, '../../../tools/license.js'), 'utf8');
- await promisifyStream(() =>
- gulp
- .src([`${config.srcDir}/components/**/*.ts`, `!${config.srcDir}/**/*-story*.ts*`, `!${config.srcDir}/**/stories/*.ts`])
- .pipe(
- babel({
- babelrc: false,
- plugins: [
- ['@babel/plugin-syntax-decorators', { decoratorsBeforeExport: true }],
- '@babel/plugin-syntax-typescript',
- '@babel/plugin-transform-nullish-coalescing-operator',
- '@babel/plugin-transform-optional-chaining',
- babelPluginCreateReactCustomElementTypeDef,
- ],
- })
- )
- .pipe(prettier())
- .pipe(header(banner))
- .pipe(
- rename((pathObj) => {
- pathObj.extname = '.d.ts';
- })
- )
- .pipe(gulp.dest(`${config.jsDestDir}/components-react`))
- );
-}
-
-gulp.task('build:modules:react-types', reactTypes);
diff --git a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react.js b/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react.js
deleted file mode 100644
index 5075a54c10fd..000000000000
--- a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/react.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const asyncDone = require('async-done');
-const gulp = require('gulp');
-const babel = require('gulp-babel');
-const header = require('gulp-header');
-const path = require('path');
-const prettier = require('gulp-prettier');
-const { promisify } = require('util');
-const { readFile } = require('fs');
-
-const config = require('../../config');
-const babelPluginCreateReactCustomElementType = require('../../../tools/babel-plugin-create-react-custom-element-type');
-const babelPluginResourceCJSPaths = require('../../../tools/babel-plugin-resource-cjs-paths');
-
-const readFileAsync = promisify(readFile);
-const promisifyStream = promisify(asyncDone);
-
-/**
- * Builds React modules.
- *
- * @param {object} options The build options.
- * @param {string} options.banner The banner content.
- * @param {string} [options.targetEnv=browser] The target environment.
- * @private
- */
-const buildModulesReact = ({ banner, targetEnv = 'browser' }) => {
- let stream = gulp
- .src([
- `${config.srcDir}/components/**/*.ts`,
- `!${config.srcDir}/**/defs.ts*`,
- `!${config.srcDir}/**/*-story*.ts*`,
- `!${config.srcDir}/**/stories/*.ts`,
- ])
- .pipe(
- babel({
- babelrc: false,
- plugins: [
- ['@babel/plugin-syntax-decorators', { decoratorsBeforeExport: true }],
- '@babel/plugin-syntax-typescript',
- '@babel/plugin-transform-nullish-coalescing-operator',
- '@babel/plugin-transform-optional-chaining',
- [babelPluginCreateReactCustomElementType, { nonUpgradable: targetEnv === 'node' }],
- ],
- })
- );
-
- if (targetEnv === 'node') {
- stream = stream.pipe(
- babel({
- babelrc: false,
- // Ensures `babel-plugin-resource-cjs-paths` runs before `@babel/plugin-transform-modules-commonjs`
- plugins: [babelPluginResourceCJSPaths, '@babel/plugin-transform-modules-commonjs'],
- })
- );
- }
-
- const destDir = {
- browser: `${config.jsDestDir}/components-react`,
- node: `${config.cjsDestDir}/components-react-node`,
- }[targetEnv];
-
- return stream.pipe(prettier()).pipe(header(banner)).pipe(gulp.dest(destDir));
-};
-
-/**
- * Builds the react modules
- *
- * @returns {Promise} Gulp stream
- */
-async function react() {
- const banner = await readFileAsync(path.resolve(__dirname, '../../../tools/license.js'), 'utf8');
- await Promise.all([
- promisifyStream(() => buildModulesReact({ banner })),
- promisifyStream(() => buildModulesReact({ banner, targetEnv: 'node' })),
- ]);
-}
-
-gulp.task('build:modules:react', react);
diff --git a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/types.js b/web-components/packages/carbon-web-components/gulp-tasks/build/modules/types.js
index 6f86cf87c513..7751268ed83d 100644
--- a/web-components/packages/carbon-web-components/gulp-tasks/build/modules/types.js
+++ b/web-components/packages/carbon-web-components/gulp-tasks/build/modules/types.js
@@ -23,7 +23,7 @@ const config = require('../../config');
function types() {
const tsProject = typescript.createProject(path.resolve(__dirname, '../../../tsconfig.json'));
const { dts } = gulp
- .src([`${config.srcDir}/**/*.ts`, `!${config.srcDir}/**/*-story*.ts*`, `!${config.srcDir}/**/stories/**/*.ts*`])
+ .src([`${config.srcDir}/**/*.ts`, `!${config.srcDir}/**/*.stories.ts*`, `!${config.srcDir}/**/stories/**/*.ts*`])
.pipe(sourcemaps.init())
.pipe(tsProject());
return dts
diff --git a/web-components/packages/carbon-web-components/package.json b/web-components/packages/carbon-web-components/package.json
index c8276e0043c1..0506e03514d4 100644
--- a/web-components/packages/carbon-web-components/package.json
+++ b/web-components/packages/carbon-web-components/package.json
@@ -12,10 +12,6 @@
"main": "es/index.js",
"module": "es/index.js",
"exports": {
- "./es/components-react/*": {
- "node": "./lib/components-react-node/*",
- "default": "./es/components-react/*"
- },
"./es/components/*": {
"node": "./lib/components/*",
"default": "./es/components/*"
@@ -108,7 +104,6 @@
"@rollup/plugin-terser": "^0.4.3",
"@rollup/pluginutils": "^4.2.0",
"@storybook/addon-essentials": "^7.6.4",
- "@storybook/addon-knobs": "^7.0.2",
"@storybook/addon-links": "^7.6.4",
"@storybook/addon-mdx-gfm": "^7.6.5",
"@storybook/addon-storysource": "^7.6.4",
@@ -184,8 +179,6 @@
"postcss-selector-parser": "^6.0.0",
"prop-types": "^15.7.2",
"puppeteer": "^13.0.0",
- "react": "16.14.0",
- "react-dom": "16.14.0",
"read-pkg-up": "^7.0.0",
"replace-ext": "^2.0.0",
"resize-observer-polyfill": "^1.5.0",
diff --git a/web-components/packages/carbon-web-components/src/components/button/button-set.ts b/web-components/packages/carbon-web-components/src/components/button/button-set.ts
index 12b7bfb9c3dc..555e08f0a134 100644
--- a/web-components/packages/carbon-web-components/src/components/button/button-set.ts
+++ b/web-components/packages/carbon-web-components/src/components/button/button-set.ts
@@ -71,5 +71,4 @@ class CDSButtonSet extends LitElement {
static styles = styles; // `styles` here is a `CSSResult` generated by custom Vite loader
}
-/* @__GENERATE_REACT_CUSTOM_ELEMENT_TYPE__ */
export default CDSButtonSet;
diff --git a/web-components/packages/carbon-web-components/src/components/implementation-practice.md b/web-components/packages/carbon-web-components/src/components/implementation-practice.md
deleted file mode 100644
index e1114cfc1b9b..000000000000
--- a/web-components/packages/carbon-web-components/src/components/implementation-practice.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Component implementation practice
-
-Starting point:
-https://github.com/carbon-design-system/carbon/blob/v10.3.2/packages/react/src/components/TextInput/TextInput.js
-
-- A set of React props for a component works as a public API of the component,
- in some ways similar to `@Input()`. What is the corresponding feature in
- Custom Elements?
-- Some React props works as event listeners, similar to `@Output()`. How can we
- define event listener API in Custom Elements? Will
- [event retargeting](https://javascript.info/shadow-dom-events) help, or do we
- need to do declarative event listener (similar to Angular
- `(event)="myEventListener"`) to translate the event for the caller? If it
- depends, what will make us choose one vs. another?
-- There is
- [code to prevent event from being fired if the component is disabled](https://github.com/carbon-design-system/carbon/blob/v10.3.2/packages/react/src/components/TextInput/TextInput.js#L43-L52).
- Should we do the same? If so, how can we do that? Can we make the disable
- style automatically take care of that?
-- Some `carbon-components-react` codebase has
- [a way to generate class list from a set of props](https://github.com/carbon-design-system/carbon/blob/v10.3.2/packages/react/src/components/TextInput/TextInput.js#L59-L65)
- (similar to `ngClass`). Can we do the same with `lit-html`?
-- Some `carbon-components-react` code
- [composes sub-portions of a component](https://github.com/carbon-design-system/carbon/blob/v10.3.2/packages/react/src/components/TextInput/TextInput.js#L76-L81),
- often conditionally (similar to `ngIf`). Can we do the same with `lit-html`?
-- How can we
- [render a Carbon icon](https://github.com/carbon-design-system/carbon/blob/v10.3.2/packages/react/src/components/TextInput/TextInput.js#L91)
- in `lit-html`, with specific CSS class applied?
diff --git a/web-components/packages/carbon-web-components/src/globals/wrappers/createReactCustomElementType.ts b/web-components/packages/carbon-web-components/src/globals/wrappers/createReactCustomElementType.ts
deleted file mode 100644
index 4462ce041aac..000000000000
--- a/web-components/packages/carbon-web-components/src/globals/wrappers/createReactCustomElementType.ts
+++ /dev/null
@@ -1,336 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2019, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import React, { Component, createElement, forwardRef } from 'react';
-import on from '../mixins/on';
-import Handle from '../internal/handle';
-
-/**
- * A descriptor for a React event prop of a custom element.
- */
-interface CustomElementEventDescriptor {
- /**
- * The event name.
- */
- name: string;
-
- /**
- * A boolean to detemine usage of capture mode or the event options.
- */
- options?: boolean | EventListenerOptions;
-}
-
-/**
- * A descriptor for a React prop for an attribute of a custom element.
- */
-interface CustomElementPropDescriptor {
- /**
- * The attribute name for the prop.
- */
- attribute?: string | false;
-
- /**
- * The event name (or descriptor) for the prop.
- */
- event?: string | CustomElementEventDescriptor;
-
- /**
- * A function that takes a property value and returns the corresponding attribute value.
- */
- serialize?: (value: any) => string | void;
-}
-
-/**
- * A descriptor for a set of React props for attributes of a custom element.
- */
-interface CustomElementPropsDescriptor {
- [propName: string]: CustomElementPropDescriptor;
-}
-
-/**
- * React props for the component `createCustomElementType()` generates.
- */
-interface CustomElementTypeProps {
- /**
- * Ordinal prop.
- */
- [propName: string]: any;
-
- /**
- * Child nodes.
- */
- // eslint-disable-next-line react/no-unused-prop-types
- children?: React.ReactNode;
-}
-
-/**
- * @param refs List of React refs to merge.
- * @returns Merged React ref.
- */
-const mergeRefs =
- (...refs: React.Ref[]) =>
- (el) => {
- refs.forEach((ref) => {
- // https://github.com/facebook/react/issues/13029#issuecomment-410002316
- if (typeof ref === 'function') {
- ref(el);
- } else if (Object(ref) === ref) {
- // `React.Ref.current` is read-only for regular use case, but we update it here
- (ref as { current: T }).current = el;
- }
- });
- };
-
-/**
- * @param prop A prop value.
- * @param descriptor A React prop descriptor.
- * @returns The corresponding attribute value for the given prop value.
- */
-const convertProp = (prop: any, descriptor: CustomElementPropDescriptor) => {
- if (!descriptor) {
- return prop;
- }
- const { event, serialize } = descriptor;
- if (event) {
- // Events are not set as props, we use DOM `addEventListener()` instead
- return undefined;
- }
- return !serialize ? prop : serialize(prop);
-};
-
-/**
- * @param props A set of React props.
- * @param descriptor A set of React prop desciptor.
- * @returns The set of React props to set to a custom element, corresponding to the given React props.
- */
-const convertProps = (
- props: CustomElementTypeProps,
- descriptor: CustomElementPropsDescriptor
-) =>
- Object.keys(props).reduce((acc, propName) => {
- const { [propName]: descriptorItem } = descriptor;
- const converted = convertProp(props[propName], descriptorItem);
- const { attribute } = descriptorItem ?? {};
- return attribute === false
- ? acc
- : {
- ...acc,
- [attribute || propName]: converted,
- };
- }, {});
-
-/**
- * Attaches listeners of custom events, to a custom element.
- *
- * @param elem The custom element.
- * @param descriptor An object, keyed by prop name, of data that may have custom event names.
- * @param callback A callback function that runs as the custom events fire.
- * @returns A handle that allows to release all event listeners attached.
- */
-const attachEventListeners = (
- elem: HTMLElement,
- descriptor: CustomElementPropsDescriptor,
- callback: (name: string, event: Event) => void
-): Handle => {
- const handles = new Set();
- Object.keys(descriptor).forEach((propName) => {
- if (descriptor[propName]) {
- const { event: eventDescriptor } = descriptor[propName];
- const name =
- Object(eventDescriptor) !== eventDescriptor
- ? (eventDescriptor as string)
- : (eventDescriptor as CustomElementEventDescriptor).name;
- const options =
- Object(eventDescriptor) !== eventDescriptor
- ? undefined
- : (eventDescriptor as CustomElementEventDescriptor).options;
- if (name) {
- handles.add(
- on(
- elem,
- name,
- (event) => {
- callback(propName, event);
- },
- options
- )
- );
- }
- }
- });
- return {
- release() {
- handles.forEach((handle) => {
- handle.release();
- handles.delete(handle);
- });
- return null;
- },
- };
-};
-
-/**
- * @param name The tag name of the custom element.
- * @param descriptor A descriptor for a set of React props for attributes of a custom element.
- * @returns A React component working as a wrapper for the given custom element.
- * @example
- * import { render } from 'react-dom';
- * import createCustomElementType, { booleanSerializer } from '/path/to/createCustomElementType';
- *
- * const CDSDropdown = createCustomElementType(`${prefix}-dropdown`, {
- * disabled: {
- * // Sets `disabled` attribute when the React prop value is truthy, unsets otherwise
- * serialize: booleanSerializer,
- * },
- * helperText: {
- * // Maps `helperText` React prop to `helper-text` attribute
- * attribute: 'helper-text',
- * },
- * onBeforeSelect: {
- * // Sets `onBeforeSelect` React prop value as a listener of `cds-dropdown-beingselected` custom event
- * event: `${prefix}-dropdown-beingselected`,
- * },
- * });
- *
- * render(
- * (
- * { console.log(`${prefix}-dropdown-beingselected is fired!`, event); }}>
- * Option 1
- * Option 2
- * Option 3
- *
- * )
- * document.body
- * );
- */
-const createReactCustomElementType = (
- name: string,
- descriptor: CustomElementPropsDescriptor
-) => {
- /**
- * Array of React prop names that should be mapped to DOM properties instead of attributes.
- */
- const nonAttributeProps = Object.keys(descriptor).filter((propName) => {
- const { [propName]: descriptorItem } = descriptor;
- const { attribute } = descriptorItem ?? {};
- return attribute === false;
- });
-
- /**
- * A React component working as a wrapper for the custom element.
- */
- class CustomElementType extends Component {
- /**
- * The element.
- */
- private _elem: HTMLElement | null = null;
-
- /**
- * The handle that allows to release all event listeners attached to this custom element.
- */
- private _eventListenersHandle: Handle | null = null;
-
- /**
- * The callback function that runs as the custom events fire.
- *
- * @param propName The React prop name associated with the event listener.
- * @param event The event.
- */
- private _handleEvent = (propName: string, event: Event) => {
- const { [propName]: listener } = this.props;
- if (listener) {
- listener.call(event.currentTarget, event);
- }
- };
-
- /**
- * Handles getting/losing the React `ref` object of this custom element.
- *
- * @param elem The custom element.
- */
- private _handleElemRef = (elem: HTMLElement) => {
- this._elem = elem;
- if (this._eventListenersHandle) {
- this._eventListenersHandle.release();
- this._eventListenersHandle = null;
- }
- if (elem) {
- this._eventListenersHandle = attachEventListeners(
- elem,
- descriptor,
- this._handleEvent
- );
- }
- };
-
- /**
- * Reflects change in React props to DOM properties.
- *
- * @param prevProps The previous props.
- */
- updateProps(prevProps: { [key: string]: any } = {}) {
- const { props, _elem: elem } = this;
- nonAttributeProps.forEach((propName) => {
- const { [propName]: prevValue } = prevProps;
- const { [propName]: value } = props;
- if (prevValue !== value) {
- elem![propName] = value;
- }
- });
- }
-
- componentDidMount() {
- this.updateProps();
- }
-
- componentDidUpdate(prevProps) {
- this.updateProps(prevProps);
- }
-
- render() {
- // eslint-disable-next-line react/prop-types
- const { children, innerRef, ...props } = this.props;
- const mergedRef = mergeRefs(innerRef, this._handleElemRef);
- return createElement(
- name,
- { ref: mergedRef, ...convertProps(props, descriptor) },
- children
- );
- }
- }
-
- return forwardRef((props, ref) =>
- createElement(CustomElementType, { ...props, innerRef: ref })
- );
-};
-
-/**
- * @param value A React prop value.
- * @returns Serialized version of React prop value, as a boolean attribute in a custom element.
- */
-export const booleanSerializer = (value) => (!value ? undefined : '');
-
-/**
- * @param value A React prop value.
- * @returns Serialized version of React prop value, as a number attribute in a custom element.
- */
-export const numberSerializer = (value) =>
- value == null ? value : String(value);
-
-/**
- * @param value A React prop value.
- * @returns Serialized version of React prop value, as a object attribute in a custom element.
- */
-export const objectSerializer = (value) =>
- value == null ? value : JSON.stringify(value);
-
-export default createReactCustomElementType;
diff --git a/web-components/packages/carbon-web-components/tests/integration/build/ie_steps.js b/web-components/packages/carbon-web-components/tests/integration/build/ie_steps.js
deleted file mode 100644
index 638a16fae42d..000000000000
--- a/web-components/packages/carbon-web-components/tests/integration/build/ie_steps.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const path = require('path');
-const fs = require('fs-extra');
-const { setup: setupDevServer, teardown: teardownDevServer } = require('jest-dev-server');
-const exec = require('../exec');
-const replaceDependencies = require('../replace-dependencies');
-
-const PORT = 8081;
-
-describe('IE example', () => {
- beforeAll(async () => {
- const projectRoot = path.resolve(__dirname, '../../..');
- const src = path.resolve(projectRoot, 'examples/codesandbox/ie');
- const tmpDir = process.env.CCE_EXAMPLE_TMPDIR;
- await fs.copy(src, `${tmpDir}/ie`);
- await replaceDependencies([`${tmpDir}/ie/package.json`]);
- await exec('yarn', ['install'], { cwd: `${tmpDir}/ie` });
- await setupDevServer({
- command: `cd ${tmpDir}/ie && node ${path.resolve(__dirname, 'webpack-server.js')} --port=${PORT}`,
- launchTimeout: Number(process.env.LAUNCH_TIMEOUT),
- port: PORT,
- });
- await page.goto(`http://localhost:${PORT}`);
- }, Number(process.env.LAUNCH_TIMEOUT));
-
- it('should show a title', async () => {
- await expect(page).toMatch('Hello World!');
- });
-
- it('should have dropdown interactive', async () => {
- await expect(page).toClick('cds-dropdown');
- await expect(page).toMatchElement('cds-dropdown[open]');
- await expect(page).toClick('cds-dropdown');
- await expect(page).toMatchElement('cds-dropdown:not([open])');
- });
-
- afterAll(async () => {
- await teardownDevServer();
- });
-});
diff --git a/web-components/packages/carbon-web-components/tests/integration/build/react-ssr_steps.js b/web-components/packages/carbon-web-components/tests/integration/build/react-ssr_steps.js
deleted file mode 100644
index 9857fb497afd..000000000000
--- a/web-components/packages/carbon-web-components/tests/integration/build/react-ssr_steps.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const path = require('path');
-const fs = require('fs-extra');
-const { setup: setupDevServer, teardown: teardownDevServer } = require('jest-dev-server');
-const exec = require('../exec');
-const replaceDependencies = require('../replace-dependencies');
-
-const PORT = 3001;
-
-describe('React SSR example', () => {
- beforeAll(async () => {
- const projectRoot = path.resolve(__dirname, '../../..');
- const src = path.resolve(projectRoot, 'examples/codesandbox/react-ssr');
- const tmpDir = process.env.CCE_EXAMPLE_TMPDIR;
- await fs.copy(src, `${tmpDir}/react-ssr`);
- await replaceDependencies([`${tmpDir}/react-ssr/package.json`]);
- await exec('yarn', ['install'], { cwd: `${tmpDir}/react-ssr` });
- await setupDevServer({
- command: `cd ${tmpDir}/react-ssr && cross-env PORT=${PORT} yarn start`,
- launchTimeout: Number(process.env.LAUNCH_TIMEOUT),
- port: PORT,
- });
- await page.goto(`http://localhost:${PORT}`);
- }, Number(process.env.LAUNCH_TIMEOUT));
-
- it('should show a title', async () => {
- await expect(page).toMatch('Hello World!');
- });
-
- it('should have dropdown interactive', async () => {
- await expect(page).toClick('cds-dropdown');
- await expect(page).toMatchElement('cds-dropdown[open]');
- await expect(page).toClick('cds-dropdown');
- await expect(page).toMatchElement('cds-dropdown:not([open])');
- });
-
- afterAll(async () => {
- await teardownDevServer();
- });
-});
diff --git a/web-components/packages/carbon-web-components/tests/integration/build/react_steps.js b/web-components/packages/carbon-web-components/tests/integration/build/react_steps.js
deleted file mode 100644
index c28bb2f56957..000000000000
--- a/web-components/packages/carbon-web-components/tests/integration/build/react_steps.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const path = require('path');
-const fs = require('fs-extra');
-const { setup: setupDevServer, teardown: teardownDevServer } = require('jest-dev-server');
-const exec = require('../exec');
-const replaceDependencies = require('../replace-dependencies');
-
-const PORT = 3000;
-
-describe('React example', () => {
- beforeAll(async () => {
- const projectRoot = path.resolve(__dirname, '../../..');
- const src = path.resolve(projectRoot, 'examples/codesandbox/react');
- const tmpDir = process.env.CCE_EXAMPLE_TMPDIR;
- await fs.copy(src, `${tmpDir}/react`);
- await replaceDependencies([`${tmpDir}/react/package.json`]);
- await exec('yarn', ['install'], { cwd: `${tmpDir}/react` });
- await setupDevServer({
- command: `cd ${tmpDir}/react && node ${path.resolve(__dirname, 'webpack-server.js')} --port=${PORT}`,
- launchTimeout: Number(process.env.LAUNCH_TIMEOUT),
- port: PORT,
- });
- await page.goto(`http://localhost:${PORT}`);
- }, Number(process.env.LAUNCH_TIMEOUT));
-
- it('should show a title', async () => {
- await expect(page).toMatch('Hello World!');
- });
-
- it('should have dropdown interactive', async () => {
- await expect(page).toClick('cds-dropdown');
- await expect(page).toMatchElement('cds-dropdown[open]');
- await expect(page).toClick('cds-dropdown');
- await expect(page).toMatchElement('cds-dropdown:not([open])');
- });
-
- afterAll(async () => {
- await teardownDevServer();
- });
-});
diff --git a/web-components/packages/carbon-web-components/tools/babel-plugin-create-react-custom-element-type-def.js b/web-components/packages/carbon-web-components/tools/babel-plugin-create-react-custom-element-type-def.js
deleted file mode 100644
index 75f925449542..000000000000
--- a/web-components/packages/carbon-web-components/tools/babel-plugin-create-react-custom-element-type-def.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2020, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const { default: template } = require('@babel/template');
-const {
- createMetadataVisitor,
-} = require('./babel-plugin-create-react-custom-element-type');
-
-const regexEvent = /^event/;
-const regexMagicComment = /The name of the custom event/g;
-const magicCommentForReact = 'The event handler for the custom event';
-
-module.exports = function generateCreateReactCustomElementType(api) {
- const { types: t } = api;
- const metadataVisitor = createMetadataVisitor(api);
-
- const types = {
- Boolean: 'boolean',
- Number: 'number',
- };
-
- return {
- name: 'create-react-custom-element-type-def',
- visitor: {
- Program(path, { file }) {
- const declaredProps = {};
- const customEvents = {};
- const namedExportsSources = {};
- const context = {
- file,
- declaredProps,
- customEvents,
- namedExportsSources,
- };
- // Gathers metadata of custom element properties and events, into `context`
- path.traverse(metadataVisitor, context);
-
- const {
- className,
- classComments = [],
- parentDescriptorSource,
- } = context;
- const props = Object.keys(declaredProps).reduce((acc, key) => {
- const { comments = [], type } = declaredProps[key];
- return [
- ...acc,
- comments.map(({ value }) => `/*${value}*/`).join('\n'),
- `${key}?: ${types[type] || 'string'};`,
- ];
- }, []);
- const events = Object.keys(customEvents).reduce((acc, key) => {
- const { comments = [] } = customEvents[key];
- return [
- ...acc,
- comments
- .map(
- ({ value }) =>
- `/*${value.replace(
- regexMagicComment,
- magicCommentForReact
- )}*/`
- )
- .join('\n'),
- `${key.replace(regexEvent, 'on')}?: (event: CustomEvent) => void;`,
- ];
- }, []);
-
- const build = template(
- `
- import { Component } from 'react';
- ${
- !parentDescriptorSource
- ? ''
- : `import { ComponentProps as ParentComponentProps } from '${parentDescriptorSource}';`
- }
- export interface ComponentProps${
- !parentDescriptorSource ? '' : ' extends ParentComponentProps'
- } {
- ${props.join('\n')}
- ${events.join('\n')}
- ${parentDescriptorSource ? '' : '[prop: string]: unknown;'}
- }
- ${classComments.map((value) => `/*${value}*/`).join('\n')}
- declare class ${className} extends Component {}
- export default ${className};
- `,
- {
- plugins: ['typescript'],
- preserveComments: true,
- sourceType: 'module',
- }
- );
-
- const body = build();
- path.replaceWith(t.program(body));
- path.stop();
- },
- },
- };
-};
diff --git a/web-components/packages/carbon-web-components/tools/babel-plugin-create-react-custom-element-type.js b/web-components/packages/carbon-web-components/tools/babel-plugin-create-react-custom-element-type.js
deleted file mode 100644
index e4064bcbeab0..000000000000
--- a/web-components/packages/carbon-web-components/tools/babel-plugin-create-react-custom-element-type.js
+++ /dev/null
@@ -1,626 +0,0 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2019, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-const { dirname, isAbsolute, relative, resolve } = require('path');
-const { default: template } = require('@babel/template');
-const { default: traverse } = require('@babel/traverse');
-const {
- default: transformTemplateLiterals,
-} = require('@babel/plugin-transform-template-literals');
-const replaceExtension = require('replace-ext');
-
-const regexEvent = /^event/;
-
-/**
- * @param {string} source The source file path.
- * @param {string} extension The extension to replace source file path with.
- * @returns {string} Given `source` with its extension replaced with the given one, preserving `./`.
- */
-function replaceExtensionRelative(source, extension) {
- return !/^\./.test(source)
- ? source
- : `${dirname(source) !== '.' ? '' : './'}${replaceExtension(
- source,
- extension
- )}`;
-}
-
-function createMetadataVisitor(api) {
- const { types: t } = api;
-
- /**
- * @param {string} path The Babel path what a `@property()` decorator call refers to.
- * @returns {boolean} `true` if such decorator is imported from `lit-element`.
- */
- const propertyIsFromLit = (path) => {
- const { parentPath } = path;
- return (
- path.isImportSpecifier() &&
- path.get('imported').isIdentifier({ name: 'property' }) &&
- parentPath.isImportDeclaration &&
- parentPath.get('source').isStringLiteral({ value: 'lit-element' })
- );
- };
-
- const getParentClassImportSource = (path) => {
- const { parentPath } = path;
- if (
- path.isImportDefaultSpecifier() &&
- parentPath.isImportDeclaration &&
- parentPath.get('source').isStringLiteral()
- ) {
- return parentPath.get('source').node.value;
- }
- return undefined;
- };
-
- /**
- * Metadata harvested from `@property` decorator.
- *
- * @typedef {object} PropertyMetadata
- * @property {string} [type] The property type.
- * @property {string|boolean} [attribute]
- * The attribute name the property maps to.
- * `false` means there is no corresponding attribute.
- */
-
- /**
- * @param {string} path The Babel path for `@property()` decorator call.
- * @returns {PropertyMetadata} The metadata harvested from the given `@property()` decorator call.
- */
- const getPropertyMetadata = (path) => {
- const metadata = {};
- const expression = path.get('expression');
- if (!t.isCallExpression(expression)) {
- return undefined;
- }
-
- if (
- !expression.get('callee').isIdentifier() ||
- !propertyIsFromLit(
- path.scope.getBinding(expression.get('callee.name').node).path
- )
- ) {
- return undefined;
- }
-
- const firstArg = expression.get('arguments.0');
- if (firstArg && firstArg.isObjectExpression()) {
- // eslint-disable-next-line no-restricted-syntax
- for (const property of firstArg.get('properties')) {
- const key = property.get('key');
- const value = property.get('value');
- if (key.isIdentifier({ name: 'type' })) {
- value.assertIdentifier();
- metadata.type = value.get('name').node;
- } else if (key.isIdentifier({ name: 'attribute' })) {
- if (!value.isBooleanLiteral() && !value.isStringLiteral()) {
- throw value.buildCodeFrameError(
- '`attribute` in `@property` must point to a boolean literal or a string literal.'
- );
- }
- metadata.attribute = value.get('value').node;
- }
- }
- }
-
- const leadingComments = path.parentPath.get('leadingComments');
- if (leadingComments) {
- metadata.comments = (
- Array.isArray(leadingComments) ? leadingComments : [leadingComments]
- )
- .map((item) => item.node)
- .filter(Boolean);
- }
-
- return metadata;
- };
-
- /**
- * @param {string} path The Babel path of the superclass.
- * @returns {PropertyMetadata}
- * The given Babel path itself if it's an identifier.
- * The first argument if the given Babel path is a function, assuming it as a mixin call.
- */
- const getTarget = (path) => {
- if (path.isIdentifier()) {
- return path;
- }
- if (path.isCallExpression()) {
- return getTarget(path.get('arguments.0'));
- }
- return null;
- };
-
- /**
- * A visitor to gather metadata of custom element properties and events,
- * from `type` in `@property()` for the former, from `eventSomething` for the latter.
- * The gathered metadata is stored in the context `declaredProps` for the former, `customEvents` for the latter.
- */
- const metadataVisitor = {
- ClassDeclaration(path, context) {
- const { file } = context;
- const superClass = getTarget(path.get('superClass'));
- if (superClass) {
- const parentClassImportSource = getParentClassImportSource(
- superClass.scope.getBinding(superClass.node.name).path
- );
- if (parentClassImportSource) {
- const relativeTarget = relative(
- resolve(__dirname, '../src/components'),
- resolve(dirname(file.opts.filename), parentClassImportSource)
- );
- if (!isAbsolute(relativeTarget) && !relativeTarget.startsWith('..')) {
- context.parentDescriptorSource = parentClassImportSource;
- }
- }
- }
- const leadingComments = path.get('leadingComments');
- if (leadingComments) {
- context.classComments = (
- Array.isArray(leadingComments) ? leadingComments : [leadingComments]
- )
- .map((item) => item.node)
- .filter(Boolean);
- }
- context.className = path.get('id.name').node;
- },
-
- ClassMethod(path, { customEvents }) {
- const { static: staticMethod, kind, key } = path.node;
- const { name } = key;
- if (staticMethod && kind === 'get' && regexEvent.test(name)) {
- const body = path.get('body');
- const firstBody = body.get('body.0');
- firstBody.assertReturnStatement();
- const argument = firstBody.get('argument');
- if (!argument.isStringLiteral() && !argument.isTemplateLiteral()) {
- throw firstBody.buildCodeFrameError(
- '`static get eventFoo` must have and be only with a return statement with a string literal or a template literal.'
- );
- }
- const metadata = {
- eventName: t.cloneDeep(argument.node),
- };
- const leadingComments = path.get('leadingComments');
- if (leadingComments) {
- metadata.comments = (
- Array.isArray(leadingComments) ? leadingComments : [leadingComments]
- )
- .map((item) => item.node)
- .filter(Boolean);
- }
- customEvents[name] = metadata;
- }
- },
-
- ClassProperty(path, { customEvents }) {
- const { static: staticField, key } = path.node;
- const value = path.get('value');
- const { name } = key;
- if (staticField && regexEvent.test(name)) {
- if (!value.isStringLiteral() && !value.isTemplateLiteral()) {
- throw value.buildCodeFrameError(
- '`static eventFoo` must refer to a string literal or a template literal.'
- );
- }
- const metadata = {
- eventName: t.cloneDeep(value.node),
- };
- const leadingComments = path.get('leadingComments');
- if (leadingComments) {
- metadata.comments = (
- Array.isArray(leadingComments) ? leadingComments : [leadingComments]
- )
- .map((item) => item.node)
- .filter(Boolean);
- }
- customEvents[name] = metadata;
- }
- },
-
- Decorator(path, context) {
- const { parent, parentPath } = path;
- const { declaredProps } = context;
- const expression = path.get('expression');
- const customElementName = expression.get('arguments.0');
- if (
- expression.isCallExpression() &&
- expression.get('callee').isIdentifier({ name: 'customElement' })
- ) {
- if (
- !customElementName.isStringLiteral() &&
- !customElementName.isTemplateLiteral()
- ) {
- throw customElementName.buildCodeFrameError(
- '`@customElement()` must be called with the custom element name.'
- );
- }
- context.customElementName = customElementName.node;
- }
-
- const metadata = getPropertyMetadata(path);
- if (metadata) {
- if (
- !parentPath.isClassProperty() &&
- (!parentPath.isClassMethod() ||
- (parentPath.node.kind !== 'get' && parentPath.node.kind !== 'set'))
- ) {
- throw parentPath.buildCodeFrameError(
- '`@property()` must target class properties.'
- );
- }
- declaredProps[parent.key.name] = metadata;
- }
- },
-
- ExportNamedDeclaration(path, context) {
- const { source, specifiers } = path.node;
- const { namedExportsSources } = context;
- if (specifiers.length > 0) {
- if (source) {
- const { value: sourceValue } = source;
- namedExportsSources[sourceValue] =
- namedExportsSources[sourceValue] || {};
- // eslint-disable-next-line no-restricted-syntax
- for (const { local, exported } of specifiers) {
- namedExportsSources[sourceValue][exported.name] = local.name;
- }
- } else {
- // eslint-disable-next-line no-restricted-syntax
- for (const { local, exported } of specifiers) {
- const { path: bindingPath } = path.scope.getBinding(local.name);
- const { value: bindingSourceValue } =
- bindingPath.parentPath.node.source;
- namedExportsSources[bindingSourceValue] =
- namedExportsSources[bindingSourceValue] || {};
- namedExportsSources[bindingSourceValue][exported.name] =
- bindingPath.isImportDefaultSpecifier()
- ? 'default'
- : bindingPath.get('imported').node.name;
- }
- }
- }
- },
- };
-
- return metadataVisitor;
-}
-
-module.exports = function generateCreateReactCustomElementType(
- api,
- { nonUpgradable } = {}
-) {
- const { types: t } = api;
-
- const booleanSerializerIdentifier = t.identifier('booleanSerializer');
- const numberSerializerIdentifier = t.identifier('numberSerializer');
- const objectSerializerIdentifier = t.identifier('objectSerializer');
-
- /**
- * The named import specifiers associated with `type` in `@property`.
- *
- * @type {(boolean|number|object)}
- */
- const importSpecifiers = {
- Boolean: t.importSpecifier(
- booleanSerializerIdentifier,
- booleanSerializerIdentifier
- ),
- Number: t.importSpecifier(
- numberSerializerIdentifier,
- numberSerializerIdentifier
- ),
- Object: t.importSpecifier(
- objectSerializerIdentifier,
- objectSerializerIdentifier
- ),
- };
-
- /**
- * The serializers associated with `type` in `@property`.
- *
- * @type {(boolean|number|object)}
- */
- const serializers = {
- Boolean: booleanSerializerIdentifier,
- Number: numberSerializerIdentifier,
- Object: objectSerializerIdentifier,
- };
-
- /**
- * The prop types associated with `type` in `@property`.
- *
- * @type {(string|boolean|number|object)}
- */
- const propTypesForLitTypes = {
- String: t.memberExpression(
- t.identifier('PropTypes'),
- t.identifier('string')
- ),
- Boolean: t.memberExpression(
- t.identifier('PropTypes'),
- t.identifier('bool')
- ),
- Number: t.memberExpression(
- t.identifier('PropTypes'),
- t.identifier('number')
- ),
- Object: t.memberExpression(
- t.identifier('PropTypes'),
- t.identifier('object')
- ),
- };
-
- /**
- *
- * @param {object} declaredProps The list of metadata harvested from `@property()` decorator calls.
- * @returns {string} The `import` statement for `src/globals/wrappers/createReactCustomElementType`.
- */
- const buildCreateReactCustomElementTypeImport = (declaredProps) => {
- const typesInUse = Object.keys(declaredProps)
- .map((name) => declaredProps[name].type)
- .filter((type) => importSpecifiers[type]);
-
- return t.importDeclaration(
- [
- t.importDefaultSpecifier(t.identifier('createReactCustomElementType')),
- ...Array.from(new Set(typesInUse)).map(
- (type) => importSpecifiers[type]
- ),
- ],
- t.stringLiteral('../../globals/wrappers/createReactCustomElementType.js')
- );
- };
-
- /**
- * @param {object} declaredProps The list of metadata harvested from `@property()` decorator calls.
- * @returns {object}
- * The list of `{ attribute: 'attribute-name', serialize: typeSerializer }` generated from `@property()` decorators.
- */
- const buildPropsDescriptor = (declaredProps) =>
- Object.keys(declaredProps).map((name) => {
- const { type, attribute } = declaredProps[name];
- const propDesciptor = [];
- if (attribute === false) {
- propDesciptor.push(
- t.objectProperty(t.identifier('attribute'), t.booleanLiteral(false))
- );
- } else {
- if (type && type !== 'String') {
- const serializer = serializers[type];
- if (!serializer) {
- throw new Error(`No serializer found for type: ${type}`);
- }
- propDesciptor.push(
- t.objectProperty(t.identifier('serialize'), serializer)
- );
- }
- if (attribute) {
- propDesciptor.push(
- t.objectProperty(
- t.identifier('attribute'),
- t.stringLiteral(attribute)
- )
- );
- }
- }
- return t.objectProperty(
- t.identifier(name),
- t.objectExpression(propDesciptor)
- );
- });
-
- /**
- * @param {string} customEvents
- * The list of metadata harvested from `eventSomething` static properties.
- * @returns {object} The list of `{ event: 'event-name' }` generated from `eventSomething` static properties.
- */
- const buildEventsDescriptor = (customEvents) =>
- Object.keys(customEvents).map((name) =>
- t.objectProperty(
- t.identifier(name.replace(regexEvent, 'on')),
- t.objectExpression([
- t.objectProperty(t.identifier('event'), customEvents[name].eventName),
- ])
- )
- );
-
- /**
- * @param {object} declaredProps The list of metadata harvested from `@property()` decorator calls.
- * @returns {object} The list of `PropTypes.someType` generated from `@property()` decorators.
- */
- const buildPropTypes = (declaredProps) =>
- Object.keys(declaredProps).map((name) => {
- const { type } = declaredProps[name];
- const propType = propTypesForLitTypes[type || 'String'];
- if (!propType) {
- throw new Error(`No React prop type found for type: ${type}`);
- }
- return t.objectProperty(t.identifier(name), propType);
- });
-
- /**
- * @param {string} customEvents
- * The list of metadata harvested from `eventSomething` static properties.
- * @returns {object} The list of `PropTypes.func` generated from `eventSomething` static properties.
- */
- const buildEventsPropTypes = (customEvents) =>
- Object.keys(customEvents).map((name) =>
- t.objectProperty(
- t.identifier(name.replace(regexEvent, 'on')),
- t.memberExpression(t.identifier('PropTypes'), t.identifier('func'))
- )
- );
-
- const metadataVisitor = createMetadataVisitor(api);
-
- /**
- * A Babel plugin that first gathers metadata of custom element properties/events from AST,
- * then creates another AST of `createReactCustomElementType()` and replaces the original AST with the created one.
- */
- return {
- name: 'create-react-custom-element-type',
- visitor: {
- Program(path, { file }) {
- const declaredProps = {};
- const customEvents = {};
- const namedExportsSources = {};
- const context = {
- file,
- declaredProps,
- customEvents,
- namedExportsSources,
- };
- // Gathers metadata of custom element properties and events, into `context`
- path.traverse(metadataVisitor, context);
-
- const relativePath = relative(
- resolve(__dirname, '../src/components'),
- file.opts.filename
- );
- const retargedPath = t.stringLiteral(
- `../../components/${replaceExtension(relativePath, '.js')}`
- );
-
- // Creates a module with `createReactCustomElementType()`
- // with the gathered metadata of custom element properties and events
- const descriptors = t.objectExpression([
- ...buildPropsDescriptor(declaredProps),
- ...buildEventsDescriptor(customEvents),
- ]);
- const descriptorsWithParent = !context.parentDescriptorSource
- ? descriptors
- : t.callExpression(
- t.memberExpression(
- t.identifier('Object'),
- t.identifier('assign')
- ),
- [
- t.objectExpression([]),
- t.identifier('parentDescriptor'),
- descriptors,
- ]
- );
-
- const propTypes = t.objectExpression([
- ...buildPropTypes(declaredProps),
- ...buildEventsPropTypes(customEvents),
- ]);
- const propTypesWithParent = !context.parentDescriptorSource
- ? propTypes
- : t.callExpression(
- t.memberExpression(
- t.identifier('Object'),
- t.identifier('assign')
- ),
- [
- t.objectExpression([]),
- t.identifier('parentPropTypes'),
- propTypes,
- ]
- );
-
- const body = [];
- if (!context.customElementName) {
- if (context.className) {
- // Class name found but custom element name not found means that it's likely a module not for custom element
- // (e.g. an abstract class like floating menu)
- // If so, we just export empty `descriptor` and re-export from the original class
- body.unshift(
- ...template.ast`
- export var descriptor = ${descriptorsWithParent};
- export var propTypes = ${propTypesWithParent};
- `
- );
- }
- } else {
- body.unshift(
- buildCreateReactCustomElementTypeImport(declaredProps),
- ...template.ast`
- import PropTypes from "prop-types";
- import { prefix } from '../../globals/settings.js';
- export var descriptor = ${descriptorsWithParent};
- export var propTypes = ${propTypesWithParent};
- const Component = createReactCustomElementType(${context.customElementName}, descriptor);
- Component.propTypes = propTypes;
- export default Component;
- `
- );
- if (!nonUpgradable) {
- body.unshift(
- t.exportNamedDeclaration(
- null,
- [
- t.exportSpecifier(
- t.identifier('default'),
- t.identifier('CustomElement')
- ),
- ],
- retargedPath
- )
- );
- }
- }
- if (context.parentDescriptorSource) {
- body.unshift(
- t.importDeclaration(
- [
- t.importSpecifier(
- t.identifier('parentDescriptor'),
- t.identifier('descriptor')
- ),
- ],
- t.stringLiteral(
- replaceExtensionRelative(context.parentDescriptorSource, '.js')
- )
- ),
- t.importDeclaration(
- [
- t.importSpecifier(
- t.identifier('parentPropTypes'),
- t.identifier('propTypes')
- ),
- ],
- t.stringLiteral(
- replaceExtensionRelative(context.parentDescriptorSource, '.js')
- )
- )
- );
- }
- // eslint-disable-next-line no-restricted-syntax
- for (const [source, exports] of Object.entries(namedExportsSources)) {
- body.unshift(
- t.exportNamedDeclaration(
- null,
- Object.keys(exports).map((exportedName) =>
- t.exportSpecifier(
- t.identifier(exports[exportedName]),
- t.identifier(exportedName)
- )
- ),
- t.stringLiteral(replaceExtensionRelative(source, '.js'))
- )
- );
- }
- const program = t.program(body);
- traverse(
- program,
- transformTemplateLiterals(api).visitor,
- path.scope,
- path
- );
- path.replaceWith(program);
- path.stop();
- },
- },
- };
-};
-
-module.exports.createMetadataVisitor = createMetadataVisitor;
diff --git a/web-components/packages/carbon-web-components/tsconfig.json b/web-components/packages/carbon-web-components/tsconfig.json
index 0fa7829e6598..73e255537924 100644
--- a/web-components/packages/carbon-web-components/tsconfig.json
+++ b/web-components/packages/carbon-web-components/tsconfig.json
@@ -17,7 +17,6 @@
"experimentalDecorators": true,
"strict": true,
"noImplicitAny": false,
- "jsx": "react",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
},