parent
5ef6dfb588
commit
3b3bf8e5d6
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
* openInula is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
import { FunctionComponent, Props } from '../type';
|
||||
import { insert } from '../dom';
|
||||
import { splitProps } from '../util';
|
||||
|
||||
type DynamicProps<P = Props> = {
|
||||
// it can be a native element tag name or a component
|
||||
component: string | FunctionComponent<P>;
|
||||
} & P;
|
||||
|
||||
/**
|
||||
* Dynamic component that can be used to switch between different components or native elements
|
||||
* @example
|
||||
* const comp = reactive('h1');
|
||||
* <Dynamic component={comp.get()}>Title</Dynamic>
|
||||
*/
|
||||
export function Dynamic(props: DynamicProps) {
|
||||
return () => {
|
||||
if (typeof props.component === 'string') {
|
||||
const el = document.createElement(props.component);
|
||||
insert(el, props.children);
|
||||
return el;
|
||||
} else if (typeof props.component === 'function') {
|
||||
// remove component from props, and pass the rest to the component
|
||||
const [, compProps] = splitProps(props, ['component']);
|
||||
return props.component(compProps);
|
||||
} else {
|
||||
throw new Error('Invalid component for Dynamic');
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
* openInula is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck For the compiled code.
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { domTest as it } from './utils';
|
||||
import { template as $$template, insert as $$insert } from '../src/dom';
|
||||
import { runComponent as $$runComponent, render } from '../src/core';
|
||||
import { Dynamic } from '../src/components/Dynamic';
|
||||
import { reactive } from 'inula-reactive';
|
||||
|
||||
describe('Dynamic', () => {
|
||||
it('should work with native elements.', ({ container }) => {
|
||||
/**
|
||||
* 源码:
|
||||
* function App() {
|
||||
* return <Dynamic component="h1">foo</Dynamic>;
|
||||
* }
|
||||
* render(() => <App />, container);
|
||||
*/
|
||||
|
||||
// 编译后:
|
||||
function App() {
|
||||
return $$runComponent(Dynamic, {
|
||||
component: 'h1',
|
||||
children: 'foo',
|
||||
});
|
||||
}
|
||||
|
||||
render(() => $$runComponent(App, {}), container);
|
||||
expect(container).toMatchInlineSnapshot(`
|
||||
<div>
|
||||
<h1>
|
||||
foo
|
||||
</h1>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
|
||||
it('should work with components.', ({ container }) => {
|
||||
/**
|
||||
* 源码:
|
||||
* function App() {
|
||||
* return <Dynamic component={Title} name="bar"/>;
|
||||
* }
|
||||
* function Title(props) {
|
||||
* return <h1>{props.name}</h1>;
|
||||
* }
|
||||
* render(() => <App />, container);
|
||||
*/
|
||||
|
||||
// 编译后:
|
||||
function App() {
|
||||
return $$runComponent(Dynamic, {
|
||||
component: Title,
|
||||
name: 'bar',
|
||||
});
|
||||
}
|
||||
|
||||
const _tmpl = /*#__PURE__*/ $$template('<h1>');
|
||||
|
||||
function Title(props) {
|
||||
return (() => {
|
||||
const _el$ = _tmpl();
|
||||
$$insert(_el$, () => props.name, null);
|
||||
return _el$;
|
||||
})();
|
||||
}
|
||||
|
||||
render(() => $$runComponent(App, {}), container);
|
||||
expect(container).toMatchInlineSnapshot(`
|
||||
<div>
|
||||
<h1>
|
||||
bar
|
||||
</h1>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
|
||||
it('should throw on invalid component.', ({ container }) => {
|
||||
/**
|
||||
* 源码:
|
||||
* function App() {
|
||||
* return <Dynamic component={null} />;
|
||||
* }
|
||||
* render(() => <App />, container);
|
||||
*/
|
||||
|
||||
// 编译后:
|
||||
function App() {
|
||||
return $$runComponent(Dynamic, {
|
||||
component: null,
|
||||
});
|
||||
}
|
||||
|
||||
expect(() => render(() => $$runComponent(App, {}), container)).toThrowError('Invalid component for Dynamic');
|
||||
});
|
||||
|
||||
it('should change component.', async ({ container }) => {
|
||||
/**
|
||||
* 源码:
|
||||
* const H1 = (props) => <h1>{props.children}</h1>;
|
||||
* const H3 = (props) => <h3>{props.children}</h3>;
|
||||
* function App() {
|
||||
* const comp = reactive('h1');
|
||||
* const comps = {
|
||||
* H1,
|
||||
* H3,
|
||||
* h1: 'h1',
|
||||
* h2: 'h2',
|
||||
* }
|
||||
* return (
|
||||
* <div>
|
||||
* <Dynamic component={comps[comp.get()]}>foo</Dynamic>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* render(() => <App />, container);
|
||||
*
|
||||
*/
|
||||
|
||||
// 编译后:
|
||||
const _tmpl$ = /*#__PURE__*/ $$template('<div></div>');
|
||||
const _h1 = /*#__PURE__*/ $$template('<h1>'),
|
||||
_h3 = /*#__PURE__*/ $$template('<h3>');
|
||||
const H1 = (props: { children: any }) => {
|
||||
const _el$ = _h1();
|
||||
$$insert(_el$, () => props.children);
|
||||
return _el$;
|
||||
};
|
||||
const H3 = (props: { children: any }) => {
|
||||
const _el$3 = _h3();
|
||||
$$insert(_el$3, () => props.children);
|
||||
return _el$3;
|
||||
};
|
||||
|
||||
const comp = reactive('h1');
|
||||
|
||||
function App() {
|
||||
const comps = {
|
||||
H1,
|
||||
H3,
|
||||
h1: 'h1',
|
||||
h2: 'h2',
|
||||
};
|
||||
return (() => {
|
||||
const _div = _tmpl$();
|
||||
$$insert(
|
||||
_div,
|
||||
$$runComponent(Dynamic, {
|
||||
get component() {
|
||||
return comps[comp.get()];
|
||||
},
|
||||
children: 'foo',
|
||||
})
|
||||
);
|
||||
return _div;
|
||||
})();
|
||||
}
|
||||
|
||||
render(() => $$runComponent(App, {}), container);
|
||||
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>foo</h1></div>"');
|
||||
comp.set('h2');
|
||||
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h2>foo</h2></div>"');
|
||||
comp.set('H3');
|
||||
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h3>foo</h3></div>"');
|
||||
comp.set('H1');
|
||||
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>foo</h1></div>"');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue