From d70610d4b2a5c1bd36d7f065e576a399b9fdd845 Mon Sep 17 00:00:00 2001 From: "chuanbin.ccb" Date: Mon, 18 Jul 2022 11:09:26 +0800 Subject: [PATCH 1/2] feat: support jsx condition --- app/rax/src/client/index.ts | 9 ++- app/rax/src/client/preview/index.ts | 22 ------- app/rax/src/client/preview/index.tsx | 66 +++++++++++++++++++ app/rax/src/client/preview/render.ts | 45 ------------- app/rax/src/client/preview/render.tsx | 61 +++++++++++++++++ app/rax/src/client/preview/types-6-0.ts | 24 +++++++ app/rax/src/client/preview/types-6-3.ts | 26 ++++++++ app/rax/src/client/preview/types.ts | 22 +++++++ app/rax/src/server/framework-preset-rax.ts | 29 ++++++++ app/rax/src/server/options.ts | 7 +- .../src/stories/addon-a11y.stories.js | 38 ++++++++--- .../src/stories/x-for.stories.js | 33 ++++++++++ 12 files changed, 302 insertions(+), 80 deletions(-) delete mode 100644 app/rax/src/client/preview/index.ts create mode 100644 app/rax/src/client/preview/index.tsx delete mode 100644 app/rax/src/client/preview/render.ts create mode 100644 app/rax/src/client/preview/render.tsx create mode 100644 app/rax/src/client/preview/types-6-0.ts create mode 100644 app/rax/src/client/preview/types-6-3.ts create mode 100644 app/rax/src/client/preview/types.ts create mode 100644 examples/rax-kitchen-sink/src/stories/x-for.stories.js diff --git a/app/rax/src/client/index.ts b/app/rax/src/client/index.ts index 21d3f4bf39..54de76c858 100644 --- a/app/rax/src/client/index.ts +++ b/app/rax/src/client/index.ts @@ -1,3 +1,4 @@ +export type { DecoratorFn } from './preview'; export { storiesOf, setAddon, @@ -5,6 +6,12 @@ export { addParameters, configure, getStorybook, - forceReRender, raw, + forceReRender, } from './preview'; + +export * from './preview/types-6-3'; + +if (module && module.hot && module.hot.decline) { + module.hot.decline(); +} diff --git a/app/rax/src/client/preview/index.ts b/app/rax/src/client/preview/index.ts deleted file mode 100644 index b198c52c60..0000000000 --- a/app/rax/src/client/preview/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { start } from '@storybook/core/client'; - -import './globals'; -import render from './render'; - -const { configure: coreConfigure, clientApi, forceReRender } = start(render); - -export const { - setAddon, - addDecorator, - addParameters, - clearDecorators, - getStorybook, - raw, -} = clientApi; - -const framework = 'rax'; -export const storiesOf = (kind: string, m: any) => - clientApi.storiesOf(kind, m).addParameters({ framework }); -export const configure = (loadable: any, m: any) => coreConfigure(framework, loadable, m); - -export { forceReRender }; diff --git a/app/rax/src/client/preview/index.tsx b/app/rax/src/client/preview/index.tsx new file mode 100644 index 0000000000..7d06738b36 --- /dev/null +++ b/app/rax/src/client/preview/index.tsx @@ -0,0 +1,66 @@ +/* eslint-disable prefer-destructuring */ +import React, { createElement } from 'rax'; +import { start } from '@storybook/core/client'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { ClientStoryApi, Loadable } from '@storybook/addons'; +import { IStorybookSection, StoryFnReactReturnType } from './types'; +import { Story } from './types-6-3'; +import './globals'; +import render from './render'; + +const framework = 'rax'; + +// @ts-ignore +const globalRender: Story = (args, { parameters }) => { + const Component = parameters.component; + return ; +}; + +interface ClientApi extends ClientStoryApi { + setAddon(addon: any): void; + configure(loader: Loadable, module: NodeModule): void; + getStorybook(): IStorybookSection[]; + clearDecorators(): void; + forceReRender(): void; + raw: () => any; // todo add type +} + +const api = start(render); + +// @ts-ignore +api.clientApi.globalRender = globalRender; + +export const storiesOf: ClientApi['storiesOf'] = (kind, m) => { + return (api.clientApi.storiesOf(kind, m) as ReturnType).addParameters({ + framework, + }); +}; + +export const configure: ClientApi['configure'] = (...args) => api.configure(framework, ...args); +export const addDecorator: ClientApi['addDecorator'] = api.clientApi + .addDecorator as ClientApi['addDecorator']; +export type DecoratorFn = Parameters[0]; +export const addParameters: ClientApi['addParameters'] = api.clientApi + .addParameters as ClientApi['addParameters']; +export const clearDecorators: ClientApi['clearDecorators'] = api.clientApi.clearDecorators; +export const setAddon: ClientApi['setAddon'] = api.clientApi.setAddon; +export const forceReRender: ClientApi['forceReRender'] = api.forceReRender; +export const getStorybook: ClientApi['getStorybook'] = api.clientApi.getStorybook; +export const raw: ClientApi['raw'] = api.clientApi.raw; + +// const { configure: coreConfigure, clientApi, forceReRender } = start(render); + +// export const { +// setAddon, +// addDecorator, +// addParameters, +// clearDecorators, +// getStorybook, +// raw, +// } = clientApi; + +// export const storiesOf = (kind: string, m: any) => +// clientApi.storiesOf(kind, m).addParameters({ framework }); +// export const configure = (loadable: any, m: any) => coreConfigure(framework, loadable, m); + +// export { forceReRender }; diff --git a/app/rax/src/client/preview/render.ts b/app/rax/src/client/preview/render.ts deleted file mode 100644 index f791ccfff7..0000000000 --- a/app/rax/src/client/preview/render.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { createElement, render } from 'rax'; -import * as DriverDOM from 'driver-dom'; - -import { document } from 'global'; -import dedent from 'ts-dedent'; - -const rootElement = document ? document.getElementById('root') : null; - -export default function renderMain({ - storyFn, - kind, - name, - showMain, - showError, -}: { - storyFn: Function; - kind: string; - name: string; - showMain: () => any; - showError: (input: { title: string; description: string }) => void; -}) { - const Element = storyFn; - - if (!Element) { - showError({ - title: `Expecting a Rax element from the story: "${name}" of "${kind}".`, - description: dedent` - Did you forget to return the Rax element from the story? - Use "() => ()" or "() => { return ; }" when defining the story. - `, - }); - return; - } - - showMain(); - - // There is something miscellaneous here, for now, more precisely on L23, - // as we are using the storyFn directly and not calling it, so `Element` is a - // function but according to `createElement` types, there is no signature - // taking a function as input. - // @ts-expect-error - render(createElement(Element), rootElement, { - driver: DriverDOM, - }); -} diff --git a/app/rax/src/client/preview/render.tsx b/app/rax/src/client/preview/render.tsx new file mode 100644 index 0000000000..d80289e08b --- /dev/null +++ b/app/rax/src/client/preview/render.tsx @@ -0,0 +1,61 @@ +import { createElement, render, Component } from 'rax'; +import * as DriverDOM from 'driver-dom'; +import dedent from 'ts-dedent'; + +import { document } from 'global'; +import { RenderContext } from './types'; + +const rootElement = document ? document.getElementById('root') : null; + +const renderDom = (node: any, el: Element, driver: any) => +new Promise((resolve) => { + render(node, el, driver, resolve); +}); + +class ErrorBoundary extends Component<{ + showException: (err: Error) => void; + showMain: () => void; +}> { + state = { hasError: false }; + + static getDerivedStateFromError() { + return { hasError: true }; + } + + componentDidMount() { + const { hasError } = this.state; + const { showMain } = this.props; + if (!hasError) { + showMain(); + } + } + + componentDidCatch(err: Error) { + const { showException } = this.props; + // message partially duplicates stack, strip it + showException(err); + } + + render() { + const { hasError } = this.state; + const { children } = this.props; + + return hasError ? null : children; + } +} + +export default async function renderMain({ + args, + storyFn, + showMain, +}: RenderContext) { + showMain(); + + // There is something miscellaneous here, for now, more precisely on L23, + // as we are using the storyFn directly and not calling it, so `Element` is a + // function but according to `createElement` types, there is no signature + // taking a function as input. + await renderDom(createElement(storyFn, args), rootElement, { + driver: DriverDOM, + }); +} diff --git a/app/rax/src/client/preview/types-6-0.ts b/app/rax/src/client/preview/types-6-0.ts new file mode 100644 index 0000000000..1f103eba50 --- /dev/null +++ b/app/rax/src/client/preview/types-6-0.ts @@ -0,0 +1,24 @@ +import { ComponentType } from 'rax'; +import { Args as DefaultArgs, Annotations, BaseMeta, BaseStory } from '@storybook/addons'; +import { StoryFnReactReturnType } from './types'; + +export type { Args, ArgTypes, Parameters, StoryContext } from '@storybook/addons'; + +type ReactComponent = ComponentType; +type ReactReturnType = StoryFnReactReturnType; + +/** + * Metadata to configure the stories for a component. + * + * @see [Default export](https://storybook.js.org/docs/formats/component-story-format/#default-export) + */ +export type Meta = BaseMeta & + Annotations; + +/** + * Story function that represents a component example. + * + * @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports) + */ +export type Story = BaseStory & + Annotations; diff --git a/app/rax/src/client/preview/types-6-3.ts b/app/rax/src/client/preview/types-6-3.ts new file mode 100644 index 0000000000..edfa6cfa9a --- /dev/null +++ b/app/rax/src/client/preview/types-6-3.ts @@ -0,0 +1,26 @@ +import { ComponentProps, JSXElementConstructor } from 'rax'; +import type { Story, Meta } from './types-6-0'; + +export * from './types-6-0'; + +/** + * For the common case where a component's stories are simple components that receives args as props: + * + * ```tsx + * export default { ... } as ComponentMeta; + * ``` + */ +export type ComponentMeta< + T extends keyof JSX.IntrinsicElements | JSXElementConstructor +> = Meta>; + +/** + * For the common case where a story is a simple component that receives args as props: + * + * ```tsx + * const Template: ComponentStory = (args) => -); +export const WithMarkdown = (props) => { + console.log('props111:', props); + return ( + + ) +}; WithMarkdown.storyName = 'with markdown'; +WithMarkdown.args = { + type: 'button', + text: 'bbb' +} diff --git a/examples/rax-kitchen-sink/src/stories/x-for.stories.js b/examples/rax-kitchen-sink/src/stories/x-for.stories.js new file mode 100644 index 0000000000..694d64e072 --- /dev/null +++ b/examples/rax-kitchen-sink/src/stories/x-for.stories.js @@ -0,0 +1,33 @@ +import { createElement } from 'rax'; +import { withKnobs } from '@storybook/addon-knobs'; + +import Text from 'rax-text'; +import View from 'rax-view'; + +export default { + title: 'Rax/x-for', + decorators: [withKnobs], +}; + +export const WithAButton = () => { + + const list = [ + { + name: 'test-1', + desc: 'desc-1' + }, + { + name: 'test-2', + desc: 'desc-2' + }, + ] + + return ( + + {/* @ts-ignore */} + {item.name}-{item.desc} + + ); +}; + +WithAButton.storyName = 'with a button'; \ No newline at end of file From c9495a415e245fa2cb1c9dc568e5ff2d88437980 Mon Sep 17 00:00:00 2001 From: "chuanbin.ccb" Date: Mon, 18 Jul 2022 16:37:56 +0800 Subject: [PATCH 2/2] feat: fix eslint --- app/rax/src/server/framework-preset-rax.ts | 71 ++++++++++++++----- app/rax/src/typings.d.ts | 1 + .../rax-kitchen-sink/src/stories/index.less | 4 ++ .../src/stories/x-for.stories.js | 16 +++-- package.json | 2 + 5 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 examples/rax-kitchen-sink/src/stories/index.less diff --git a/app/rax/src/server/framework-preset-rax.ts b/app/rax/src/server/framework-preset-rax.ts index 538dd7ad5f..aa1ca99f40 100644 --- a/app/rax/src/server/framework-preset-rax.ts +++ b/app/rax/src/server/framework-preset-rax.ts @@ -1,4 +1,7 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { TransformOptions } from '@babel/core'; +import { Configuration } from 'webpack'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; export function babelDefault(config: TransformOptions) { return { @@ -12,24 +15,6 @@ export function babelDefault(config: TransformOptions) { ], plugins: [ ...config.plugins, - [ - '@babel/plugin-transform-runtime', - { - corejs: false, - helpers: true, - regenerator: true, - useESModules: false, - }, - ], - [ - '@babel/plugin-transform-typescript', - { - jsxPragma: 'createElement', - jsxPragmaFrag: 'Fragment', - isTSX: true, - }, - ], - '@babel/plugin-syntax-jsx', ['@babel/plugin-transform-react-jsx', { pragma: 'createElement' }], 'babel-plugin-transform-jsx-list', 'babel-plugin-transform-jsx-condition', @@ -41,3 +26,53 @@ export function babelDefault(config: TransformOptions) { ], }; } + +export function webpackFinal(config: Configuration) { + const cssRuleIndex = config.module.rules.findIndex( + (rule) => rule.test.toString() === '/\\.css$/' + ); + if (cssRuleIndex) { + config.module.rules.splice(cssRuleIndex, 1); + } + + const stylesLoaders = [ + 'css-loader', + 'postcss-loader', + { + loader: 'webpack-rpx', + options: { + width: 750, + unit: 'rpx', + }, + }, + ]; + + const lessLoaders = [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'less-loader', + 'postcss-loader', + { + loader: 'webpack-rpx', + options: { + width: 750, + unit: 'rpx', + }, + }, + ]; + + config.module.rules.push({ + test: /\.css$/, + use: stylesLoaders, + }); + + config.module.rules.push({ + test: /\.less$/, + use: lessLoaders, + }); + + return { + ...config, + plugins: [...config.plugins, new MiniCssExtractPlugin()], + }; +} diff --git a/app/rax/src/typings.d.ts b/app/rax/src/typings.d.ts index 2f4eb9cf4f..c93e316952 100644 --- a/app/rax/src/typings.d.ts +++ b/app/rax/src/typings.d.ts @@ -1 +1,2 @@ declare module 'global'; +declare module 'mini-css-extract-plugin' \ No newline at end of file diff --git a/examples/rax-kitchen-sink/src/stories/index.less b/examples/rax-kitchen-sink/src/stories/index.less new file mode 100644 index 0000000000..79587e85f4 --- /dev/null +++ b/examples/rax-kitchen-sink/src/stories/index.less @@ -0,0 +1,4 @@ +.text { + display: block; + width: 100%; +} \ No newline at end of file diff --git a/examples/rax-kitchen-sink/src/stories/x-for.stories.js b/examples/rax-kitchen-sink/src/stories/x-for.stories.js index 694d64e072..455eeb45e9 100644 --- a/examples/rax-kitchen-sink/src/stories/x-for.stories.js +++ b/examples/rax-kitchen-sink/src/stories/x-for.stories.js @@ -1,5 +1,7 @@ +/* eslint-disable no-undef */ import { createElement } from 'rax'; import { withKnobs } from '@storybook/addon-knobs'; +import './index.less'; import Text from 'rax-text'; import View from 'rax-view'; @@ -10,24 +12,24 @@ export default { }; export const WithAButton = () => { - const list = [ { name: 'test-1', - desc: 'desc-1' + desc: 'desc-1', }, { name: 'test-2', - desc: 'desc-2' + desc: 'desc-2', }, - ] + ]; return ( - {/* @ts-ignore */} - {item.name}-{item.desc} + + {item.name}-{item.desc} + ); }; -WithAButton.storyName = 'with a button'; \ No newline at end of file +WithAButton.storyName = 'with a button'; diff --git a/package.json b/package.json index b02d47c1c9..05fb80fc59 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "lerna": "^3.22.1", "lint-staged": "^10.5.4", "lodash": "^4.17.20", + "mini-css-extract-plugin": "^0.9.0", "mocha-list-tests": "^1.0.5", "node-cleanup": "^2.1.2", "node-fetch": "^2.6.1", @@ -223,6 +224,7 @@ "web-component-analyzer": "^1.1.6", "webpack": "4", "webpack-dev-middleware": "^3.7.3", + "webpack-rpx": "^1.0.0", "window-size": "^1.1.1" }, "optionalDependencies": {