Merge branch 'baidu:master' into fix-affixHeader-table-hide-nested-table-header

This commit is contained in:
Song Mingxu 2023-08-14 09:16:33 +08:00 committed by GitHub
commit 522c393ae9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 636 additions and 220 deletions

View File

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2023 Baidu
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3149,7 +3149,7 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
| -------------- | ----------------------------------------------------------------------- | -------------------- |
| selectedChange | `selectedItems: item[]` 已选择行<br/>`unSelectedItems: item[]` 未选择行 | 手动选择表格项时触发 |
| columnSort | `orderBy: string` 列排序列名<br/>`orderDir: string` 列排序值 | 点击列排序时触发 |
| columnFilter | `filterName: string` 列筛选列名<br/>`filterValue: string` 列筛选值 | 点击列筛选时触发 |
| columnFilter | `filterName: string` 列筛选列名<br/>`filterValue: string \| undefined` 列筛选值 | 点击列筛选时触发,点击重置后事件参数`filterValue`为`undefined` |
| columnSearch | `searchName: string` 列搜索列名<br/>`searchValue: object` 列搜索数据 | 点击列搜索时触发 |
| orderChange | `movedItems: item[]` 已排序数据 | 手动拖拽行排序时触发 |
| columnToggled | `columns: item[]` 当前显示的列配置数据 | 点击自定义列时触发 |

View File

@ -156,6 +156,102 @@ IconSchema 配置
| -------- | ------------------------------------ | ---------------- |
| change | `[name]: string \| boolean` 组件的值 | 开关值变化时触发 |
### change
switch 值更新时弹出确认提示,确认后发送请求。
```schema: scope="body"
{
"type": "crud",
"syncLocation": false,
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",
"label": "ID",
"id": "u:daa79afa2e53"
},
{
"name": "engine",
"label": "Rendering engine",
"id": "u:3343cf518656"
},
{
"name": "browser",
"label": "Browser",
"id": "u:fbdc85e45e2f"
},
{
"name": "platform",
"label": "Platform(s)",
"id": "u:ccdb48cc1804"
},
{
"name": "switch",
"label": "开关",
"id": "u:30a36768acce",
"type": "switch",
"inline": true,
"onEvent": {
"change": {
"weight": 0,
"actions": [
{
"actionType": "confirmDialog",
"dialog": {
"type": "dialog",
"title": "弹框标题",
"body": [
{
"type": "tpl",
"tpl": "确定要修改${id}吗?",
"wrapperComponent": "",
"inline": false,
"id": "u:1965506c7599"
}
],
"showCloseButton": true,
"showErrorMsg": true,
"showLoading": true,
"className": "app-popover",
"id": "u:d9783223df98",
"actions": [
{
"type": "button",
"actionType": "cancel",
"label": "取消",
"id": "u:302efee8613b"
},
{
"type": "button",
"actionType": "confirm",
"label": "确定",
"primary": true,
"id": "u:4a4d63cf35e1"
}
]
}
},
{
"actionType": "ajax",
"outputVar": "responseResult",
"options": {
},
"api": {
"method": "get",
"url": "/api/mock2/form/saveForm"
}
}
]
}
},
"value": false
}
],
"id": "u:6c781a765f97"
}
```
## 动作表
当前组件对外暴露以下特性动作,其他组件可以通过指定`actionType: 动作名称`、`componentId: 该组件id`来触发这些动作,动作配置可以通过`args: {动作配置项名称: xxx}`来配置具体的参数,详细请查看[事件动作](../../docs/concepts/event-action#触发其他组件的动作)。
@ -163,3 +259,52 @@ IconSchema 配置
| 动作名称 | 动作配置 | 说明 |
| -------- | ------------------------------------- | -------- |
| setValue | `value: string \| boolean` 更新的数据 | 更新数据 |
### setValue
```schema: scope="body"
[
{
"type": "button",
"label": "修改开关的值",
"className": "mb-2",
"onEvent": {
"click": {
"actions": [
{
"componentId": "u:6613bfa3a18e",
"actionType": "setValue",
"args": {
"value": true
}
}
]
}
},
"id": "u:9d7d695145bb"
},
{
"type": "form",
"title": "表单",
"debug": true,
"body": [
{
"label": "开启",
"type": "switch",
"name": "switch",
"id": "u:6613bfa3a18e",
"value": false,
"mode": "inline"
}
],
"id": "u:82d44e407eb0",
"actions": [
{
"type": "submit",
"label": "提交",
"primary": true
}
]
}
]
```

View File

@ -175,6 +175,9 @@ let amisScoped = amis.embed(
// 用来判断是否目标地址当前地址。
// isCurrentUrl: url => {},
//
// 用来配置弹窗等组件的挂载位置
// getModalContainer: () => document.getElementsByTagName('body')[0],
//
// 用来实现复制到剪切板
// copy: content => {},
//
@ -533,6 +536,10 @@ class MyComponent extends React.Component<any, any> {
// // 地址替换,跟 jumpTo 类似
// },
// getModalContainer: () => {
// // 弹窗挂载的 DOM 节点
// },
// isCurrentUrl: (
// url: string /*url地址*/,
// ) => {

View File

@ -318,6 +318,7 @@ export const runAction = async (
// 二次确认弹窗如果取消,则终止后续动作
if (action?.actionType === 'confirmDialog' && !actionResult) {
stopped = true;
preventDefault = true; // 这种对表单项change比较有意义例如switch切换时弹确认弹窗如果取消后不能把switch修改了
}
let stopPropagation = false;

View File

@ -847,7 +847,9 @@ export default class Form extends React.Component<FormProps, object> {
this.hooks['validate'] || [],
forceValidate,
throwErrors,
__(filter(messages && messages.validateFailed, store.data))
typeof messages?.validateFailed === 'string'
? __(filter(messages.validateFailed, store.data))
: undefined
)
.then((result: boolean) => {
if (result) {
@ -892,7 +894,9 @@ export default class Form extends React.Component<FormProps, object> {
return store.submit(
fn,
this.hooks['validate'] || [],
__(filter(messages && messages.validateFailed, store.data)),
typeof messages?.validateFailed === 'string'
? __(filter(messages.validateFailed, store.data))
: undefined,
validateErrCb,
throwErrors
);
@ -1172,8 +1176,14 @@ export default class Form extends React.Component<FormProps, object> {
return store
.saveRemote(action.api || (api as Api), values, {
successMessage: filter(saveSuccess, store.data),
errorMessage: filter(saveFailed, store.data),
successMessage:
typeof saveSuccess === 'string'
? filter(saveSuccess, store.data)
: undefined,
errorMessage:
typeof saveFailed === 'string'
? filter(saveFailed, store.data)
: undefined,
onSuccess: async (result: Payload) => {
// result为提交接口返回的内容
const dispatcher = await dispatchEvent(
@ -1303,20 +1313,21 @@ export default class Form extends React.Component<FormProps, object> {
if (!isEffectiveApi(action.api)) {
return env.alert(__(`当 actionType 为 ajax 时,请设置 api 属性`));
}
let successMsg =
(action.messages && action.messages.success) || saveSuccess;
let failMsg = (action.messages && action.messages.failed) || saveFailed;
return store
.saveRemote(action.api as Api, data, {
successMessage: __(
filter(
(action.messages && action.messages.success) || saveSuccess,
store.data
)
typeof successMsg === 'string'
? filter(successMsg, store.data)
: undefined
),
errorMessage: __(
filter(
(action.messages && action.messages.failed) || saveFailed,
store.data
)
typeof failMsg === 'string'
? filter(failMsg, store.data)
: undefined
)
})
.then(async response => {

View File

@ -56,6 +56,7 @@
font-size: 12px;
color: var(--text--muted-color);
pointer-events: none;
padding: 10px;
}
&-footer {

View File

@ -2,164 +2,7 @@ import 'amis';
import './locale/index';
export * from 'amis-editor-core';
import './tpl/index';
// 布局容器
import './plugin/Flex'; // flex布局
import './plugin/Grid'; // 分栏
import './plugin/Container'; // 容器
import './plugin/Layout/Layout_free_container'; // 自由容器
import './plugin/Layout/Layout_sorption_container'; // 吸附容器
import './plugin/Layout/Layout_fixed'; // 悬浮容器
// import './plugin/Layout/Layout1_2_v4';
import './plugin/CollapseGroup'; // 折叠面板
import './plugin/Panel'; // 面板
import './plugin/Tabs'; // 选项卡
// 数据容器
import './plugin/CRUD'; // 增删改查
import './plugin/CRUD2';
import './plugin/Form/Form'; // 表单
import './plugin/Service'; // 服务service
// 表单项
import './plugin/Form/InputText'; // 文本框
import './plugin/Form/Textarea'; // 多行文本框
import './plugin/Form/InputNumber'; // 数字框
import './plugin/Form/Select'; // 下拉框
import './plugin/Form/NestedSelect'; // 级联选择器
import './plugin/Form/ChainedSelect'; // 链式下拉框
import './plugin/DropDownButton'; // 下拉按钮
import './plugin/Form/Checkboxes'; // 复选框
import './plugin/Form/Radios'; // 单选框
import './plugin/Form/Checkbox'; // 勾选框
import './plugin/Form/InputDate'; // 日期
import './plugin/Form/InputDateRange'; // 日期范围
import './plugin/Form/InputFile'; // 文件上传
import './plugin/Form/InputImage'; // 图片上传
import './plugin/Form/InputExcel'; // 上传 Excel
import './plugin/Form/InputTree'; // 树选择器
import './plugin/Form/InputTag'; // 标签选择器
import './plugin/Form/ListSelect'; // 列表选择
import './plugin/Form/ButtonGroupSelect'; // 按钮点选
import './plugin/Form/ButtonToolbar'; // 按钮工具栏
import './plugin/Form/Picker'; // 列表选取
import './plugin/Form/Switch'; // 开关
import './plugin/Form/InputRange'; // 滑块
import './plugin/Form/InputRating'; // 评分
import './plugin/Form/InputCity'; // 城市选择
import './plugin/Form/Transfer'; // 穿梭器
import './plugin/Form/TabsTransfer'; // 组合穿梭器
import './plugin/Form/InputColor'; // 颜色框
import './plugin/Form/ConditionBuilder'; // 条件组合
import './plugin/Form/FieldSet'; // 字段集
import './plugin/Form/Combo'; // 组合输入
import './plugin/Form/InputGroup'; // 输入组合
import './plugin/Form/InputTable'; // 表格编辑器
import './plugin/Form/MatrixCheckboxes'; // 矩阵开关
import './plugin/Form/InputRichText'; // 富文本编辑器
import './plugin/Form/DiffEditor'; // diff编辑器
import './plugin/Form/CodeEditor'; // 代码编辑器
import './plugin/SearchBox'; // 搜索框
import './plugin/Form/InputKV'; // KV键值对
import './plugin/Form/InputRepeat'; // 重复周期
import './plugin/Form/UUID'; // UUID
import './plugin/Form/LocationPicker'; // 地理位置
import './plugin/Form/InputSubForm'; // 子表单项
import './plugin/Form/Hidden'; // 隐藏域
// 功能
import './plugin/Button'; // 按钮
import './plugin/ButtonGroup'; // 按钮组
import './plugin/Nav'; // 导航
import './plugin/AnchorNav'; // 锚点导航
import './plugin/TooltipWrapper'; // 文字提示
import './plugin/Alert'; // 提示
import './plugin/Wizard'; // 向导
import './plugin/TableView'; // 表格视图
import './plugin/WebComponent';
import './plugin/Audio'; // 音频
import './plugin/Video'; // 视频
import './plugin/Custom'; // 自定义代码
import './plugin/Tasks'; // 异步任务
import './plugin/Each'; // 循环
import './plugin/Property'; // 属性表
import './plugin/IFrame';
import './plugin/QRCode'; // 二维码
// 展示
import './plugin/Tpl'; // 文字
import './plugin/Icon'; // 图标
import './plugin/Link'; // 链接
import './plugin/List'; // 列表
import './plugin/Mapping'; // 映射
import './plugin/Avatar'; // 头像
import './plugin/Card'; // 卡片
import './plugin/Card2';
import './plugin/Cards'; // 卡片列表
import './plugin/Table'; // 表格
import './plugin/Table2';
import './plugin/TableCell2'; // 列配置
import './plugin/Chart'; // 图表
import './plugin/Sparkline'; // 走势图
import './plugin/Carousel'; // 轮播图
import './plugin/Image'; // 图片展示
import './plugin/Images'; // 图片集
import './plugin/Time'; // 时间展示
import './plugin/Date'; // 日期展示
import './plugin/Datetime'; // 日期时间展示
import './plugin/Tag'; // 标签
import './plugin/Json'; // JSON展示
import './plugin/Progress'; // 进度展示
import './plugin/Status'; // 状态展示
import './plugin/Steps'; // 步骤条
import './plugin/Timeline'; // 时间轴
import './plugin/Divider'; // 分隔线
import './plugin/CodeView'; // 代码高亮
import './plugin/Markdown';
import './plugin/Collapse'; // 折叠器
import './plugin/OfficeViewer'; // 文档预览
import './plugin/Log'; // 日志
// 其他
import './plugin/Others/Action';
import './plugin/Others/TableCell';
import './plugin/Form/InputArray';
import './plugin/Form/ConditionBuilder';
import './plugin/Form/Control';
import './plugin/Form/InputDateTime';
import './plugin/Form/InputDateTimeRange';
import './plugin/Form/InputEmail';
import './plugin/Form/Formula';
import './plugin/Form/Group';
import './plugin/Form/Item';
import './plugin/Form/InputMonth';
import './plugin/Form/InputMonthRange';
import './plugin/Form/InputPassword';
import './plugin/Form/InputQuarter';
import './plugin/Form/InputQuarterRange';
import './plugin/Form/Static';
import './plugin/Form/InputTime';
import './plugin/Form/InputTimeRange';
import './plugin/Form/TreeSelect';
import './plugin/Form/InputURL';
import './plugin/Form/InputYear';
import './plugin/Form/InputYearRange';
import './plugin/Breadcrumb';
import './plugin/CustomRegion';
import './plugin/Dialog';
import './plugin/Drawer';
import './plugin/HBox';
import './plugin/ListItem';
import './plugin/Operation';
import './plugin/Page';
import './plugin/Pagination';
import './plugin/Plain';
import './plugin/Reset';
import './plugin/Submit';
import './plugin/Wrapper';
import './plugin/ColumnToggler';
import {GridPlugin} from './plugin/Grid';
export * from './plugin';
import './renderer/OptionControl';
import './renderer/NavSourceControl';
@ -210,5 +53,3 @@ import 'amis-theme-editor/lib/renderers.css';
export * from './component/BaseControl';
export * from './icons/index';
export {GridPlugin};

View File

@ -160,6 +160,7 @@ export class CarouselPlugin extends BasePlugin {
(item: {
html?: string;
image?: string;
href?: string;
title?: string;
titleClassName?: string;
description?: string;
@ -174,6 +175,7 @@ export class CarouselPlugin extends BasePlugin {
type: 'image',
content: item.image,
title: item.title,
href: item.href,
titleClassName: item.titleClassName,
description: item.description,
descriptionClassName: item.descriptionClassName
@ -187,6 +189,7 @@ export class CarouselPlugin extends BasePlugin {
(item: {
type: string;
content: string;
href?: string;
title?: string;
titleClassName?: string;
description?: string;
@ -198,6 +201,7 @@ export class CarouselPlugin extends BasePlugin {
}
: {
image: item.content,
href: item.href,
title: item.title,
titleClassName: item.titleClassName,
description: item.description,

View File

@ -249,6 +249,25 @@ export class CheckboxesControlPlugin extends BasePlugin {
originalValue: node.schema?.value // 记录原始值,循环引用检测需要
};
if (node.schema?.joinValues === false) {
dataSchema = {
...dataSchema,
type: 'object',
title: node.schema?.label || node.schema?.name,
properties: {
label: {
type: 'string',
title: '文本'
},
value: {
type,
title: '值'
}
}
};
}
if (node.schema?.multiple) {
if (node.schema?.extractValue) {
dataSchema = {
type: 'array',
@ -261,20 +280,12 @@ export class CheckboxesControlPlugin extends BasePlugin {
items: {
type: 'object',
title: '成员',
properties: {
label: {
type: 'string',
title: '文本'
},
value: {
type,
title: '值'
}
}
properties: dataSchema.properties
},
originalValue: dataSchema.originalValue
};
}
}
return dataSchema;
}

View File

@ -0,0 +1,160 @@
// 布局容器
export * from './Flex'; // flex布局
export * from './Grid'; // 分栏
export * from './Container'; // 容器
export * from './Layout/Layout_free_container'; // 自由容器
export * from './Layout/Layout_sorption_container'; // 吸附容器
export * from './Layout/Layout_fixed'; // 悬浮容器
// export * from './Layout/Layout1_2_v4';
export * from './CollapseGroup'; // 折叠面板
export * from './Panel'; // 面板
export * from './Tabs'; // 选项卡
// 数据容器
export * from './CRUD'; // 增删改查
export {
TableCRUDPlugin,
ListCRUDPlugin,
CardsCRUDPlugin,
CRUDPlugin as CRUD2Plugin
} from './CRUD2';
export * from './Form/Form'; // 表单
export * from './Service'; // 服务service
// 表单项
export * from './Form/InputText'; // 文本框
export * from './Form/Textarea'; // 多行文本框
export * from './Form/InputNumber'; // 数字框
export * from './Form/Select'; // 下拉框
export * from './Form/NestedSelect'; // 级联选择器
export * from './Form/ChainedSelect'; // 链式下拉框
export * from './DropDownButton'; // 下拉按钮
export * from './Form/Checkboxes'; // 复选框
export * from './Form/Radios'; // 单选框
export * from './Form/Checkbox'; // 勾选框
export * from './Form/InputDate'; // 日期
export * from './Form/InputDateRange'; // 日期范围
export * from './Form/InputFile'; // 文件上传
export * from './Form/InputImage'; // 图片上传
export * from './Form/InputExcel'; // 上传 Excel
export * from './Form/InputTree'; // 树选择器
export * from './Form/InputTag'; // 标签选择器
export * from './Form/ListSelect'; // 列表选择
export * from './Form/ButtonGroupSelect'; // 按钮点选
export * from './Form/ButtonToolbar'; // 按钮工具栏
export * from './Form/Picker'; // 列表选取
export * from './Form/Switch'; // 开关
export * from './Form/InputRange'; // 滑块
export * from './Form/InputRating'; // 评分
export * from './Form/InputCity'; // 城市选择
export * from './Form/Transfer'; // 穿梭器
export * from './Form/TabsTransfer'; // 组合穿梭器
export * from './Form/InputColor'; // 颜色框
export * from './Form/ConditionBuilder'; // 条件组合
export * from './Form/FieldSet'; // 字段集
export * from './Form/Combo'; // 组合输入
export * from './Form/InputGroup'; // 输入组合
export * from './Form/InputTable'; // 表格编辑器
export * from './Form/MatrixCheckboxes'; // 矩阵开关
export * from './Form/InputRichText'; // 富文本编辑器
export * from './Form/DiffEditor'; // diff编辑器
export * from './Form/CodeEditor'; // 代码编辑器
export * from './SearchBox'; // 搜索框
export * from './Form/InputKV'; // KV键值对
export * from './Form/InputRepeat'; // 重复周期
export * from './Form/UUID'; // UUID
export * from './Form/LocationPicker'; // 地理位置
export * from './Form/InputSubForm'; // 子表单项
export * from './Form/Hidden'; // 隐藏域
// 功能
export * from './Button'; // 按钮
export * from './ButtonGroup'; // 按钮组
export * from './Nav'; // 导航
export * from './AnchorNav'; // 锚点导航
export * from './TooltipWrapper'; // 文字提示
export * from './Alert'; // 提示
export * from './Wizard'; // 向导
export * from './TableView'; // 表格视图
export * from './WebComponent';
export * from './Audio'; // 音频
export * from './Video'; // 视频
export * from './Custom'; // 自定义代码
export * from './Tasks'; // 异步任务
export * from './Each'; // 循环
export * from './Property'; // 属性表
export * from './IFrame';
export * from './QRCode'; // 二维码
// 展示
export * from './Tpl'; // 文字
export * from './Icon'; // 图标
export * from './Link'; // 链接
export * from './List'; // 列表
export * from './Mapping'; // 映射
export * from './Avatar'; // 头像
export * from './Card'; // 卡片
export * from './Card2';
export * from './Cards'; // 卡片列表
export * from './Table'; // 表格
export * from './Table2';
export * from './TableCell2'; // 列配置
export * from './Chart'; // 图表
export * from './Sparkline'; // 走势图
export * from './Carousel'; // 轮播图
export * from './Image'; // 图片展示
export * from './Images'; // 图片集
export * from './Time'; // 时间展示
export * from './Date'; // 日期展示
export * from './Datetime'; // 日期时间展示
export * from './Tag'; // 标签
export * from './Json'; // JSON展示
export * from './Progress'; // 进度展示
export * from './Status'; // 状态展示
export * from './Steps'; // 步骤条
export * from './Timeline'; // 时间轴
export * from './Divider'; // 分隔线
export * from './CodeView'; // 代码高亮
export * from './Markdown';
export * from './Collapse'; // 折叠器
export * from './OfficeViewer'; // 文档预览
export * from './Log'; // 日志
// 其他
export * from './Others/Action';
export * from './Others/TableCell';
export * from './Form/InputArray';
export * from './Form/ConditionBuilder';
export * from './Form/Control';
export * from './Form/InputDateTime';
export * from './Form/InputDateTimeRange';
export * from './Form/InputEmail';
export * from './Form/Formula';
export * from './Form/Group';
export * from './Form/Item';
export * from './Form/InputMonth';
export * from './Form/InputMonthRange';
export * from './Form/InputPassword';
export * from './Form/InputQuarter';
export * from './Form/InputQuarterRange';
export * from './Form/Static';
export * from './Form/InputTime';
export * from './Form/InputTimeRange';
export * from './Form/TreeSelect';
export * from './Form/InputURL';
export * from './Form/InputYear';
export * from './Form/InputYearRange';
export * from './Breadcrumb';
export {CustomPlugin as CustomRegionPlugin} from './CustomRegion';
export * from './Dialog';
export * from './Drawer';
export * from './HBox';
export * from './ListItem';
export * from './Operation';
export * from './Page';
export * from './Pagination';
export * from './Plain';
export * from './Reset';
export * from './Submit';
export * from './Wrapper';
export * from './ColumnToggler';

View File

@ -29,7 +29,7 @@ export interface TimelineItemProps extends FormControlProps {
export interface TimelineItemState {
items: Array<Partial<TimelineItem>>;
source: 'custom' | 'api';
source: 'custom' | 'api' | 'variable';
api: SchemaApi;
}
@ -54,7 +54,7 @@ export default class TimelineItemControl extends React.Component<
*
*/
@autobind
handleSourceChange(source: 'custom' | 'api') {
handleSourceChange(source: 'custom' | 'api' | 'variable') {
this.setState({source: source}, this.onChange);
}
@ -80,6 +80,11 @@ export default class TimelineItemControl extends React.Component<
data.items = items.map(item => ({...item}));
data.source = api;
}
if (source === 'variable') {
const {items, api} = this.state;
data.items = items.map(item => ({...item}));
data.source = api;
}
onBulkChange && onBulkChange(data);
}
@ -339,10 +344,14 @@ export default class TimelineItemControl extends React.Component<
{
label: '接口获取',
value: 'api'
},
{
label: '上下文变量',
value: 'variable'
}
] as Array<{
label: string;
value: 'custom' | 'api';
value: 'custom' | 'api' | 'variable';
}>
).map(item => ({
...item,
@ -562,6 +571,19 @@ export default class TimelineItemControl extends React.Component<
</div>
</div>
) : null}
{source === 'variable'
? render(
'variable',
getSchemaTpl('sourceBindControl', {
label: false,
className: 'ae-ExtendMore'
}),
{
onChange: this.handleAPIChange
}
)
: null}
{this.renderApiPanel()}
</div>
);

View File

@ -1,6 +1,4 @@
.#{$ns}Chart {
min-width: 300px;
min-height: 300px;
position: relative;
&-placeholder {

View File

@ -31,6 +31,7 @@ class InputInner extends React.Component<InputProps, InputState> {
@autobind
handleComposition(e: React.CompositionEvent<HTMLInputElement>) {
this.isOnComposition = e.type !== 'compositionend';
if (!this.isOnComposition) {
this.handleChange(e as any);
}
@ -47,6 +48,17 @@ class InputInner extends React.Component<InputProps, InputState> {
});
}
@autobind
handleKeyDown(e: React.KeyboardEvent<any>) {
const {onKeyDown} = this.props;
if (this.isOnComposition) {
return;
}
onKeyDown?.(e);
}
render() {
const {forwardedRef, ...rest} = this.props;
@ -57,6 +69,7 @@ class InputInner extends React.Component<InputProps, InputState> {
value={this.state.value}
ref={forwardedRef}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
onCompositionStart={this.handleComposition}
onCompositionUpdate={this.handleComposition}
onCompositionEnd={this.handleComposition}

View File

@ -8,6 +8,7 @@ import {uncontrollable} from 'amis-core';
import {autobind, isMobile} from 'amis-core';
import {LocaleProps, localeable} from 'amis-core';
import chain from 'lodash/chain';
import Input from './Input';
export interface HistoryRecord {
/** 历史记录值 */
@ -306,17 +307,17 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
)}
style={style}
>
<input
<Input
name={name}
ref={this.inputRef}
disabled={disabled}
placeholder={__(placeholder || 'placeholder.enter')}
value={inputValue ?? ''}
autoComplete="off"
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
value={inputValue ?? ''}
disabled={disabled}
placeholder={__(placeholder || 'placeholder.enter')}
autoComplete="off"
/>
{!mini && clearable && inputValue && !disabled ? (

View File

@ -0,0 +1,102 @@
import {fireEvent, render, waitFor} from '@testing-library/react';
import '../../../../src';
import {render as amisRender} from '../../../../src';
import {makeEnv, wait} from '../../../helper';
test('EventAction:switch', async () => {
const fetcher = jest.fn().mockImplementation(() =>
Promise.resolve({
data: {
status: 0,
msg: 'ok',
data: {
id: 1
}
}
})
);
const {container, getByText} = render(
amisRender(
{
type: 'page',
body: [
{
type: 'button',
label: '修改开关的值',
className: 'mb-2',
onEvent: {
click: {
actions: [
{
componentId: 'u:6613bfa3a18e',
actionType: 'setValue',
args: {
value: true
}
}
]
}
},
id: 'u:9d7d695145bb'
},
{
type: 'form',
title: '表单',
debug: true,
body: [
{
label: '开启',
type: 'switch',
name: 'switch',
id: 'u:6613bfa3a18e',
value: false,
mode: 'inline',
onEvent: {
change: {
actions: [
{
actionType: 'ajax',
api: '/api/mock2/form/saveForm?switch=${switch}'
}
]
}
}
}
],
id: 'u:82d44e407eb0',
actions: [
{
type: 'submit',
label: '提交',
primary: true
}
]
}
]
},
{},
makeEnv({
fetcher
})
)
);
await waitFor(() => {
expect(getByText('修改开关的值')).toBeInTheDocument();
});
fireEvent.click(getByText(/修改开关的值/));
await waitFor(() => {
expect(container.querySelector('.is-checked')).toBeInTheDocument();
});
fireEvent.click(container.querySelector('.is-checked')!);
await waitFor(() => {
expect(fetcher).toHaveBeenCalled();
expect(fetcher.mock.calls[0][0].url).toEqual(
'/api/mock2/form/saveForm?switch=false'
);
});
});

View File

@ -118,7 +118,7 @@ test('Renderer:Carousel with name & option config', async () => {
imageClassName: 'thisisimageClassName',
title: '这是标题',
titleClassName: 'thiisistitleClassName',
description: '描述', // description 属性没用
description: '这是描述',
href: 'https://www.baidu.com'
},
{
@ -146,6 +146,7 @@ test('Renderer:Carousel with name & option config', async () => {
'https://www.baidu.com'
);
expect(item).toHaveTextContent('这是标题');
expect(item).toHaveTextContent('这是描述');
fireEvent.click(container.querySelector('.cxd-Carousel-dot:nth-child(2)')!);

View File

@ -10,6 +10,7 @@
* 7. defaultIsOpened
* 8. align
* 9. block & size
* 10. buttons level & className
*/
import React from 'react';
@ -396,3 +397,36 @@ test('Renderer:dropdown-button with block & size', async () => {
);
expect(container).toMatchSnapshot();
});
test('Renderer:dropdown-button buttons with className & level', async () => {
const {container} = render(
amisRender({
type: "dropdown-button",
level: "success",
label: "下拉菜单",
buttons: [
{
type: "button",
label: "按钮1",
level: "success",
className: "custom-button-class"
},
{
type: "button",
label: "按钮2"
}
]
})
);
const dropdownButton = document.querySelector('button.cxd-Button');
fireEvent.click(dropdownButton as HTMLDivElement);
expect(container.querySelectorAll('.cxd-DropDown-menu-root .cxd-DropDown-button')[0]!).toHaveClass(
'cxd-Button--success'
);
expect(container.querySelectorAll('.cxd-DropDown-menu-root .cxd-DropDown-button')[0]!).toHaveClass(
'custom-button-class'
);
});

View File

@ -6,10 +6,11 @@
* 3. clearable
* 4. mini
* 5. searchImediately className
* 6. Composition触发
*/
import React from 'react';
import {fireEvent, render} from '@testing-library/react';
import {fireEvent, render, screen} from '@testing-library/react';
import '../../src';
import {render as amisRender} from '../../src';
import {makeEnv, wait} from '../helper';
@ -36,7 +37,7 @@ test('Renderer:Searchbox', async () => {
)
);
await wait(200);
await wait(500);
expect(fetcher).toHaveBeenCalledTimes(1);
expect(fetcher.mock.calls[0][0].query).toEqual({
@ -212,3 +213,34 @@ test('Renderer:Searchbox with searchImediately & className', async () => {
keywords: 'aabb'
});
});
test('6. Renderer: Searchbox is not supposed to be triggered with composition input', async () => {
const onQuery = jest.fn();
const {container} = render(amisRender({
type: "search-box",
name: "keywords",
}, {
onQuery
}))
const inputEl = container.querySelector('.cxd-SearchBox input')!;
expect(inputEl).toBeInTheDocument();
/** 第一次输入 Enter 后,文本填入 */
fireEvent.compositionStart(inputEl);
fireEvent.keyDown(inputEl, { key: 'Enter', keyCode: 13 });
await wait(200);
expect(onQuery).not.toHaveBeenCalled();
/** 退出输入法,触发搜索 */
fireEvent.compositionEnd(inputEl);
fireEvent.change(inputEl, {target: {value: 'test'}});
fireEvent.keyDown(inputEl, { key: 'Enter', keyCode: 13 });
await wait(200);
expect(onQuery).toHaveBeenCalledTimes(1);
expect(onQuery.mock.calls[0][0]).toEqual({
keywords: 'test'
});
})

View File

@ -32,6 +32,12 @@ exports[`Renderer:Carousel 1`] = `
>
标题
</div>
<div
class="cxd-Image-caption"
title="描述"
>
描述
</div>
</div>
</div>
</div>

View File

@ -22,6 +22,7 @@ exports[`Renderer:Searchbox 1`] = `
autocomplete="off"
name="keywords"
placeholder="请输入"
type="text"
value="searchkey"
/>
<a
@ -49,6 +50,7 @@ exports[`Renderer:Searchbox with clearable 1`] = `
autocomplete="off"
name="keywords"
placeholder="请输入"
type="text"
value="tmpvalue"
/>
<div
@ -93,6 +95,7 @@ exports[`Renderer:Searchbox with enhance 1`] = `
autocomplete="off"
name="keywords"
placeholder="请输入"
type="text"
value=""
/>
<a
@ -120,6 +123,7 @@ exports[`Renderer:Searchbox with mini 1`] = `
autocomplete="off"
name="keywords"
placeholder="请输入"
type="text"
value=""
/>
<a
@ -143,6 +147,7 @@ exports[`Renderer:Searchbox with mini 2`] = `
autocomplete="off"
name="keywords"
placeholder="请输入"
type="text"
value="what?"
/>
<a

View File

@ -146,7 +146,7 @@ const defaultSchema = {
href={data.href}
blank={data.blank}
htmlTarget={data.htmlTarget}
imageCaption={data.description}
caption={data.description}
thumbMode={data.thumbMode ?? thumbMode ?? 'contain'}
imageMode="original"
className={cx('Carousel-image')}

View File

@ -596,9 +596,8 @@ export class Chart extends React.Component<ChartProps> {
data
} = this.props;
let style = this.props.style || {};
width && (style.width = width);
height && (style.height = height);
style.width = style.width || width || '100%';
style.height = style.width || height || '300px';
const styleVar = buildStyle(style, data);
return (

View File

@ -279,15 +279,25 @@ export default class DropDownButton extends React.Component<
return (
<li
key={index}
className={cx('DropDown-button', {
className={cx(
'DropDown-button',
{
['is-disabled']: isDisabled(button, data)
})}
},
typeof button.level === 'undefined'
? ''
: button.level
? `Button--${button.level}`
: '',
button.className
)}
>
{render(
`button/${index}`,
{
type: 'button',
...(button as any)
...(button as any),
className: ''
},
{
isMenuItem: true,

View File

@ -255,8 +255,21 @@ export class HeadCellFilterDropDown extends React.Component<
});
}
handleReset() {
const {name, onQuery} = this.props;
async handleReset() {
const {name, dispatchEvent, data, onQuery} = this.props;
const rendererEvent = await dispatchEvent(
'columnFilter',
createObject(data, {
filterName: name,
filterValue: undefined
})
);
if (rendererEvent?.prevented) {
return;
}
onQuery(
{
[name]: undefined

View File

@ -16,7 +16,6 @@ import type {
SchemaTokenizeableString
} from '../Schema';
import type {IconCheckedSchema} from 'amis-ui';
import {TimelineItemProps} from 'packages/amis-ui/src/components/TimelineItem';
export interface TimelineItemSchema extends Omit<BaseSchema, 'type'> {
/**
@ -149,7 +148,7 @@ export function TimelineCmpt(props: TimelineProps) {
typeof val === 'string' ? filter(val, data) : val && render(region, val);
// 处理源数据
const resolveTimelineItems: Array<TimelineItemProps> = (items || []).map(
const resolveTimelineItems = (items || []).map(
(timelineItem: TimelineItemSchema, index: number) => {
const {
icon,