From e6c231c887a513752197d5a712de5dfc485b8896 Mon Sep 17 00:00:00 2001 From: liaoxuezhi <2betop.cn@gmail.com> Date: Thu, 20 Jul 2023 20:32:55 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20api=20=E5=8F=91=E9=80=81=E9=80=82?= =?UTF-8?q?=E9=85=8D=E5=99=A8=E6=94=AF=E6=8C=81=E5=BC=82=E6=AD=A5=20(#7525?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/amis-core/src/types.ts | 5 +- packages/amis-core/src/utils/api.ts | 12 ++-- packages/amis/__tests__/utils/api.test.ts | 73 ++++++++++++++++++++++- 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/packages/amis-core/src/types.ts b/packages/amis-core/src/types.ts index cf4ea4a52..bef0d5dc7 100644 --- a/packages/amis-core/src/types.ts +++ b/packages/amis-core/src/types.ts @@ -223,7 +223,10 @@ export interface ApiObject extends BaseApiObject { api: ApiObject, context: any ) => any; - requestAdaptor?: (api: ApiObject, context: any) => ApiObject; + requestAdaptor?: ( + api: ApiObject, + context: any + ) => ApiObject | Promise; /** 是否过滤为空字符串的 query 参数 */ filterEmptyQuery?: boolean; } diff --git a/packages/amis-core/src/utils/api.ts b/packages/amis-core/src/utils/api.ts index f8da005b0..984678ebe 100644 --- a/packages/amis-core/src/utils/api.ts +++ b/packages/amis-core/src/utils/api.ts @@ -76,7 +76,7 @@ export function buildApi( } if (api.requestAdaptor && typeof api.requestAdaptor === 'string') { - api.requestAdaptor = str2function( + api.requestAdaptor = str2AsyncFunction( api.requestAdaptor, 'api', 'context' @@ -84,7 +84,7 @@ export function buildApi( } if (api.adaptor && typeof api.adaptor === 'string') { - api.adaptor = str2function( + api.adaptor = str2AsyncFunction( api.adaptor, 'payload', 'response', @@ -464,12 +464,16 @@ export function wrapFetcher( return fn as any; } - const wrappedFetcher = function (api: Api, data: object, options?: object) { + const wrappedFetcher = async function ( + api: Api, + data: object, + options?: object + ) { api = buildApi(api, data, options) as ApiObject; if (api.requestAdaptor) { debug('api', 'before requestAdaptor', api); - api = api.requestAdaptor(api, data) || api; + api = (await api.requestAdaptor(api, data)) || api; debug('api', 'after requestAdaptor', api); } diff --git a/packages/amis/__tests__/utils/api.test.ts b/packages/amis/__tests__/utils/api.test.ts index cbd7ce0d2..fc5d1fffc 100644 --- a/packages/amis/__tests__/utils/api.test.ts +++ b/packages/amis/__tests__/utils/api.test.ts @@ -1,6 +1,6 @@ import {render as amisRender} from '../../src'; import {wait, makeEnv} from '../helper'; -import {render, fireEvent, cleanup} from '@testing-library/react'; +import {render, fireEvent, cleanup, waitFor} from '@testing-library/react'; import {buildApi, isApiOutdated, isValidApi} from 'amis-core'; test('api:buildApi', () => { @@ -358,3 +358,74 @@ test('api:isvalidapi', () => { ) ).toBeTruthy(); }); + +test('api:requestAdaptor', async () => { + const notify = jest.fn(); + const fetcher = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + status: 0, + msg: 'ok', + data: { + id: 1 + } + } + }) + ); + const requestAdaptor = jest.fn().mockImplementation(api => { + return Promise.resolve({ + ...api, + data: { + ...api.data, + email: 'appended@test.com' + } + }); + }); + + const {container, getByText} = render( + amisRender( + { + type: 'page', + body: [ + { + type: 'form', + id: 'form_submit', + submitText: '提交表单', + api: { + method: 'post', + url: '/api/mock2/form/saveForm', + requestAdaptor: requestAdaptor + }, + body: [ + { + type: 'input-text', + name: 'name', + label: '姓名:', + value: 'fex' + } + ] + } + ] + }, + {}, + makeEnv({ + notify, + fetcher + }) + ) + ); + + await waitFor(() => { + expect(getByText('提交表单')).toBeInTheDocument(); + }); + + fireEvent.click(getByText(/提交表单/)); + await wait(300); + + expect(requestAdaptor).toHaveBeenCalled(); + expect(fetcher).toHaveBeenCalled(); + expect(fetcher.mock.calls[0][0].data).toMatchObject({ + name: 'fex', + email: 'appended@test.com' + }); +}); From 2667ce67f0ae7f0c29493f0dc6e4d63931043c61 Mon Sep 17 00:00:00 2001 From: liaoxuezhi <2betop.cn@gmail.com> Date: Thu, 20 Jul 2023 20:38:57 +0800 Subject: [PATCH 2/4] =?UTF-8?q?chore:=20=E8=B0=83=E6=95=B4=20debug=20?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=20=E8=A1=A5=E5=85=85=E9=94=80=E6=AF=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91,=20=E9=80=82=E9=85=8D=20react=2018=20(#7535)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/amis-core/src/index.tsx | 24 +++--- packages/amis-core/src/utils/debug.tsx | 80 +++++++++++++------ packages/amis-ui/scss/components/_debug.scss | 35 +++++++- packages/amis-ui/scss/components/_drawer.scss | 11 ++- packages/amis-ui/src/components/Alert.tsx | 39 ++++++--- .../amis-ui/src/components/ContextMenu.tsx | 19 ++++- .../amis/__tests__/renderers/Action.test.tsx | 18 +++-- packages/amis/src/renderers/Dialog.tsx | 4 + packages/amis/src/renderers/Drawer.tsx | 3 +- .../amis/src/renderers/Table/TableBody.tsx | 2 +- 10 files changed, 177 insertions(+), 58 deletions(-) diff --git a/packages/amis-core/src/index.tsx b/packages/amis-core/src/index.tsx index aac8a827c..4c9fdcace 100644 --- a/packages/amis-core/src/index.tsx +++ b/packages/amis-core/src/index.tsx @@ -98,7 +98,13 @@ import Overlay from './components/Overlay'; import PopOver from './components/PopOver'; import {FormRenderer} from './renderers/Form'; import type {FormHorizontal, FormSchemaBase} from './renderers/Form'; -import {enableDebug, promisify, replaceText, wrapFetcher} from './utils/index'; +import { + enableDebug, + disableDebug, + promisify, + replaceText, + wrapFetcher +} from './utils/index'; import type {OnEventProps} from './utils/index'; import {valueMap as styleMap} from './utils/style-helper'; import {RENDERER_TRANSMISSION_OMIT_PROPS} from './SchemaRenderer'; @@ -195,7 +201,9 @@ export { OnEventProps, FormSchemaBase, filterTarget, - CustomStyle + CustomStyle, + enableDebug, + disableDebug }; export function render( @@ -257,13 +265,6 @@ function AMISRenderer({ translate } as any; - if (options.enableAMISDebug) { - // 因为里面还有 render - setTimeout(() => { - enableDebug(); - }, 10); - } - store = RendererStore.create({}, options); stores[options.session || 'global'] = store; } else { @@ -291,6 +292,11 @@ function AMISRenderer({ } env.theme = getTheme(theme); + React.useEffect(() => { + env.enableAMISDebug ? enableDebug() : disableDebug(); + return () => env.enableAMISDebug || disableDebug(); + }, [env.enableAMISDebug]); + if (props.locale !== undefined) { env.translate = translate; env.locale = locale; diff --git a/packages/amis-core/src/utils/debug.tsx b/packages/amis-core/src/utils/debug.tsx index 599383606..96813ed51 100644 --- a/packages/amis-core/src/utils/debug.tsx +++ b/packages/amis-core/src/utils/debug.tsx @@ -2,9 +2,10 @@ * amis 运行时调试功能,为了避免循环引用,这个组件不要依赖 amis 里的组件 */ -import React, {Component, useEffect, useRef, useState} from 'react'; +import React, {Component, useEffect, useRef, useState, version} from 'react'; import cx from 'classnames'; -import {findDOMNode, render} from 'react-dom'; +import {findDOMNode, render, unmountComponentAtNode} from 'react-dom'; +import {createRoot} from 'react-dom/client'; import {autorun, observable} from 'mobx'; import {observer} from 'mobx-react'; import {uuidv4} from './helper'; @@ -84,16 +85,18 @@ const LogView = observer(({store}: {store: AMISDebugStore}) => { [{log.cat}] {log.msg} {log.ext ? ( - + Loading...}> + + ) : null} ); @@ -126,16 +129,18 @@ const AMISDebug = observer(({store}: {store: AMISDebugStore}) => { stackDataView.push(

Data Level-{level}

- + Loading...
}> + + ); level += 1; @@ -319,7 +324,7 @@ function handleMouseclick(e: MouseEvent) { } const dom = e.target as HTMLElement; const target = dom.closest(`[data-debug-id]`); - if (target) { + if (target && !target.closest('.AMISDebug')) { store.activeId = target.getAttribute('data-debug-id')!; store.tab = 'inspect'; } @@ -366,6 +371,7 @@ autorun(() => { // 页面中只能有一个实例 let isEnabled = false; +let unmount: () => void; export function enableDebug() { if (isEnabled) { @@ -376,7 +382,21 @@ export function enableDebug() { const amisDebugElement = document.createElement('div'); document.body.appendChild(amisDebugElement); const element = ; - render(element, amisDebugElement); + + if (parseInt(version.split('.')[0], 10) >= 18) { + const root = createRoot(amisDebugElement); + root.render(element); + unmount = () => { + root.unmount(); + document.body.removeChild(amisDebugElement); + }; + } else { + render(element, amisDebugElement); + unmount = () => { + unmountComponentAtNode(amisDebugElement); + document.body.removeChild(amisDebugElement); + }; + } document.body.appendChild(amisHoverBox); document.body.appendChild(amisActiveBox); @@ -384,6 +404,18 @@ export function enableDebug() { document.addEventListener('click', handleMouseclick); } +export function disableDebug() { + if (!isEnabled) { + return; + } + isEnabled = false; + unmount?.(); + document.body.removeChild(amisHoverBox); + document.body.removeChild(amisActiveBox); + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('click', handleMouseclick); +} + interface DebugWrapperProps { renderer: any; children?: React.ReactNode; diff --git a/packages/amis-ui/scss/components/_debug.scss b/packages/amis-ui/scss/components/_debug.scss index 3a7894f63..14dab782a 100644 --- a/packages/amis-ui/scss/components/_debug.scss +++ b/packages/amis-ui/scss/components/_debug.scss @@ -40,6 +40,7 @@ &-tab { overflow: hidden; + border-bottom: 1px solid #3d3d3d; } &-tab > button { @@ -90,6 +91,7 @@ &-content { pointer-events: all; display: none; + height: 100%; } &-resize { @@ -122,7 +124,7 @@ &.is-expanded { width: 420px; - overflow: auto; + background: #272821; color: #cccccc; box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; @@ -168,6 +170,37 @@ padding: var(--gap-sm); } + &-log, + &-inspect { + height: 100%; + overflow: auto; + + // 火狐浏览器 + scrollbar-width: thin; + scrollbar-color: #6b6b6b #2b2b2b; + &::-webkit-scrollbar { + position: relative; + z-index: 10; + background-color: #2c2c2c; + width: 16px; + height: 16px; + border-left: 1px solid #3d3d3d; + // border-top: 1px solid #3d3d3d; + } + + &::-webkit-scrollbar-thumb { + background: #6b6b6b; + background-clip: content-box; + border: 4px solid transparent; + border-radius: 500px; + + &:hover { + background: #939393; + background-clip: content-box; + } + } + } + &-logLine { overflow-x: hidden; } diff --git a/packages/amis-ui/scss/components/_drawer.scss b/packages/amis-ui/scss/components/_drawer.scss index 491ba052a..7df8e2690 100644 --- a/packages/amis-ui/scss/components/_drawer.scss +++ b/packages/amis-ui/scss/components/_drawer.scss @@ -89,11 +89,20 @@ } &-body { - padding: var(--drawer-content-paddingTop) var(--drawer-content-paddingRight) + padding: 0 var(--drawer-content-paddingRight) var(--drawer-content-paddingBottom) var(--drawer-content-paddingLeft); flex-basis: 0; flex-grow: 1; overflow: auto; + + // 因为如果成员里面有 position:sticky 的内容 + // 用 padding 会导致位置不正确 + // 所以改成这种写法 + &:before { + content: ''; + display: block; + height: var(--drawer-content-paddingTop); + } } &-footer { diff --git a/packages/amis-ui/src/components/Alert.tsx b/packages/amis-ui/src/components/Alert.tsx index 958850415..5305a3afc 100644 --- a/packages/amis-ui/src/components/Alert.tsx +++ b/packages/amis-ui/src/components/Alert.tsx @@ -3,7 +3,7 @@ * @author fex */ -import React from 'react'; +import React, {version} from 'react'; import {render} from 'react-dom'; import Modal from './Modal'; import Button from './Button'; @@ -11,6 +11,7 @@ import {ClassNamesFn, themeable, ThemeProps} from 'amis-core'; import {LocaleProps, localeable} from 'amis-core'; import Html from './Html'; import type {PlainObject} from 'amis-core'; +import {createRoot} from 'react-dom/client'; export interface AlertProps extends ThemeProps, LocaleProps { container?: any; confirmText?: string; @@ -52,13 +53,21 @@ export interface AlertState { export class Alert extends React.Component { static instance: any = null; - static getInstance() { + static async getInstance() { if (!Alert.instance) { console.warn('Alert 组件应该没有被渲染,所以隐性的渲染到 body 了'); const container = document.body; const div = document.createElement('div'); container.appendChild(div); - render(, div); + + if (parseInt(version.split('.')[0], 10) >= 18) { + const root = createRoot(div); + await new Promise(resolve => + root.render( resolve()} />) + ); + } else { + render(, div); + } } return Alert.instance; @@ -346,23 +355,35 @@ function renderForm( return renderSchemaFn?.(controls, value, callback, scopeRef, theme); } -export const alert: (content: string, title?: string) => void = ( +export const alert: (content: string, title?: string) => Promise = async ( content, title -) => Alert.getInstance().alert(content, title); +) => { + const instance = await Alert.getInstance(); + return instance.alert(content, title); +}; export const confirm: ( content: string | React.ReactNode, title?: string, optionsOrCofnrimText?: string | ConfirmOptions, cancelText?: string -) => Promise = (content, title, optionsOrCofnrimText, cancelText) => - Alert.getInstance().confirm(content, title, optionsOrCofnrimText, cancelText); +) => Promise = async ( + content, + title, + optionsOrCofnrimText, + cancelText +) => { + const instance = await Alert.getInstance(); + return instance.confirm(content, title, optionsOrCofnrimText, cancelText); +}; export const prompt: ( controls: any, defaultvalue?: any, title?: string, confirmText?: string -) => Promise = (controls, defaultvalue, title, confirmText) => - Alert.getInstance().prompt(controls, defaultvalue, title, confirmText); +) => Promise = async (controls, defaultvalue, title, confirmText) => { + const instance = await Alert.getInstance(); + return instance.prompt(controls, defaultvalue, title, confirmText); +}; export const FinnalAlert = themeable(localeable(Alert)); export default FinnalAlert; diff --git a/packages/amis-ui/src/components/ContextMenu.tsx b/packages/amis-ui/src/components/ContextMenu.tsx index d6eb21746..dd6dfde78 100644 --- a/packages/amis-ui/src/components/ContextMenu.tsx +++ b/packages/amis-ui/src/components/ContextMenu.tsx @@ -1,5 +1,5 @@ import {ClassNamesFn, themeable} from 'amis-core'; -import React from 'react'; +import React, {version} from 'react'; import {render} from 'react-dom'; import {autobind, calculatePosition} from 'amis-core'; import Transition, { @@ -7,6 +7,7 @@ import Transition, { ENTERING, EXITING } from 'react-transition-group/Transition'; +import {createRoot} from 'react-dom/client'; const fadeStyles: { [propName: string]: string; } = { @@ -49,12 +50,20 @@ export class ContextMenu extends React.Component< ContextMenuState > { static instance: any = null; - static getInstance() { + static async getInstance() { if (!ContextMenu.instance) { const container = document.body; const div = document.createElement('div'); container.appendChild(div); - render(, div); + + if (parseInt(version.split('.')[0], 10) >= 18) { + const root = createRoot(div); + await new Promise(resolve => + root.render( resolve()} />) + ); + } else { + render(, div); + } } return ContextMenu.instance; @@ -309,5 +318,7 @@ export function openContextMenus( menus: Array, onClose?: () => void ) { - return ContextMenu.getInstance().openContextMenus(info, menus, onClose); + return ContextMenu.getInstance().then(instance => + instance.openContextMenus(info, menus, onClose) + ); } diff --git a/packages/amis/__tests__/renderers/Action.test.tsx b/packages/amis/__tests__/renderers/Action.test.tsx index 62cf2c0e3..a95e78831 100644 --- a/packages/amis/__tests__/renderers/Action.test.tsx +++ b/packages/amis/__tests__/renderers/Action.test.tsx @@ -342,7 +342,7 @@ test('Renderers:Action tooltip', async () => { // }); // 14. confirmText -test('Renderers:Action with confirmText & actionType ajax', () => { +test('Renderers:Action with confirmText & actionType ajax', async () => { const fetcher = jest.fn().mockImplementation(() => Promise.resolve({ data: { @@ -372,7 +372,7 @@ test('Renderers:Action with confirmText & actionType ajax', () => { ) ); fireEvent.click(container.querySelector('.cxd-Button')); - wait(500); + await wait(500); expect(baseElement).toMatchSnapshot(); expect(baseElement.querySelector('.cxd-Modal-content')!).toHaveTextContent( @@ -380,14 +380,16 @@ test('Renderers:Action with confirmText & actionType ajax', () => { ); fireEvent.click(getByText('取消')); - wait(500); + await wait(500); expect(fetcher).not.toBeCalled(); - // fireEvent.click(container.querySelector('.cxd-Button')); - // wait(500); - // fireEvent.click(getByText('确认')); - // fetcher 不生效 - // expect(fetcher).toBeCalled(); + fireEvent.click(container.querySelector('.cxd-Button')); + await wait(500); + fireEvent.click(getByText('确认')); + + await wait(200); + // fetcher 该被执行了 + expect(fetcher).toBeCalled(); }); // 15.Action 作为容器组件 diff --git a/packages/amis/src/renderers/Dialog.tsx b/packages/amis/src/renderers/Dialog.tsx index 3f0b0767e..dcee5790e 100644 --- a/packages/amis/src/renderers/Dialog.tsx +++ b/packages/amis/src/renderers/Dialog.tsx @@ -465,6 +465,10 @@ export default class Dialog extends React.Component { syncLocation: false // 弹框中的 crud 一般不需要同步地址栏 }; + if (this.props.size === 'full') { + subProps.affixOffsetTop = 0; + } + if (!(body as Schema).type) { return render(`body${key ? `/${key}` : ''}`, body, subProps); } diff --git a/packages/amis/src/renderers/Drawer.tsx b/packages/amis/src/renderers/Drawer.tsx index 838a00cc2..0650c03b2 100644 --- a/packages/amis/src/renderers/Drawer.tsx +++ b/packages/amis/src/renderers/Drawer.tsx @@ -465,7 +465,8 @@ export default class Drawer extends React.Component { onInit: this.handleFormInit, onSaved: this.handleFormSaved, onActionSensor: this.handleActionSensor, - syncLocation: false + syncLocation: false, + affixOffsetTop: 0 }; if (schema.type === 'form') { diff --git a/packages/amis/src/renderers/Table/TableBody.tsx b/packages/amis/src/renderers/Table/TableBody.tsx index 59584edc7..18a9474ed 100644 --- a/packages/amis/src/renderers/Table/TableBody.tsx +++ b/packages/amis/src/renderers/Table/TableBody.tsx @@ -272,7 +272,7 @@ export class TableBody extends React.Component { return ( {render(`summary-row/${index}`, item, { From 4d2e2b76eae4e774c89c7c1e247da6e4786a7ee2 Mon Sep 17 00:00:00 2001 From: wanglinfang Date: Thu, 20 Jul 2023 19:39:14 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20nav=E5=9B=BE=E6=A0=87=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E9=97=AE=E9=A2=98=E3=80=81=E8=8F=9C=E5=8D=95=E6=B5=AE?= =?UTF-8?q?=E5=B1=82=E6=94=AF=E6=8C=81=E6=8C=87=E5=AE=9A=E6=8C=82=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/amis-ui/scss/components/_menu.scss | 1 + .../amis/__tests__/renderers/Nav.test.tsx | 178 ++++++++++++++ .../renderers/__snapshots__/Nav.test.tsx.snap | 222 ++++++++++++++++++ packages/amis/src/renderers/Nav.tsx | 79 ++++--- 4 files changed, 446 insertions(+), 34 deletions(-) diff --git a/packages/amis-ui/scss/components/_menu.scss b/packages/amis-ui/scss/components/_menu.scss index 0d1fe71bb..3e3741f65 100644 --- a/packages/amis-ui/scss/components/_menu.scss +++ b/packages/amis-ui/scss/components/_menu.scss @@ -350,6 +350,7 @@ &-submenu-title { display: flex; + align-items: center; justify-content: space-between; .#{$ns}Nav-Menu-item-wrap { diff --git a/packages/amis/__tests__/renderers/Nav.test.tsx b/packages/amis/__tests__/renderers/Nav.test.tsx index 0e7f76fcc..54cfeb5b6 100644 --- a/packages/amis/__tests__/renderers/Nav.test.tsx +++ b/packages/amis/__tests__/renderers/Nav.test.tsx @@ -517,3 +517,181 @@ test('Renderer:Nav with itemActions', async () => { expect(container).toMatchSnapshot(); expect(getByText('编辑')).toBeInTheDocument(); }); + +// 8.各种图标展示 +test('Renderer:Nav with icons', async () => { + const {container} = render( + amisRender( + { + type: 'page', + body: { + type: 'nav', + stacked: true, + links: [ + { + label: 'Nav 1', + to: '?cat=1', + value: '1', + icon: 'fa fa-user', + __id: 1 + }, + { + label: 'Nav 2', + __id: 2, + unfolded: true, + children: [ + { + __id: 2.1, + label: 'Nav 2-1', + icon: [ + { + icon: 'star', + position: 'before' + }, + { + icon: 'search', + position: 'before' + }, + { + icon: 'https://suda.cdn.bcebos.com/images%2F2021-01%2Fdiamond.svg', + position: 'after' + } + ], + children: [ + { + label: 'Nav 2-1-1', + to: '?cat=2-1', + value: '2-1', + __id: 2.11 + } + ] + } + ] + } + ] + } + }, + {}, + makeEnv({}) + ) + ); + expect(container).toMatchSnapshot(); + expect(container.querySelectorAll('.fa-user').length).toBe(1); + expect(container.querySelectorAll('[icon=search]').length).toBe(1); + expect(container.querySelectorAll('img').length).toBe(1); +}); + +// 9.Nav在Dialog里 +test('Renderer:Nav with Dialog', async () => { + const {container, getByText} = render( + amisRender( + { + type: 'page', + body: { + type: 'button', + label: '点击弹框', + actionType: 'dialog', + dialog: { + title: '弹框', + body: [ + { + type: 'nav', + stacked: true, + className: 'w-md', + draggable: true, + saveOrderApi: '/api/options/nav', + source: '/api/options/nav?parentId=${value}', + itemActions: [ + { + type: 'icon', + icon: 'cloud', + visibleOn: "this.to === '?cat=1'" + }, + { + type: 'dropdown-button', + level: 'link', + icon: 'fa fa-ellipsis-h', + hideCaret: true, + buttons: [ + { + type: 'button', + label: '编辑' + }, + { + type: 'button', + label: '删除' + } + ] + } + ], + links: [ + { + label: 'Nav 1', + to: '?cat=1', + value: '1', + icon: 'fa fa-user', + __id: 1 + }, + { + label: 'Nav 2', + __id: 2, + unfolded: true, + children: [ + { + __id: 2.1, + label: 'Nav 2-1', + children: [ + { + label: 'Nav 2-1-1', + to: '?cat=2-1', + value: '2-1', + __id: 2.11 + } + ] + }, + { + label: 'Nav 2-2', + to: '?cat=2-2', + value: '2-2', + __id: 2.2 + } + ] + }, + { + label: 'Nav 3', + to: '?cat=3', + value: '3', + defer: true, + __id: 3 + } + ] + } + ] + } + } + }, + {}, + makeEnv({ + getModalContainer: () => container + }) + ) + ); + expect(container).toMatchSnapshot(); + + fireEvent.click(getByText('点击弹框')); + await waitFor(() => { + expect(container.querySelector('[role="dialog"]')).toBeInTheDocument(); + }); + + fireEvent.click( + container.querySelector( + '[role="dialog"] .cxd-Nav-Menu-item-extra .cxd-Button' + )! + ); + + await waitFor(() => { + expect( + container.querySelector('[role="dialog"] .cxd-PopOver') + ).toBeInTheDocument(); + }); +}); diff --git a/packages/amis/__tests__/renderers/__snapshots__/Nav.test.tsx.snap b/packages/amis/__tests__/renderers/__snapshots__/Nav.test.tsx.snap index ba8492e7f..a049512ab 100644 --- a/packages/amis/__tests__/renderers/__snapshots__/Nav.test.tsx.snap +++ b/packages/amis/__tests__/renderers/__snapshots__/Nav.test.tsx.snap @@ -208,6 +208,228 @@ exports[`Renderer:Nav 1`] = ` `; +exports[`Renderer:Nav with Dialog 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+`; + +exports[`Renderer:Nav with icons 1`] = ` +
+
+
+
+
+
+ + +
+
+
+
+
+
+`; + exports[`Renderer:Nav with itemActions 1`] = `
HTMLElement; } export interface IDropInfo { @@ -550,6 +550,8 @@ export class Navigation extends React.Component< mode, itemActions, render, + popOverContainer, + env, classnames: cx, data } = this.props; @@ -562,32 +564,28 @@ export class Navigation extends React.Component< } return links.map((link: Link) => { - let beforeIcon = null; - let afterIcon = null; - if (Array.isArray(link.icon)) { - beforeIcon = link.icon - .filter(item => item.position === 'before') - .map(item => { + const beforeIcon: Array = []; + const afterIcon: Array = []; + + link.icon && + (Array.isArray(link.icon) ? link.icon : [link.icon]).forEach( + (item, i) => { if (React.isValidElement(item)) { - return item; + beforeIcon.push(item); + } else if (isString(item)) { + beforeIcon.push(); + } else if (item && isObject(item)) { + const icon = ( + + ); + if (item['position'] === 'after') { + afterIcon.push(icon); + } else { + beforeIcon.push(icon); + } } - return ; - }); - afterIcon = link.icon - .filter(item => item.position === 'after') - .map(item => { - if (React.isValidElement(item)) { - return item; - } - return ; - }); - } else if (link.icon) { - if (React.isValidElement(link.icon)) { - beforeIcon = link.icon; - } else { - beforeIcon = ; - } - } + } + ); const label = typeof link.label === 'string' @@ -642,10 +640,10 @@ export class Navigation extends React.Component< return { link, label, - labelExtra: afterIcon ? ( + labelExtra: afterIcon.length ? ( {afterIcon} ) : null, - icon: beforeIcon ? {beforeIcon} : null, + icon: beforeIcon.length ? {beforeIcon} : null, children: children ? this.normalizeNavigations(children, depth + 1) : [], @@ -654,7 +652,11 @@ export class Navigation extends React.Component< extra: itemActions ? render('inline', itemActions, { data: createObject(data, link), - popOverContainer: () => document.body, + popOverContainer: popOverContainer + ? popOverContainer + : env.getModalContainer + ? env.getModalContainer + : () => document.body, // 点击操作之后 就关闭 因为close方法里执行了preventDefault closeOnClick: true }) @@ -693,7 +695,9 @@ export class Navigation extends React.Component< popupClassName, disabled, id, - render + render, + popOverContainer, + env } = this.props; const {dropIndicator} = this.state; @@ -796,6 +800,13 @@ export class Navigation extends React.Component< data={data} disabled={disabled} onDragStart={this.handleDragStart} + popOverContainer={ + popOverContainer + ? popOverContainer + : env.getModalContainer + ? env.getModalContainer + : () => document.body + } > ) : null} From 4bb0c71dd1863056dd70864512a787ef1a9f3c5f Mon Sep 17 00:00:00 2001 From: RUNZE LU <36724300+lurunze1226@users.noreply.github.com> Date: Fri, 21 Jul 2023 08:55:52 +0800 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20InputTable=E5=8D=95=E6=B5=8B?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E4=BF=AE=E5=A4=8D=20(#7537)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/renderers/Form/inputTable.test.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/amis/__tests__/renderers/Form/inputTable.test.tsx b/packages/amis/__tests__/renderers/Form/inputTable.test.tsx index f0a716152..01967bb4c 100644 --- a/packages/amis/__tests__/renderers/Form/inputTable.test.tsx +++ b/packages/amis/__tests__/renderers/Form/inputTable.test.tsx @@ -136,13 +136,15 @@ test('Renderer: input-table with default value column', async () => { await wait(200); expect(onSubmitCallbackFn).toHaveBeenCalledTimes(1); - expect(onSubmitCallbackFn.mock.calls[0][0]).toEqual({ - table: [ - {a: 'a1', b: 'b1', c: 'a1'}, - {a: 'a2', b: 'b2', c: 'a2'}, - {a: 'a3', b: 'b3', c: 'a3'} - ] - }); + expect(onSubmitCallbackFn.mock.calls[0][0]).toEqual( + expect.objectContaining({ + table: [ + {a: 'a1', b: 'b1', c: 'a1'}, + {a: 'a2', b: 'b2', c: 'a2'}, + {a: 'a3', b: 'b3', c: 'a3'} + ] + }) + ); }, 10000); test('Renderer:input table add', async () => {