mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
merge e2e into master
This commit is contained in:
commit
a7c550ac2f
@ -246,6 +246,7 @@ export default function (schema, schemaProps, showCode, envOverrides) {
|
||||
};
|
||||
});
|
||||
},
|
||||
enableTestid: true,
|
||||
...envOverrides
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@ import {IScopedContext, ScopedContext} from './Scoped';
|
||||
import {Schema, SchemaNode} from './types';
|
||||
import {DebugWrapper} from './utils/debug';
|
||||
import getExprProperties from './utils/filter-schema';
|
||||
import {anyChanged, chainEvents, autobind} from './utils/helper';
|
||||
import {anyChanged, chainEvents, autobind, TestIdBuilder} from './utils/helper';
|
||||
import {SimpleMap} from './utils/SimpleMap';
|
||||
import {bindEvent, dispatchEvent, RendererEvent} from './utils/renderer-event';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
@ -476,8 +476,14 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
(props as any).static = isStatic;
|
||||
}
|
||||
|
||||
if (rest.env.enableTestid && props.id && !props.testid) {
|
||||
props.testid = props.id;
|
||||
// 优先使用组件自己的testid或者id,这个解决不了table行内的一些子元素
|
||||
// 每一行都会出现这个testid的元素,只在测试工具中直接使用nth拿序号
|
||||
if (props.testid || props.id || props.testIdBuilder == null) {
|
||||
if (!(props.testIdBuilder instanceof TestIdBuilder)) {
|
||||
props.testIdBuilder = new TestIdBuilder(
|
||||
rest.env.enableTestid ? props.testid || props.id : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动解析变量模式,主要是方便直接引入第三方组件库,无需为了支持变量封装一层
|
||||
|
@ -9,7 +9,8 @@ import {
|
||||
qsparse,
|
||||
string2regExp,
|
||||
parseQuery,
|
||||
isMobile
|
||||
isMobile,
|
||||
TestIdBuilder
|
||||
} from './utils/helper';
|
||||
import {
|
||||
fetcherResult,
|
||||
@ -72,6 +73,7 @@ export interface RendererProps
|
||||
env: RendererEnv;
|
||||
$path: string; // 当前组件所在的层级信息
|
||||
$schema: any; // 原始 schema 配置
|
||||
testIdBuild: TestIdBuilder;
|
||||
store?: IIRendererStore;
|
||||
syncSuperStore?: boolean;
|
||||
data: {
|
||||
|
@ -49,7 +49,7 @@ import LazyComponent from '../components/LazyComponent';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
|
||||
import type {LabelAlign} from './Item';
|
||||
import {buildTestId, injectObjectChain} from '../utils';
|
||||
import {injectObjectChain} from '../utils';
|
||||
import {reaction} from 'mobx';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
|
||||
@ -1860,7 +1860,6 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
)}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
noValidate
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{/* 实现回车自动提交 */}
|
||||
<input type="submit" style={{display: 'none'}} />
|
||||
|
@ -552,7 +552,6 @@ export interface FormItemProps extends RendererProps {
|
||||
// error string
|
||||
error?: string;
|
||||
showErrorMsg?: boolean;
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
// 下发下去的属性
|
||||
|
@ -2,6 +2,7 @@
|
||||
import type {JSONSchema7} from 'json-schema';
|
||||
import {ListenerAction} from './actions/Action';
|
||||
import {debounceConfig, trackConfig} from './utils/renderer-event';
|
||||
import type {TestIdBuilder} from './utils/helper';
|
||||
|
||||
export interface Option {
|
||||
/**
|
||||
@ -693,6 +694,8 @@ export interface BaseSchemaWithoutType {
|
||||
* 可以组件级别用来关闭移动端样式
|
||||
*/
|
||||
useMobileUI?: boolean;
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export type OperatorType =
|
||||
|
@ -2282,18 +2282,49 @@ export function replaceUrlParams(path: string, params: Record<string, any>) {
|
||||
|
||||
export const TEST_ID_KEY: 'data-testid' = 'data-testid';
|
||||
|
||||
export function buildTestId(testid?: string, data?: PlainObject) {
|
||||
if (!testid) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
[TEST_ID_KEY]: filter(testid, data)
|
||||
};
|
||||
}
|
||||
export class TestIdBuilder {
|
||||
testId?: string;
|
||||
|
||||
export function getTestId(testid?: string, data?: PlainObject) {
|
||||
if (!testid) {
|
||||
return undefined;
|
||||
static fast(testId: string) {
|
||||
return {
|
||||
[TEST_ID_KEY]: testId
|
||||
};
|
||||
}
|
||||
|
||||
// 为空就表示没有启用testId,后续一直返回都将是空
|
||||
constructor(testId?: string) {
|
||||
this.testId = testId;
|
||||
}
|
||||
|
||||
// 生成子区域的testid生成器
|
||||
getChild(childPath: string | number, data?: object) {
|
||||
if (this.testId == null) {
|
||||
return new TestIdBuilder();
|
||||
}
|
||||
|
||||
return new TestIdBuilder(
|
||||
data
|
||||
? filter(`${this.testId}-${childPath}`, data)
|
||||
: `${this.testId}-${childPath}`
|
||||
);
|
||||
}
|
||||
|
||||
// 获取当前组件的testid
|
||||
getTestId(data?: object) {
|
||||
if (this.testId == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
[TEST_ID_KEY]: data ? filter(this.testId, data) : this.testId
|
||||
};
|
||||
}
|
||||
|
||||
getTestIdValue(data?: object) {
|
||||
if (this.testId == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return data ? filter(this.testId, data) : this.testId;
|
||||
}
|
||||
return buildTestId(testid, data)[TEST_ID_KEY];
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Html, render, TooltipWrapper, buildTestId} from 'amis';
|
||||
import {Html, render, TestIdBuilder, TooltipWrapper} from 'amis';
|
||||
import {observer} from 'mobx-react';
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
@ -18,6 +18,7 @@ type PanelProps = {
|
||||
};
|
||||
searchRendererType: string;
|
||||
className?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
};
|
||||
|
||||
type PanelStates = {
|
||||
@ -98,7 +99,7 @@ export default class RenderersPanel extends React.Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
const {store, searchRendererType, className} = this.props;
|
||||
const {store, searchRendererType, className, testIdBuilder} = this.props;
|
||||
const grouped = this.props.groupedRenderers || {};
|
||||
const keys = Object.keys(grouped);
|
||||
|
||||
@ -189,7 +190,7 @@ export default class RenderersPanel extends React.Component<
|
||||
onDragStart={(e: React.DragEvent) =>
|
||||
this.handleDragStart(e, item.name)
|
||||
}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getChild(testid).getTestId()}
|
||||
>
|
||||
<div
|
||||
className="icon-box"
|
||||
|
@ -30,6 +30,16 @@
|
||||
color: var(--colors-neutral-text-6);
|
||||
outline: none;
|
||||
transition: border 0.24s ease-in-out;
|
||||
position: relative;
|
||||
> input {
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropzone:focus {
|
||||
|
@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {mapTree} from 'amis-core';
|
||||
import {ClassNamesFn, themeable, buildTestId} from 'amis-core';
|
||||
import {TestIdBuilder, mapTree} from 'amis-core';
|
||||
import {ClassNamesFn, themeable} from 'amis-core';
|
||||
|
||||
export type LinkItem = LinkItemProps;
|
||||
interface LinkItemProps {
|
||||
@ -19,8 +19,8 @@ interface LinkItemProps {
|
||||
children?: Array<LinkItem>;
|
||||
path?: string;
|
||||
icon?: string;
|
||||
testid?: string;
|
||||
component?: React.ElementType;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface Navigation {
|
||||
@ -56,7 +56,7 @@ interface AsideNavState {
|
||||
export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
|
||||
static defaultProps = {
|
||||
renderLink: (item: LinkItemProps) => (
|
||||
<a {...buildTestId(item.testid, item)}>{item.label}</a>
|
||||
<a {...item.testIdBuilder?.getTestId()}>{item.label}</a>
|
||||
),
|
||||
renderSubLinks: (
|
||||
link: LinkItemProps,
|
||||
|
@ -128,7 +128,8 @@ export class AssociatedSelection extends BaseSelection<
|
||||
loadingConfig,
|
||||
checkAll,
|
||||
checkAllLabel,
|
||||
deferField = 'defer'
|
||||
deferField = 'defer',
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const selectdOption = BaseSelection.resolveSelected(
|
||||
@ -152,6 +153,7 @@ export class AssociatedSelection extends BaseSelection<
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
loadingConfig={loadingConfig}
|
||||
testIdBuilder={testIdBuilder?.getChild('left-selection')}
|
||||
/>
|
||||
) : (
|
||||
<GroupedSelection
|
||||
@ -164,6 +166,7 @@ export class AssociatedSelection extends BaseSelection<
|
||||
clearable={false}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
testIdBuilder={testIdBuilder?.getChild('left-selection')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -204,6 +207,7 @@ export class AssociatedSelection extends BaseSelection<
|
||||
multiple={multiple}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
testIdBuilder={testIdBuilder?.getChild('right-selection')}
|
||||
/>
|
||||
) : rightMode === 'tree' ? (
|
||||
<Tree
|
||||
@ -218,6 +222,7 @@ export class AssociatedSelection extends BaseSelection<
|
||||
loadingConfig={loadingConfig}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('right-selection')}
|
||||
/>
|
||||
) : rightMode === 'chained' ? (
|
||||
<ChainedSelection
|
||||
@ -234,6 +239,7 @@ export class AssociatedSelection extends BaseSelection<
|
||||
loadingConfig={loadingConfig}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('right-selection')}
|
||||
/>
|
||||
) : (
|
||||
<GroupedSelection
|
||||
@ -249,6 +255,7 @@ export class AssociatedSelection extends BaseSelection<
|
||||
itemHeight={itemHeight}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('right-selection')}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
@ -8,6 +8,7 @@ import {ClassNamesFn, themeable, ThemeProps} from 'amis-core';
|
||||
import {RootClose} from 'amis-core';
|
||||
import {removeHTMLTag} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export type ItemPlace = 'start' | 'middle' | 'end';
|
||||
export type TooltipPositionType = 'top' | 'bottom' | 'left' | 'right';
|
||||
@ -32,6 +33,7 @@ interface BreadcrumbItemProps {
|
||||
tooltipContainer?: any;
|
||||
tooltipPosition?: TooltipPositionType;
|
||||
classnames: ClassNamesFn;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@ -39,6 +41,7 @@ interface BreadcrumbProps extends ThemeProps {
|
||||
tooltipContainer?: any;
|
||||
tooltipPosition?: TooltipPositionType;
|
||||
items: Array<BreadcrumbBaseItem>;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@ -65,6 +68,7 @@ export class Breadcrumb extends React.Component<BreadcrumbProps> {
|
||||
separatorClassName,
|
||||
items,
|
||||
separator,
|
||||
testIdBuilder,
|
||||
...restProps
|
||||
} = this.props;
|
||||
|
||||
@ -75,6 +79,9 @@ export class Breadcrumb extends React.Component<BreadcrumbProps> {
|
||||
|
||||
const crumbs = items
|
||||
.map<React.ReactNode>((item, index) => {
|
||||
const itemTestIdBuilder = testIdBuilder?.getChild(
|
||||
`item-${item.label || index}`
|
||||
);
|
||||
let itemPlace: ItemPlace = 'middle';
|
||||
if (index === 0) {
|
||||
itemPlace = 'start';
|
||||
@ -88,6 +95,7 @@ export class Breadcrumb extends React.Component<BreadcrumbProps> {
|
||||
item={item}
|
||||
itemPlace={itemPlace}
|
||||
key={index}
|
||||
testIdBuilder={itemTestIdBuilder}
|
||||
></BreadcrumbItem>
|
||||
);
|
||||
})
|
||||
@ -157,7 +165,12 @@ export class BreadcrumbItem extends React.Component<
|
||||
item: BreadcrumbBaseItem,
|
||||
label?: string
|
||||
) {
|
||||
const {itemClassName, dropdownItemClassName, classnames: cx} = this.props;
|
||||
const {
|
||||
itemClassName,
|
||||
dropdownItemClassName,
|
||||
classnames: cx,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const baseItemClassName =
|
||||
itemType === 'default' ? itemClassName : dropdownItemClassName;
|
||||
if (showHref) {
|
||||
@ -165,6 +178,7 @@ export class BreadcrumbItem extends React.Component<
|
||||
<a
|
||||
href={item.href}
|
||||
className={cx('Breadcrumb-item-' + itemType, baseItemClassName)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{item.icon ? (
|
||||
<Icon
|
||||
@ -179,7 +193,10 @@ export class BreadcrumbItem extends React.Component<
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className={cx('Breadcrumb-item-' + itemType, baseItemClassName)}>
|
||||
<span
|
||||
className={cx('Breadcrumb-item-' + itemType, baseItemClassName)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{item.icon ? (
|
||||
<Icon
|
||||
cx={cx}
|
||||
|
@ -5,8 +5,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import TooltipWrapper, {TooltipObject, Trigger} from './TooltipWrapper';
|
||||
import {pickEventsProps} from 'amis-core';
|
||||
import {ClassNamesFn, themeable, buildTestId} from 'amis-core';
|
||||
import {TestIdBuilder, pickEventsProps} from 'amis-core';
|
||||
import {ClassNamesFn, themeable} from 'amis-core';
|
||||
import Spinner, {SpinnerExtraProps} from './Spinner';
|
||||
|
||||
export interface ButtonProps
|
||||
@ -14,7 +14,6 @@ export interface ButtonProps
|
||||
SpinnerExtraProps {
|
||||
id?: string;
|
||||
className?: string;
|
||||
testid?: string;
|
||||
style?: any;
|
||||
href?: string;
|
||||
title?: string;
|
||||
@ -37,6 +36,7 @@ export interface ButtonProps
|
||||
overrideClassName?: boolean;
|
||||
loading?: boolean;
|
||||
loadingClassName?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class Button extends React.Component<ButtonProps> {
|
||||
@ -78,7 +78,7 @@ export class Button extends React.Component<ButtonProps> {
|
||||
loadingClassName,
|
||||
overrideClassName,
|
||||
loadingConfig,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -94,7 +94,7 @@ export class Button extends React.Component<ButtonProps> {
|
||||
{...pickEventsProps(rest)}
|
||||
onClick={rest.onClick && disabled ? () => {} : rest.onClick}
|
||||
href={href}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
className={cx(
|
||||
overrideClassName
|
||||
? ''
|
||||
|
@ -72,9 +72,11 @@ export class ChainedSelection extends BaseSelection<
|
||||
itemClassName,
|
||||
itemRender,
|
||||
multiple,
|
||||
labelField
|
||||
labelField,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const valueArray = this.valueArray;
|
||||
const itemTIB = testIdBuilder?.getChild(`item-${option.value || index}`);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -96,6 +98,7 @@ export class ChainedSelection extends BaseSelection<
|
||||
disabled={disabled || option.disabled}
|
||||
labelClassName={labelClassName}
|
||||
description={option.description}
|
||||
testIdBuilder={itemTIB}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -130,9 +133,11 @@ export class ChainedSelection extends BaseSelection<
|
||||
multiple,
|
||||
labelField,
|
||||
deferField = 'defer',
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const valueArray = this.valueArray;
|
||||
const itemTIB = testIdBuilder?.getChild(`item-${option.value || index}`);
|
||||
|
||||
if (Array.isArray(option.children) || option[deferField]) {
|
||||
return (
|
||||
@ -147,6 +152,7 @@ export class ChainedSelection extends BaseSelection<
|
||||
~this.state.selected.indexOf(id) ? 'is-active' : ''
|
||||
)}
|
||||
onClick={() => this.selectOption(option, depth, id)}
|
||||
{...itemTIB?.getTestId()}
|
||||
>
|
||||
<div className={cx('ChainedSelection-itemLabel')}>
|
||||
{itemRender(option, {
|
||||
@ -230,7 +236,8 @@ export class ChainedSelection extends BaseSelection<
|
||||
translate: __,
|
||||
virtualThreshold = 1000,
|
||||
itemHeight = 32,
|
||||
virtualListHeight
|
||||
virtualListHeight,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
this.valueArray = BaseSelection.value2array(value, options, option2value);
|
||||
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {ThemeProps, themeable} from 'amis-core';
|
||||
import {TestIdBuilder, ThemeProps, themeable} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
|
||||
const preventEvent = (e: any) => e.stopPropagation();
|
||||
@ -28,6 +28,7 @@ interface CheckboxProps extends ThemeProps {
|
||||
partial?: boolean;
|
||||
optionType?: 'default' | 'button';
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||
@ -72,7 +73,8 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||
name,
|
||||
labelClassName,
|
||||
optionType,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const _checked =
|
||||
typeof checked !== 'undefined'
|
||||
@ -96,6 +98,7 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
data-role="checkbox"
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<input
|
||||
type={type}
|
||||
@ -114,8 +117,13 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||
readOnly={readOnly}
|
||||
name={name}
|
||||
/>
|
||||
<i />
|
||||
<span className={cx(labelClassName)}>{children || label}</span>
|
||||
<i {...testIdBuilder?.getChild('input').getTestId()} />
|
||||
<span
|
||||
className={cx(labelClassName)}
|
||||
{...testIdBuilder?.getChild('label').getTestId()}
|
||||
>
|
||||
{children || label}
|
||||
</span>
|
||||
{description ? (
|
||||
<div className={cx('Checkbox-desc')}>{description}</div>
|
||||
) : null}
|
||||
|
@ -27,7 +27,7 @@ import Input from './Input';
|
||||
import Button from './Button';
|
||||
|
||||
import type {Moment} from 'moment';
|
||||
import type {PlainObject, RendererEnv} from 'amis-core';
|
||||
import type {PlainObject, RendererEnv, TestIdBuilder} from 'amis-core';
|
||||
import type {ChangeEventViewMode, MutableUnitOfTime} from './calendar/Calendar';
|
||||
|
||||
const availableShortcuts: {[propName: string]: any} = {
|
||||
@ -334,6 +334,7 @@ export interface DateProps extends LocaleProps, ThemeProps {
|
||||
|
||||
// 是否为结束时间
|
||||
isEndDate?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
|
||||
disabledDate?: (date: moment.Moment) => any;
|
||||
onClick?: (date: moment.Moment) => any;
|
||||
@ -949,6 +950,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
mobileCalendarMode,
|
||||
label,
|
||||
env,
|
||||
testIdBuilder,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
@ -1058,6 +1060,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
testIdBuilder={testIdBuilder?.getChild('calendar')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -1081,6 +1084,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
)}
|
||||
ref={this.domRef}
|
||||
onClick={this.handleClick}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<Input
|
||||
className={cx('DatePicker-input')}
|
||||
@ -1092,17 +1096,25 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
value={this.state.inputValue || ''}
|
||||
disabled={disabled}
|
||||
readOnly={mobileUI}
|
||||
{...testIdBuilder?.getChild('input').getTestId()}
|
||||
/>
|
||||
|
||||
{clearable &&
|
||||
!disabled &&
|
||||
normalizeDate(value, valueFormat || format) ? (
|
||||
<a className={cx(`DatePicker-clear`)} onClick={this.clearValue}>
|
||||
<a
|
||||
className={cx(`DatePicker-clear`)}
|
||||
onClick={this.clearValue}
|
||||
{...testIdBuilder?.getChild('clear').getTestId()}
|
||||
>
|
||||
<Icon icon="input-clear" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
|
||||
<a className={cx(`DatePicker-toggler`)}>
|
||||
<a
|
||||
className={cx(`DatePicker-toggler`)}
|
||||
{...testIdBuilder?.getChild('toggler').getTestId()}
|
||||
>
|
||||
<Icon
|
||||
icon={viewMode === 'time' ? 'clock' : 'date'}
|
||||
className="icon"
|
||||
@ -1151,6 +1163,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
testIdBuilder={testIdBuilder?.getChild('calendar')}
|
||||
// utc={utc}
|
||||
/>
|
||||
{isConfirmMode ? (
|
||||
@ -1209,6 +1222,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
testIdBuilder={testIdBuilder?.getChild('calendar')}
|
||||
// utc={utc}
|
||||
/>
|
||||
</PopUp>
|
||||
|
@ -38,6 +38,7 @@ import type {
|
||||
MutableUnitOfTime,
|
||||
ChangeEventViewStatus
|
||||
} from './calendar/Calendar';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface DateRangePickerProps extends ThemeProps, LocaleProps {
|
||||
className?: string;
|
||||
@ -86,6 +87,7 @@ export interface DateRangePickerProps extends ThemeProps, LocaleProps {
|
||||
animation?: boolean;
|
||||
/** 日期处理函数,通常用于自定义处理绑定日期的值 */
|
||||
transform?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface DateRangePickerState {
|
||||
@ -1328,14 +1330,21 @@ export class DateRangePicker extends React.Component<
|
||||
if (!shortcuts) {
|
||||
return null;
|
||||
}
|
||||
const {classPrefix: ns, format, valueFormat, data} = this.props;
|
||||
const {
|
||||
classPrefix: ns,
|
||||
format,
|
||||
valueFormat,
|
||||
data,
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
let shortcutArr: Array<string | ShortCuts>;
|
||||
if (typeof shortcuts === 'string') {
|
||||
shortcutArr = shortcuts.split(',');
|
||||
} else {
|
||||
shortcutArr = shortcuts;
|
||||
}
|
||||
const __ = this.props.translate;
|
||||
const TIDBuilder = testIdBuilder?.getChild('shortcut');
|
||||
|
||||
return (
|
||||
<ul className={`${ns}DateRangePicker-rangers`}>
|
||||
@ -1407,7 +1416,9 @@ export class DateRangePicker extends React.Component<
|
||||
onClick={() => this.selectShortcut(shortcut)}
|
||||
key={index}
|
||||
>
|
||||
<a>{__(shortcut.label)}</a>
|
||||
<a {...TIDBuilder?.getChild(shortcut.key).getTestId()}>
|
||||
{__(shortcut.label)}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
@ -1537,6 +1548,7 @@ export class DateRangePicker extends React.Component<
|
||||
|
||||
renderDay(props: any, currentDate: moment.Moment) {
|
||||
let {startDate, endDate} = this.state;
|
||||
const {testIdBuilder} = this.props;
|
||||
|
||||
if (
|
||||
startDate &&
|
||||
@ -1567,7 +1579,9 @@ export class DateRangePicker extends React.Component<
|
||||
|
||||
return (
|
||||
<td {...omit(props, ['todayActiveStyle'])} {...others}>
|
||||
<span>{currentDate.date()}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key)?.getTestId()}>
|
||||
{currentDate.date()}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@ -1576,7 +1590,7 @@ export class DateRangePicker extends React.Component<
|
||||
const currentDate = props.viewDate.year(year).month(month);
|
||||
const {startDate, endDate} = this.state;
|
||||
|
||||
const {translate: __} = this.props;
|
||||
const {translate: __, testIdBuilder} = this.props;
|
||||
const monthStr = currentDate.format(__('MMM'));
|
||||
const strLength = 3;
|
||||
// Because some months are up to 5 characters long, we want to
|
||||
@ -1612,7 +1626,9 @@ export class DateRangePicker extends React.Component<
|
||||
|
||||
return (
|
||||
<td {...omit(props, 'viewDate')} {...others}>
|
||||
<span>{monthStrFixedLength}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key).getTestId()}>
|
||||
{monthStrFixedLength}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@ -1620,6 +1636,7 @@ export class DateRangePicker extends React.Component<
|
||||
renderQuarter(props: any, quarter: number, year: number) {
|
||||
const currentDate = moment().year(year).quarter(quarter);
|
||||
const {startDate, endDate} = this.state;
|
||||
const {testIdBuilder} = this.props;
|
||||
|
||||
if (
|
||||
startDate &&
|
||||
@ -1650,13 +1667,16 @@ export class DateRangePicker extends React.Component<
|
||||
|
||||
return (
|
||||
<td {...props} {...others}>
|
||||
<span>Q{quarter}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key).getTestId()}>
|
||||
Q{quarter}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
renderYear(props: any, year: number) {
|
||||
const currentDate = moment().year(year);
|
||||
const {startDate, endDate} = this.state;
|
||||
const {testIdBuilder} = this.props;
|
||||
|
||||
if (
|
||||
startDate &&
|
||||
@ -1687,7 +1707,7 @@ export class DateRangePicker extends React.Component<
|
||||
|
||||
return (
|
||||
<td {...props} {...others}>
|
||||
<span>{year}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key).getTestId()}>{year}</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@ -1705,7 +1725,8 @@ export class DateRangePicker extends React.Component<
|
||||
type,
|
||||
viewMode = 'days',
|
||||
label,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const __ = this.props.translate;
|
||||
const {startDate, endDate, editState, curDateFormat, curTimeFormat} =
|
||||
@ -1793,6 +1814,7 @@ export class DateRangePicker extends React.Component<
|
||||
timeRangeHeader="开始时间"
|
||||
embed={embed}
|
||||
status="start"
|
||||
testIdBuilder={testIdBuilder?.getChild('calendar-start')}
|
||||
/>
|
||||
)}
|
||||
{(!isTimeRange ||
|
||||
@ -1826,6 +1848,7 @@ export class DateRangePicker extends React.Component<
|
||||
timeRangeHeader="结束时间"
|
||||
embed={embed}
|
||||
status="end"
|
||||
testIdBuilder={testIdBuilder?.getChild('calendar-end')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -1964,7 +1987,8 @@ export class DateRangePicker extends React.Component<
|
||||
ranges,
|
||||
shortcuts,
|
||||
label,
|
||||
animation
|
||||
animation,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const useCalendarMobile =
|
||||
mobileUI && ['days', 'months', 'quarters'].indexOf(viewMode) > -1;
|
||||
@ -2059,6 +2083,7 @@ export class DateRangePicker extends React.Component<
|
||||
value={this.state.startInputValue || ''}
|
||||
disabled={disabled}
|
||||
readOnly={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('start')}
|
||||
/>
|
||||
<span
|
||||
className={cx('DateRangePicker-input-separator')}
|
||||
@ -2079,6 +2104,7 @@ export class DateRangePicker extends React.Component<
|
||||
value={this.state.endInputValue || ''}
|
||||
disabled={disabled}
|
||||
readOnly={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('end')}
|
||||
/>
|
||||
|
||||
{/* 指示游标 */}
|
||||
|
@ -129,10 +129,12 @@ export class GroupedSelection extends BaseSelection<BaseSelectionProps> {
|
||||
itemClassName,
|
||||
itemRender,
|
||||
multiple,
|
||||
labelField
|
||||
labelField,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const valueArray = this.valueArray;
|
||||
const itemTIB = testIdBuilder?.getChild(`item-${option.value || index}`);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -146,6 +148,7 @@ export class GroupedSelection extends BaseSelection<BaseSelectionProps> {
|
||||
!!~valueArray.indexOf(option) ? 'is-active' : ''
|
||||
)}
|
||||
onClick={() => this.toggleOption(option)}
|
||||
{...itemTIB?.getTestId()}
|
||||
>
|
||||
{multiple ? (
|
||||
<Checkbox
|
||||
@ -154,6 +157,7 @@ export class GroupedSelection extends BaseSelection<BaseSelectionProps> {
|
||||
disabled={disabled || option.disabled}
|
||||
labelClassName={labelClassName}
|
||||
description={option.description}
|
||||
testIdBuilder={itemTIB?.getChild('checkbox')}
|
||||
/>
|
||||
) : null}
|
||||
<div className={cx('GroupedSelection-itemLabel')}>
|
||||
|
@ -4,11 +4,12 @@
|
||||
* 功能很有必要。
|
||||
*/
|
||||
import React from 'react';
|
||||
import {autobind} from 'amis-core';
|
||||
import {TestIdBuilder, autobind} from 'amis-core';
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
forwardedRef: React.Ref<HTMLInputElement>;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface InputState {
|
||||
@ -60,7 +61,7 @@ class InputInner extends React.Component<InputProps, InputState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {forwardedRef, ...rest} = this.props;
|
||||
const {forwardedRef, testIdBuilder, ...rest} = this.props;
|
||||
|
||||
return (
|
||||
<input
|
||||
@ -73,6 +74,7 @@ class InputInner extends React.Component<InputProps, InputState> {
|
||||
onCompositionStart={this.handleComposition}
|
||||
onCompositionUpdate={this.handleComposition}
|
||||
onCompositionEnd={this.handleComposition}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -81,5 +83,6 @@ class InputInner extends React.Component<InputProps, InputState> {
|
||||
export default React.forwardRef<HTMLInputElement>((props, ref) => {
|
||||
return <InputInner {...props} forwardedRef={ref} />;
|
||||
}) as React.ComponentType<
|
||||
React.InputHTMLAttributes<HTMLInputElement> & {ref?: any}
|
||||
Omit<InputProps, 'forwardedRef'> &
|
||||
React.InputHTMLAttributes<HTMLInputElement> & {ref?: any}
|
||||
>;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {ThemeProps, buildTestId, themeable} from 'amis-core';
|
||||
import {TestIdBuilder, ThemeProps, themeable} from 'amis-core';
|
||||
import Input from './Input';
|
||||
import {autobind, ucFirst} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
@ -21,7 +21,7 @@ export interface InputBoxProps
|
||||
prefix?: JSX.Element;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
borderMode?: 'full' | 'half' | 'none';
|
||||
testid?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface InputBoxState {
|
||||
@ -88,7 +88,7 @@ export class InputBox extends React.Component<InputBoxProps, InputBoxState> {
|
||||
borderMode,
|
||||
onClick,
|
||||
mobileUI,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
const isFocused = this.state.isFocused;
|
||||
@ -116,7 +116,7 @@ export class InputBox extends React.Component<InputBoxProps, InputBoxState> {
|
||||
onBlur={this.handleBlur}
|
||||
size={12}
|
||||
disabled={disabled}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
/>
|
||||
|
||||
{children}
|
||||
|
@ -2,6 +2,7 @@ import {ThemeProps, themeable} from 'amis-core';
|
||||
import React from 'react';
|
||||
import {Options, Option} from 'amis-core';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface ListMenuProps extends ThemeProps, LocaleProps {
|
||||
options: Options;
|
||||
@ -14,6 +15,7 @@ export interface ListMenuProps extends ThemeProps, LocaleProps {
|
||||
getItemProps: (props: {item: Option; index: number}) => any;
|
||||
prefix?: JSX.Element;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
interface RenderResult {
|
||||
@ -37,7 +39,8 @@ export class ListMenu extends React.Component<ListMenuProps> {
|
||||
highlightIndex,
|
||||
selectedOptions,
|
||||
mobileUI,
|
||||
onSelect
|
||||
onSelect,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
if (Array.isArray(option.children) && option.children.length) {
|
||||
@ -74,6 +77,7 @@ export class ListMenu extends React.Component<ListMenuProps> {
|
||||
)}
|
||||
key={index}
|
||||
onClick={onSelect ? (e: any) => onSelect(e, option) : undefined}
|
||||
{...testIdBuilder?.getChild(option.value || index).getTestId()}
|
||||
{...getItemProps({
|
||||
item: option,
|
||||
index: index
|
||||
|
@ -10,7 +10,14 @@ import getMiniDecimal, {
|
||||
} from '@rc-component/mini-decimal';
|
||||
|
||||
import {Icon} from './icons';
|
||||
import {ThemeProps, themeable, isNumeric, autobind, ucFirst} from 'amis-core';
|
||||
import {
|
||||
ThemeProps,
|
||||
themeable,
|
||||
isNumeric,
|
||||
autobind,
|
||||
ucFirst,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
|
||||
export type ValueType = string | number;
|
||||
|
||||
@ -75,6 +82,8 @@ export interface NumberProps extends ThemeProps {
|
||||
* 数字输入框类名
|
||||
*/
|
||||
inputControlClassName?: string;
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface NumberState {
|
||||
@ -319,7 +328,8 @@ export class NumberInput extends React.Component<NumberProps, NumberState> {
|
||||
keyboard,
|
||||
inputControlClassName,
|
||||
mobileUI,
|
||||
name
|
||||
name,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const precisionProps: any = {
|
||||
precision: NumberInput.normalizePrecision(precision, step)
|
||||
@ -359,6 +369,7 @@ export class NumberInput extends React.Component<NumberProps, NumberState> {
|
||||
stringMode={this.isBig ? true : false}
|
||||
keyboard={keyboard}
|
||||
{...precisionProps}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -5,7 +5,12 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import isInteger from 'lodash/isInteger';
|
||||
import {localeable, LocaleProps, resolveEventData} from 'amis-core';
|
||||
import {
|
||||
localeable,
|
||||
LocaleProps,
|
||||
resolveEventData,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {themeable, ThemeProps} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
@ -120,6 +125,7 @@ export interface PaginationProps
|
||||
ThemeProps,
|
||||
LocaleProps {
|
||||
popOverContainer?: any;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
export interface PaginationState {
|
||||
pageNum: string;
|
||||
@ -189,7 +195,7 @@ export class Pagination extends React.Component<
|
||||
* @param page 页码
|
||||
*/
|
||||
renderPageItem(page: number) {
|
||||
const {classnames: cx, activePage} = this.props;
|
||||
const {classnames: cx, activePage, testIdBuilder} = this.props;
|
||||
const {perPage} = this.state;
|
||||
|
||||
return (
|
||||
@ -200,7 +206,12 @@ export class Pagination extends React.Component<
|
||||
'is-active': page === activePage
|
||||
})}
|
||||
>
|
||||
<a role="button">{page}</a>
|
||||
<a
|
||||
role="button"
|
||||
{...testIdBuilder?.getChild(`page-${page}`).getTestId()}
|
||||
>
|
||||
{page}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@ -212,7 +223,12 @@ export class Pagination extends React.Component<
|
||||
* @param page 页码
|
||||
*/
|
||||
renderEllipsis(key: string) {
|
||||
const {classnames: cx, activePage, ellipsisPageGap} = this.props;
|
||||
const {
|
||||
classnames: cx,
|
||||
activePage,
|
||||
ellipsisPageGap,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {perPage} = this.state;
|
||||
const lastPage = this.getLastPage();
|
||||
const gap: number =
|
||||
@ -240,6 +256,7 @@ export class Pagination extends React.Component<
|
||||
isPrevEllipsis ? 'backward' : 'forward'
|
||||
);
|
||||
}}
|
||||
{...testIdBuilder?.getChild(key).getTestId()}
|
||||
>
|
||||
<a role="button">...</a>
|
||||
<span className="icon">{jumpContent}</span>
|
||||
@ -382,7 +399,8 @@ export class Pagination extends React.Component<
|
||||
popOverContainerSelector,
|
||||
mobileUI,
|
||||
size,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
let maxButtons = this.props.maxButtons;
|
||||
const {pageNum, perPage, internalPageNum} = this.state;
|
||||
@ -402,6 +420,7 @@ export class Pagination extends React.Component<
|
||||
onKeyUp={this.handleSimpleKeyUp}
|
||||
onBlur={this.handleSimpleBlur}
|
||||
value={internalPageNum}
|
||||
{...testIdBuilder?.getChild('simple-input').getTestId()}
|
||||
/>
|
||||
/
|
||||
<span className={cx('Pagination-simplego-right')} key="go-right">
|
||||
@ -419,6 +438,7 @@ export class Pagination extends React.Component<
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<ul
|
||||
key="pager-items"
|
||||
@ -445,7 +465,7 @@ export class Pagination extends React.Component<
|
||||
}}
|
||||
key="prev"
|
||||
>
|
||||
<span>
|
||||
<span {...testIdBuilder?.getChild(`go-prev`).getTestId()}>
|
||||
<Icon icon="left-arrow" className="icon" />
|
||||
</span>
|
||||
</li>
|
||||
@ -466,7 +486,7 @@ export class Pagination extends React.Component<
|
||||
}}
|
||||
key="next"
|
||||
>
|
||||
<span>
|
||||
<span {...testIdBuilder?.getChild(`go-next`).getTestId()}>
|
||||
<Icon icon="right-arrow" className="icon" />
|
||||
</span>
|
||||
</li>
|
||||
@ -557,7 +577,7 @@ export class Pagination extends React.Component<
|
||||
}}
|
||||
key="prev"
|
||||
>
|
||||
<span>
|
||||
<span {...testIdBuilder?.getChild('go-prev').getTestId()}>
|
||||
<Icon icon="left-arrow" className="icon" />
|
||||
</span>
|
||||
</li>
|
||||
@ -576,7 +596,7 @@ export class Pagination extends React.Component<
|
||||
}}
|
||||
key="next"
|
||||
>
|
||||
<span>
|
||||
<span {...testIdBuilder?.getChild('go-next').getTestId()}>
|
||||
<Icon icon="right-arrow" className="icon" />
|
||||
</span>
|
||||
</li>
|
||||
@ -613,6 +633,7 @@ export class Pagination extends React.Component<
|
||||
this.handlePageNumChange(v, perPage);
|
||||
}}
|
||||
value={pageNum}
|
||||
{...testIdBuilder?.getChild('go-input').getTestId()}
|
||||
/>
|
||||
<span
|
||||
className={cx('Pagination-inputGroup-right')}
|
||||
@ -624,6 +645,7 @@ export class Pagination extends React.Component<
|
||||
this.setState({pageNum: ''});
|
||||
this.handlePageNumChange(+pageNum, perPage);
|
||||
}}
|
||||
{...testIdBuilder?.getChild('go').getTestId()}
|
||||
>
|
||||
{__('Pagination.go')}
|
||||
</span>
|
||||
@ -649,6 +671,7 @@ export class Pagination extends React.Component<
|
||||
});
|
||||
this.handlePageNumChange(1, p.value);
|
||||
}}
|
||||
{...testIdBuilder?.getChild('perpage').getTestId()}
|
||||
/>
|
||||
);
|
||||
// total或者lastpage不存在,不渲染总数
|
||||
@ -667,6 +690,7 @@ export class Pagination extends React.Component<
|
||||
{disabled: disabled},
|
||||
className
|
||||
)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{layoutList.map(layoutItem => {
|
||||
if (layoutItem === PaginationWidget.Pager) {
|
||||
|
@ -22,6 +22,7 @@ import {value2array, OptionProps, Option} from './Select';
|
||||
import chunk from 'lodash/chunk';
|
||||
import {ClassNamesFn, themeable} from 'amis-core';
|
||||
import {columnsSplit} from 'amis-core';
|
||||
import {TestIdBuilder} from 'amis-core';
|
||||
|
||||
interface RadioProps extends OptionProps {
|
||||
id?: string;
|
||||
@ -42,6 +43,7 @@ interface RadioProps extends OptionProps {
|
||||
classPrefix: string;
|
||||
classnames: ClassNamesFn;
|
||||
renderLabel?: (item: Option, props: RadioProps) => JSX.Element;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
const defaultLabelRender = (item: Option, props: RadioProps) => (
|
||||
@ -123,8 +125,10 @@ export class Radios extends React.Component<RadioProps, any> {
|
||||
level,
|
||||
btnActiveLevel,
|
||||
classPrefix: ns,
|
||||
testIdBuilder,
|
||||
renderLabel = defaultLabelRender
|
||||
} = this.props;
|
||||
const itemTestIdBuilder = testIdBuilder?.getChild(option.value || index);
|
||||
|
||||
if (optionType === 'button') {
|
||||
const active = !!~valueArray.indexOf(option);
|
||||
@ -153,6 +157,7 @@ export class Radios extends React.Component<RadioProps, any> {
|
||||
description={option.description}
|
||||
inline={inline}
|
||||
labelClassName={labelClassName}
|
||||
testIdBuilder={itemTestIdBuilder}
|
||||
>
|
||||
{renderLabel(option, this.props)}
|
||||
</Checkbox>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {ThemeProps, themeable} from 'amis-core';
|
||||
import {TestIdBuilder, ThemeProps, themeable} from 'amis-core';
|
||||
import React from 'react';
|
||||
import omit from 'lodash/omit';
|
||||
import isInteger from 'lodash/isInteger';
|
||||
@ -30,6 +30,7 @@ export interface ResultBoxProps
|
||||
showInvalidMatch?: boolean;
|
||||
popOverContainer?: any;
|
||||
showArrow?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
@ -116,7 +117,8 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
itemRender,
|
||||
classnames: cx,
|
||||
showInvalidMatch,
|
||||
popOverContainer
|
||||
popOverContainer,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
@ -143,6 +145,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
{label: `+ ${tags.length - maxVisibleCount} ...`}
|
||||
].map((item, index) => {
|
||||
const isShowInvalid = showInvalidMatch && item?.__unmatched;
|
||||
const itemTIB = testIdBuilder?.getChild(item.value || index);
|
||||
return index === maxVisibleCount ? (
|
||||
<TooltipWrapper
|
||||
key={tags.length}
|
||||
@ -162,11 +165,16 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
'is-invalid': showInvalidMatch && item?.__unmatched
|
||||
})}
|
||||
key={itemIndex}
|
||||
{...itemTIB?.getTestId()}
|
||||
>
|
||||
<span className={cx('ResultBox-valueLabel')}>
|
||||
{itemRender(item)}
|
||||
</span>
|
||||
<a data-index={itemIndex} onClick={this.removeItem}>
|
||||
<a
|
||||
data-index={itemIndex}
|
||||
onClick={this.removeItem}
|
||||
{...itemTIB?.getChild('close').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
@ -197,11 +205,16 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
className={cx('ResultBox-value', {
|
||||
'is-invalid': isShowInvalid
|
||||
})}
|
||||
{...itemTIB?.getTestId()}
|
||||
>
|
||||
<span className={cx('ResultBox-valueLabel')}>
|
||||
{itemRender(item)}
|
||||
</span>
|
||||
<a data-index={index} onClick={this.removeItem}>
|
||||
<a
|
||||
data-index={index}
|
||||
onClick={this.removeItem}
|
||||
{...itemTIB?.getChild('close').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
@ -210,26 +223,36 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
});
|
||||
}
|
||||
|
||||
return tags.map((item, index) => (
|
||||
<TooltipWrapper
|
||||
container={popOverContainer}
|
||||
placement={'top'}
|
||||
tooltip={item['label']}
|
||||
trigger={'hover'}
|
||||
key={index}
|
||||
>
|
||||
<div
|
||||
className={cx('ResultBox-value', {
|
||||
'is-invalid': showInvalidMatch && item?.__unmatched
|
||||
})}
|
||||
return tags.map((item, index) => {
|
||||
const itemTIB = testIdBuilder?.getChild(index);
|
||||
return (
|
||||
<TooltipWrapper
|
||||
container={popOverContainer}
|
||||
placement={'top'}
|
||||
tooltip={item['label']}
|
||||
trigger={'hover'}
|
||||
key={index}
|
||||
>
|
||||
<span className={cx('ResultBox-valueLabel')}>{itemRender(item)}</span>
|
||||
<a data-index={index} onClick={this.removeItem}>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</TooltipWrapper>
|
||||
));
|
||||
<div
|
||||
className={cx('ResultBox-value', {
|
||||
'is-invalid': showInvalidMatch && item?.__unmatched
|
||||
})}
|
||||
{...itemTIB?.getTestId()}
|
||||
>
|
||||
<span className={cx('ResultBox-valueLabel')}>
|
||||
{itemRender(item)}
|
||||
</span>
|
||||
<a
|
||||
data-index={index}
|
||||
onClick={this.removeItem}
|
||||
{...itemTIB?.getChild('close').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</TooltipWrapper>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -265,6 +288,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
overflowTagPopover,
|
||||
showArrow,
|
||||
popOverContainer,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
const isFocused = this.state.isFocused;
|
||||
@ -286,6 +310,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
onKeyPress={allowInput ? undefined : onKeyPress}
|
||||
onFocus={allowInput ? undefined : onFocus}
|
||||
onBlur={allowInput ? undefined : onBlur}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<div className={cx('ResultBox-value-wrap')}>
|
||||
{Array.isArray(result) && result.length ? (
|
||||
@ -320,6 +345,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
)}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
testIdBuilder={testIdBuilder?.getChild('input')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -335,6 +361,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
className={cx('ResultBox-clear', {
|
||||
'ResultBox-clear-with-arrow': hasDropDownArrow
|
||||
})}
|
||||
{...testIdBuilder?.getChild('clear').getTestId()}
|
||||
>
|
||||
<div className={cx('ResultBox-clear-wrap')}>
|
||||
<Icon icon="input-clear" className="icon" />
|
||||
|
@ -15,6 +15,8 @@ import {LocaleProps, localeable, ClassNamesFn} from 'amis-core';
|
||||
import TransferSearch from './TransferSearch';
|
||||
import VirtualList, {AutoSizer} from './virtual-list';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface ResultListProps extends ThemeProps, LocaleProps {
|
||||
className?: string;
|
||||
value?: Array<Option>;
|
||||
@ -33,6 +35,7 @@ export interface ResultListProps extends ThemeProps, LocaleProps {
|
||||
itemHeight?: number; // 每个选项的高度,主要用于虚拟渲染
|
||||
virtualThreshold?: number; // 数据量多大的时候开启虚拟渲染
|
||||
showInvalidMatch?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface ItemRenderStates {
|
||||
@ -275,9 +278,10 @@ export class ResultList extends React.Component<
|
||||
sortable,
|
||||
labelField,
|
||||
translate: __,
|
||||
showInvalidMatch
|
||||
showInvalidMatch,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const itemTIB = testIdBuilder?.getChild(`item-${option.value || index}`);
|
||||
return (
|
||||
<div
|
||||
style={styles}
|
||||
@ -309,6 +313,7 @@ export class ResultList extends React.Component<
|
||||
onClick={(e: React.MouseEvent<HTMLElement>) =>
|
||||
this.handleCloseItem(e, option)
|
||||
}
|
||||
{...itemTIB?.getChild('close').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
|
@ -178,7 +178,8 @@ export class BaseResultTableSelection extends BaseSelection<
|
||||
translate: __,
|
||||
placeholder,
|
||||
virtualThreshold,
|
||||
itemHeight
|
||||
itemHeight,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const {searching, tableOptions, searchTableOptions} = this.state;
|
||||
@ -208,6 +209,10 @@ export class BaseResultTableSelection extends BaseSelection<
|
||||
rowIndex: number
|
||||
) => {
|
||||
const raw = cellRender(column, option, colIndex, rowIndex);
|
||||
const itemTIB = testIdBuilder?.getChild(
|
||||
`item-${option.value || rowIndex}`
|
||||
);
|
||||
|
||||
if (colIndex === columns.length - 1) {
|
||||
return (
|
||||
<>
|
||||
@ -219,6 +224,7 @@ export class BaseResultTableSelection extends BaseSelection<
|
||||
e.stopPropagation();
|
||||
this.handleCloseItem(option);
|
||||
}}
|
||||
{...itemTIB?.getChild(`close`).getTestId()}
|
||||
>
|
||||
<CloseIcon />
|
||||
</span>
|
||||
|
@ -288,7 +288,8 @@ export class BaseResultTreeList extends React.Component<
|
||||
placeholder,
|
||||
virtualThreshold,
|
||||
itemHeight,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const {treeOptions, searching, searchTreeOptions} = this.state;
|
||||
@ -309,6 +310,7 @@ export class BaseResultTreeList extends React.Component<
|
||||
onDelete={(option: Option) => this.deleteTreeChecked(option)}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
) : (
|
||||
<div className={cx('Selections-placeholder')}>{__(placeholder)}</div>
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import isInteger from 'lodash/isInteger';
|
||||
import debounce from 'lodash/debounce';
|
||||
import moment from 'moment';
|
||||
import {ThemeProps, themeable} from 'amis-core';
|
||||
import {TestIdBuilder, ThemeProps, themeable} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
import {uncontrollable} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
@ -56,6 +56,7 @@ export interface SearchBoxProps
|
||||
history?: SearchHistoryOptions;
|
||||
clearAndSubmit?: boolean;
|
||||
loading?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface SearchBoxState {
|
||||
@ -297,7 +298,8 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
mobileUI,
|
||||
translate: __,
|
||||
loading,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {isFocused, inputValue} = this.state;
|
||||
const {enable} = this.getHistoryOptions();
|
||||
@ -315,6 +317,7 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
{'is-mobile': mobileUI}
|
||||
)}
|
||||
style={style}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<Input
|
||||
name={name}
|
||||
@ -327,10 +330,15 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
onBlur={this.handleBlur}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
testIdBuilder={testIdBuilder?.getChild('input')}
|
||||
/>
|
||||
|
||||
{!mini && clearable && inputValue && !disabled ? (
|
||||
<div className={cx('SearchBox-clearable')} onClick={this.handleClear}>
|
||||
<div
|
||||
className={cx('SearchBox-clearable')}
|
||||
onClick={this.handleClear}
|
||||
{...testIdBuilder?.getChild('clear').getTestId()}
|
||||
>
|
||||
<Icon icon="input-clear" className="icon" />
|
||||
</div>
|
||||
) : null}
|
||||
@ -341,6 +349,7 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
'SearchBox-searchBtn--loading': loading
|
||||
})}
|
||||
onClick={this.handleSearch}
|
||||
{...testIdBuilder?.getChild('search').getTestId()}
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner
|
||||
|
@ -9,8 +9,7 @@ import {
|
||||
getOptionValue,
|
||||
getOptionValueBindField,
|
||||
labelToString,
|
||||
uncontrollable,
|
||||
buildTestId
|
||||
uncontrollable
|
||||
} from 'amis-core';
|
||||
import React from 'react';
|
||||
import isInteger from 'lodash/isInteger';
|
||||
@ -41,7 +40,7 @@ import Checkbox from './Checkbox';
|
||||
import Input from './Input';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import Spinner, {SpinnerExtraProps} from './Spinner';
|
||||
import type {Option, Options} from 'amis-core';
|
||||
import type {Option, Options, TestIdBuilder} from 'amis-core';
|
||||
import {RemoteOptionsProps, withRemoteConfig} from './WithRemoteConfig';
|
||||
import Picker from './Picker';
|
||||
import PopUp from './PopUp';
|
||||
@ -96,6 +95,7 @@ export interface OptionProps {
|
||||
onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
|
||||
removable?: boolean;
|
||||
onDelete?: (value: Option) => void;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export type OptionValue = string | number | null | undefined | Option;
|
||||
@ -324,7 +324,6 @@ export interface SelectProps
|
||||
LocaleProps,
|
||||
SpinnerExtraProps {
|
||||
className?: string;
|
||||
testid?: string;
|
||||
popoverClassName?: string;
|
||||
showInvalidMatch?: boolean;
|
||||
creatable: boolean;
|
||||
@ -1016,7 +1015,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
mobileUI,
|
||||
filterOption = defaultFilterOption,
|
||||
overlay,
|
||||
loading
|
||||
loading,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {selection} = this.state;
|
||||
|
||||
@ -1061,6 +1061,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
}
|
||||
|
||||
let label = labelToString(item[labelField]);
|
||||
let optTestIdBudr = testIdBuilder?.getChild(`option-${label || index}`);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -1079,6 +1080,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
'is-highlight': highlightedIndex === index,
|
||||
'is-active': checked
|
||||
})}
|
||||
{...optTestIdBudr?.getTestId()}
|
||||
>
|
||||
{renderMenu ? (
|
||||
multiple ? (
|
||||
@ -1089,6 +1091,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
this.handleChange(item);
|
||||
}}
|
||||
disabled={item.disabled}
|
||||
testIdBuilder={optTestIdBudr?.getChild('chekbx')}
|
||||
>
|
||||
{renderMenu(item, {
|
||||
multiple,
|
||||
@ -1137,6 +1140,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
<span
|
||||
className={cx('Select-option-content')}
|
||||
title={typeof label === 'string' ? label : ''}
|
||||
{...optTestIdBudr?.getChild('content').getTestId()}
|
||||
>
|
||||
{item.disabled
|
||||
? label
|
||||
@ -1322,7 +1326,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
borderMode,
|
||||
mobileUI,
|
||||
hasError,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
loadingConfig
|
||||
} = this.props;
|
||||
|
||||
@ -1354,7 +1358,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
onClick={this.toggle}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
className={cx(
|
||||
`Select`,
|
||||
{
|
||||
@ -1384,7 +1388,11 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
(Array.isArray(value)
|
||||
? value.length
|
||||
: value != null && value !== resetValue) ? (
|
||||
<a onClick={this.clearValue} className={cx('Select-clear')}>
|
||||
<a
|
||||
onClick={this.clearValue}
|
||||
className={cx('Select-clear')}
|
||||
{...testIdBuilder?.getChild('clear').getTestId()}
|
||||
>
|
||||
<Icon icon="input-clear" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
@ -1398,7 +1406,10 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<span className={cx('Select-arrow')}>
|
||||
<span
|
||||
className={cx('Select-arrow')}
|
||||
{...testIdBuilder?.getChild('arrow').getTestId()}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
{isOpen ? this.renderOuter(options) : null}
|
||||
|
@ -23,6 +23,8 @@ import {
|
||||
import Checkbox from './Checkbox';
|
||||
import {Option, Options} from './Select';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface BaseSelectionProps extends ThemeProps, LocaleProps {
|
||||
options: Options;
|
||||
className?: string;
|
||||
@ -49,6 +51,7 @@ export interface BaseSelectionProps extends ThemeProps, LocaleProps {
|
||||
placeholderRender?: (props: any) => JSX.Element | null;
|
||||
checkAll?: boolean;
|
||||
checkAllLabel?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface ItemRenderStates {
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {ClassNamesFn, buildTestId, themeable} from 'amis-core';
|
||||
import {ClassNamesFn, TestIdBuilder, themeable} from 'amis-core';
|
||||
import {Spinner} from './Spinner';
|
||||
|
||||
const sizeMap = {
|
||||
@ -44,7 +44,7 @@ interface SwitchProps {
|
||||
root?: string;
|
||||
show?: boolean;
|
||||
};
|
||||
testid?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class Switch extends React.PureComponent<SwitchProps, any> {
|
||||
@ -88,7 +88,7 @@ export class Switch extends React.PureComponent<SwitchProps, any> {
|
||||
classnames: cx,
|
||||
loading,
|
||||
loadingConfig,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -112,7 +112,7 @@ export class Switch extends React.PureComponent<SwitchProps, any> {
|
||||
'is-disabled': isDisabled
|
||||
})}
|
||||
data-role="switch"
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
@ -77,7 +77,8 @@ export class TableSelection extends BaseSelection<TableSelectionProps, any> {
|
||||
value,
|
||||
disabled,
|
||||
option2value,
|
||||
multiple
|
||||
multiple,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
let columns = this.getColumns();
|
||||
let valueArray = BaseSelection.value2array(value, options, option2value);
|
||||
@ -108,6 +109,7 @@ export class TableSelection extends BaseSelection<TableSelectionProps, any> {
|
||||
onChange={this.toggleAll}
|
||||
checked={partialChecked}
|
||||
partial={partialChecked && !allChecked}
|
||||
testIdBuilder={testIdBuilder?.getChild('check-all')}
|
||||
/>
|
||||
</th>
|
||||
) : null}
|
||||
@ -140,10 +142,12 @@ export class TableSelection extends BaseSelection<TableSelectionProps, any> {
|
||||
multiple,
|
||||
translate: __,
|
||||
itemClassName,
|
||||
resultMode
|
||||
resultMode,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const checked = valueArray.indexOf(option) !== -1;
|
||||
const itemTIB = testIdBuilder?.getChild(`item-${option.value || rowIndex}`);
|
||||
|
||||
return (
|
||||
<tr
|
||||
@ -171,7 +175,12 @@ export class TableSelection extends BaseSelection<TableSelectionProps, any> {
|
||||
this.toggleOption(option);
|
||||
}}
|
||||
>
|
||||
<Checkbox size="sm" checked={checked} disabled={disabled} />
|
||||
<Checkbox
|
||||
size="sm"
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
testIdBuilder={itemTIB}
|
||||
/>
|
||||
</td>
|
||||
) : null}
|
||||
{columns.map((column, colIndex) => (
|
||||
|
@ -5,7 +5,13 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {ClassName, localeable, LocaleProps, Schema} from 'amis-core';
|
||||
import {
|
||||
ClassName,
|
||||
localeable,
|
||||
LocaleProps,
|
||||
Schema,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import Transition, {ENTERED, ENTERING} from 'react-transition-group/Transition';
|
||||
import {themeable, ThemeProps, noop} from 'amis-core';
|
||||
import {uncontrollable} from 'amis-core';
|
||||
@ -59,6 +65,7 @@ export interface TabProps extends ThemeProps {
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
swipeable?: boolean;
|
||||
onSelect?: (eventKey: string | number) => void;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
class TabComponent extends React.PureComponent<TabProps> {
|
||||
@ -113,7 +120,8 @@ class TabComponent extends React.PureComponent<TabProps> {
|
||||
children,
|
||||
className,
|
||||
swipeable,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -140,6 +148,7 @@ class TabComponent extends React.PureComponent<TabProps> {
|
||||
onTouchMove={swipeable && mobileUI ? this.onTouchMove : noop}
|
||||
onTouchEnd={swipeable && mobileUI ? this.onTouchEnd : noop}
|
||||
onTouchCancel={swipeable && mobileUI ? this.onTouchEnd : noop}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
@ -181,6 +190,7 @@ export interface TabsProps extends ThemeProps, LocaleProps {
|
||||
collapseBtnLabel?: string;
|
||||
popOverContainer?: any;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface IDragInfo {
|
||||
@ -586,7 +596,8 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
draggable,
|
||||
showTip,
|
||||
showTipClassName,
|
||||
editable
|
||||
editable,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@ -646,7 +657,9 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
)}
|
||||
</a>
|
||||
);
|
||||
|
||||
const tabTestIdBuidr = testIdBuilder?.getChild(
|
||||
`tab-${typeof title === 'string' ? title : index}`
|
||||
);
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
@ -662,6 +675,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
typeof title === 'string' &&
|
||||
this.handleStartEdit(index, title);
|
||||
}}
|
||||
{...tabTestIdBuidr?.getChild('link').getTestId()}
|
||||
>
|
||||
{showTip ? (
|
||||
<TooltipWrapper
|
||||
@ -684,6 +698,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
this.props.onClose &&
|
||||
this.props.onClose(index, eventKey ?? index);
|
||||
}}
|
||||
{...tabTestIdBuidr?.getChild('close').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className={cx('Tabs-link-close-icon')} />
|
||||
</span>
|
||||
@ -722,7 +737,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
}
|
||||
|
||||
renderArrow(type: 'left' | 'right') {
|
||||
const {mode: dMode, tabsMode} = this.props;
|
||||
const {mode: dMode, tabsMode, testIdBuilder} = this.props;
|
||||
const mode = tabsMode || dMode;
|
||||
if (['vertical', 'sidebar'].includes(mode)) {
|
||||
return;
|
||||
@ -738,6 +753,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
'Tabs-linksContainer-arrow--' + type,
|
||||
disabled && 'Tabs-linksContainer-arrow--disabled'
|
||||
)}
|
||||
{...testIdBuilder?.getChild(`arrow-${type}`).getTestId()}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</div>
|
||||
@ -835,7 +851,8 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
draggable,
|
||||
sidePosition,
|
||||
addBtnText,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const {isOverflow} = this.state;
|
||||
@ -851,6 +868,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
<div
|
||||
className={cx('Tabs-addable')}
|
||||
onClick={() => this.handleAddBtn()}
|
||||
{...testIdBuilder?.getChild('add-tab').getTestId()}
|
||||
>
|
||||
<Icon icon="plus" className={cx('Tabs-addable-icon')} />
|
||||
{addBtnText}
|
||||
@ -871,6 +889,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{!['vertical', 'sidebar', 'chrome'].includes(mode) ? (
|
||||
<div
|
||||
@ -885,6 +904,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
'Tabs-linksContainer',
|
||||
isOverflow && 'Tabs-linksContainer--overflow'
|
||||
)}
|
||||
{...testIdBuilder?.getChild('links').getTestId()}
|
||||
>
|
||||
{!mobileUI ? this.renderArrow('left') : null}
|
||||
<div className={cx('Tabs-linksContainer-main')}>
|
||||
@ -911,6 +931,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
role="tablist"
|
||||
{...testIdBuilder?.getChild('links').getTestId()}
|
||||
>
|
||||
{this.renderNavs()}
|
||||
{additionBtns}
|
||||
@ -925,7 +946,11 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
})}
|
||||
</div>
|
||||
{draggable && (
|
||||
<div className={cx('Tabs-drag-tip')} ref={this.dragTipRef} />
|
||||
<div
|
||||
className={cx('Tabs-drag-tip')}
|
||||
ref={this.dragTipRef}
|
||||
{...testIdBuilder?.getChild('drag').getTestId()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -177,10 +177,12 @@ export class TabsTransfer extends React.Component<
|
||||
activeKey,
|
||||
options: optionsConfig,
|
||||
valueField = 'value',
|
||||
labelField = 'label'
|
||||
labelField = 'label',
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const options = searchResult || [];
|
||||
const mode = searchResultMode || selectMode; // 没有配置时默认和左侧选项展示形式一致
|
||||
const searchTIB = testIdBuilder?.getChild('search-result');
|
||||
|
||||
const activeTab = optionsConfig[activeKey];
|
||||
return mode === 'table' ? (
|
||||
@ -196,6 +198,7 @@ export class TabsTransfer extends React.Component<
|
||||
cellRender={cellRender}
|
||||
itemHeight={itemHeight}
|
||||
virtualThreshold={virtualThreshold}
|
||||
testIdBuilder={searchTIB}
|
||||
/>
|
||||
) : mode === 'tree' ? (
|
||||
<Tree
|
||||
@ -221,6 +224,7 @@ export class TabsTransfer extends React.Component<
|
||||
}
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
testIdBuilder={searchTIB}
|
||||
/>
|
||||
) : mode === 'chained' ? (
|
||||
<ChainedCheckboxes
|
||||
@ -243,6 +247,7 @@ export class TabsTransfer extends React.Component<
|
||||
virtualThreshold={virtualThreshold}
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
testIdBuilder={searchTIB}
|
||||
/>
|
||||
) : (
|
||||
<ListCheckboxes
|
||||
@ -265,6 +270,7 @@ export class TabsTransfer extends React.Component<
|
||||
virtualThreshold={virtualThreshold}
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
testIdBuilder={searchTIB}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -279,7 +285,8 @@ export class TabsTransfer extends React.Component<
|
||||
translate: __,
|
||||
ctx,
|
||||
mobileUI,
|
||||
searchable
|
||||
searchable,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const showOptions = options.filter(item => item.visible !== false);
|
||||
|
||||
@ -297,6 +304,7 @@ export class TabsTransfer extends React.Component<
|
||||
className={cx('TabsTransfer-tabs')}
|
||||
onSelect={this.handleTabChange}
|
||||
activeKey={activeKey}
|
||||
testIdBuilder={testIdBuilder?.getChild('tabs')}
|
||||
>
|
||||
{showOptions.map((option, index) => (
|
||||
<Tab
|
||||
@ -307,6 +315,7 @@ export class TabsTransfer extends React.Component<
|
||||
createObject(ctx, option)
|
||||
)}
|
||||
className="TabsTransfer-tab"
|
||||
testIdBuilder={testIdBuilder?.getChild(`tab-${index}`)}
|
||||
>
|
||||
{option.searchable || searchable ? (
|
||||
<div
|
||||
@ -359,9 +368,11 @@ export class TabsTransfer extends React.Component<
|
||||
initiallyOpen = true,
|
||||
valueField = 'value',
|
||||
labelField = 'label',
|
||||
deferField = 'defer'
|
||||
deferField = 'defer',
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const selectMode = option.selectMode || this.props.selectMode;
|
||||
const selTIB = testIdBuilder?.getChild('selection');
|
||||
|
||||
return selectMode === 'table' ? (
|
||||
<TableCheckboxes
|
||||
@ -379,6 +390,7 @@ export class TabsTransfer extends React.Component<
|
||||
virtualThreshold={virtualThreshold}
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
testIdBuilder={selTIB}
|
||||
/>
|
||||
) : selectMode === 'tree' ? (
|
||||
<Tree
|
||||
@ -409,6 +421,7 @@ export class TabsTransfer extends React.Component<
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
initiallyOpen={initiallyOpen}
|
||||
testIdBuilder={selTIB}
|
||||
/>
|
||||
) : selectMode === 'chained' ? (
|
||||
<ChainedCheckboxes
|
||||
@ -434,6 +447,7 @@ export class TabsTransfer extends React.Component<
|
||||
virtualThreshold={virtualThreshold}
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
testIdBuilder={selTIB}
|
||||
/>
|
||||
) : selectMode === 'associated' ? (
|
||||
<AssociatedCheckboxes
|
||||
@ -464,6 +478,7 @@ export class TabsTransfer extends React.Component<
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
deferField={deferField}
|
||||
testIdBuilder={selTIB}
|
||||
/>
|
||||
) : (
|
||||
<ListCheckboxes
|
||||
@ -488,6 +503,7 @@ export class TabsTransfer extends React.Component<
|
||||
virtualThreshold={virtualThreshold}
|
||||
valueField={valueField}
|
||||
labelField={labelField}
|
||||
testIdBuilder={selTIB}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import BaseTextArea from 'react-textarea-autosize';
|
||||
import {buildTestId, localeable, LocaleProps} from 'amis-core';
|
||||
import {localeable, LocaleProps, TestIdBuilder} from 'amis-core';
|
||||
import {themeable, ThemeProps} from 'amis-core';
|
||||
import {autobind, ucFirst} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
@ -58,7 +58,7 @@ export interface TextAreaProps extends ThemeProps, LocaleProps {
|
||||
placeholder?: string;
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
testid?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
|
||||
forwardRef?: {current: HTMLTextAreaElement | null};
|
||||
}
|
||||
@ -185,7 +185,7 @@ export class Textarea extends React.Component<TextAreaProps, TextAreaState> {
|
||||
maxLength,
|
||||
showCounter,
|
||||
clearable,
|
||||
testid
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const counter = showCounter ? this.valueToString(value).length : 0;
|
||||
|
||||
@ -220,7 +220,7 @@ export class Textarea extends React.Component<TextAreaProps, TextAreaState> {
|
||||
onChange={this.handleChange}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
/>
|
||||
|
||||
{clearable && !disabled && value ? (
|
||||
|
@ -25,6 +25,8 @@ import ResultTreeList from './ResultTreeList';
|
||||
import {SpinnerExtraProps} from './Spinner';
|
||||
import Pagination from './Pagination';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export type SelectMode =
|
||||
| 'table'
|
||||
| 'group'
|
||||
@ -165,6 +167,7 @@ export interface TransferProps
|
||||
* ui级联关系,true代表级联选中,false代表不级联,默认为true
|
||||
*/
|
||||
autoCheckChildren?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface TransferState {
|
||||
@ -540,7 +543,8 @@ export class Transfer<
|
||||
translate: __,
|
||||
searchPlaceholder = __('Transfer.searchKeyword'),
|
||||
mobileUI,
|
||||
valueField = 'value'
|
||||
valueField = 'value',
|
||||
testIdBuilder
|
||||
} = props;
|
||||
|
||||
if (selectRender) {
|
||||
@ -585,6 +589,7 @@ export class Transfer<
|
||||
partial={checkedPartial && !checkedAll}
|
||||
onChange={props.onToggleAll || this.toggleAll}
|
||||
size="sm"
|
||||
testIdBuilder={testIdBuilder?.getChild('toggle-all')}
|
||||
/>
|
||||
) : null}
|
||||
{__(selectTitle || 'Transfer.available')}
|
||||
@ -605,6 +610,7 @@ export class Transfer<
|
||||
'Transfer-checkAll',
|
||||
disabled || !options.length ? 'is-disabled' : ''
|
||||
)}
|
||||
{...testIdBuilder?.getChild('toggle-all').getTestId()}
|
||||
>
|
||||
{__('Select.checkAll')}
|
||||
</a>
|
||||
@ -620,9 +626,13 @@ export class Transfer<
|
||||
onKeyDown={this.handleSearchKeyDown}
|
||||
placeholder={searchPlaceholder}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('search-input')}
|
||||
>
|
||||
{this.state.searchResult !== null ? (
|
||||
<a onClick={this.handleSeachCancel}>
|
||||
<a
|
||||
onClick={this.handleSeachCancel}
|
||||
{...testIdBuilder?.getChild('search-cancel').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
) : (
|
||||
@ -702,7 +712,8 @@ export class Transfer<
|
||||
virtualListHeight,
|
||||
checkAll,
|
||||
checkAllLabel,
|
||||
onlyChildren
|
||||
onlyChildren,
|
||||
testIdBuilder
|
||||
} = props;
|
||||
const {isTreeDeferLoad, searchResult, inputValue} = this.state;
|
||||
const options = searchResult ?? [];
|
||||
@ -730,6 +741,7 @@ export class Transfer<
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
virtualListHeight={virtualListHeight}
|
||||
testIdBuilder={testIdBuilder?.getChild('search-result')}
|
||||
/>
|
||||
) : mode === 'tree' ? (
|
||||
<Tree
|
||||
@ -793,6 +805,7 @@ export class Transfer<
|
||||
virtualListHeight={virtualListHeight}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('search-result')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -826,7 +839,8 @@ export class Transfer<
|
||||
checkAllLabel,
|
||||
onlyChildren,
|
||||
autoCheckChildren = true,
|
||||
initiallyOpen = true
|
||||
initiallyOpen = true,
|
||||
testIdBuilder
|
||||
} = props;
|
||||
|
||||
return selectMode === 'table' ? (
|
||||
@ -846,6 +860,7 @@ export class Transfer<
|
||||
virtualListHeight={virtualListHeight}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('selection')}
|
||||
/>
|
||||
) : selectMode === 'tree' ? (
|
||||
<Tree
|
||||
@ -871,6 +886,7 @@ export class Transfer<
|
||||
checkAll={checkAll}
|
||||
initiallyOpen={initiallyOpen}
|
||||
autoCheckChildren={autoCheckChildren}
|
||||
testIdBuilder={testIdBuilder?.getChild('selection')}
|
||||
/>
|
||||
) : selectMode === 'chained' ? (
|
||||
<ChainedSelection
|
||||
@ -891,6 +907,7 @@ export class Transfer<
|
||||
loadingConfig={loadingConfig}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('selection')}
|
||||
/>
|
||||
) : selectMode === 'associated' ? (
|
||||
<AssociatedSelection
|
||||
@ -917,6 +934,7 @@ export class Transfer<
|
||||
loadingConfig={loadingConfig}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('selection')}
|
||||
/>
|
||||
) : (
|
||||
<GroupedSelection
|
||||
@ -936,6 +954,7 @@ export class Transfer<
|
||||
virtualListHeight={virtualListHeight}
|
||||
checkAllLabel={checkAllLabel}
|
||||
checkAll={checkAll}
|
||||
testIdBuilder={testIdBuilder?.getChild('selection')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -962,7 +981,8 @@ export class Transfer<
|
||||
loadingConfig,
|
||||
showInvalidMatch,
|
||||
pagination,
|
||||
accumulatedOptions
|
||||
accumulatedOptions,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {resultSelectMode, isTreeDeferLoad} = this.state;
|
||||
const searchable = !isTreeDeferLoad && resultSearchable;
|
||||
@ -987,6 +1007,7 @@ export class Transfer<
|
||||
onSearch={onResultSearch}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
testIdBuilder={testIdBuilder?.getChild('result')}
|
||||
/>
|
||||
);
|
||||
case 'tree':
|
||||
@ -1008,6 +1029,7 @@ export class Transfer<
|
||||
labelField={labelField}
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
testIdBuilder={testIdBuilder?.getChild('result')}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
@ -1028,6 +1050,7 @@ export class Transfer<
|
||||
virtualThreshold={virtualThreshold}
|
||||
itemHeight={itemHeight}
|
||||
showInvalidMatch={showInvalidMatch}
|
||||
testIdBuilder={testIdBuilder?.getChild('result')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1050,7 +1073,8 @@ export class Transfer<
|
||||
translate: __,
|
||||
valueField = 'value',
|
||||
mobileUI,
|
||||
pagination
|
||||
pagination,
|
||||
testIdBuilder
|
||||
} = this.props as any;
|
||||
const {searchResult} = this.state;
|
||||
|
||||
@ -1113,6 +1137,7 @@ export class Transfer<
|
||||
'Transfer-clearAll',
|
||||
disabled || !this.valueArray.length ? 'is-disabled' : ''
|
||||
)}
|
||||
{...testIdBuilder?.getChild('clear-all').getTestId()}
|
||||
>
|
||||
{__('clear')}
|
||||
</a>
|
||||
|
@ -27,7 +27,8 @@ import {
|
||||
getTreeParent,
|
||||
getTreeAncestors,
|
||||
flattenTree,
|
||||
flattenTreeWithLeafNodes
|
||||
flattenTreeWithLeafNodes,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {Option, Options, value2array} from './Select';
|
||||
import {themeable, ThemeProps, highlight} from 'amis-core';
|
||||
@ -152,6 +153,8 @@ interface TreeSelectorProps extends ThemeProps, LocaleProps, SpinnerExtraProps {
|
||||
// 全选按钮文案
|
||||
checkAllLabel?: string;
|
||||
enableDefaultIcon?: boolean;
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
interface TreeSelectorState {
|
||||
@ -766,7 +769,7 @@ export class TreeSelector extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
renderInput(prfix: JSX.Element | null = null) {
|
||||
renderInput(prfix: JSX.Element | null = null, testIdBuilder?: TestIdBuilder) {
|
||||
const {classnames: cx, mobileUI, translate: __} = this.props;
|
||||
const {inputValue} = this.state;
|
||||
|
||||
@ -786,11 +789,20 @@ export class TreeSelector extends React.Component<
|
||||
onChange={this.handleInputChange}
|
||||
value={inputValue}
|
||||
placeholder={__('placeholder.enter')}
|
||||
{...testIdBuilder?.getChild('input').getTestId()}
|
||||
/>
|
||||
<a data-tooltip={__('cancel')} onClick={this.handleCancel}>
|
||||
<a
|
||||
data-tooltip={__('cancel')}
|
||||
onClick={this.handleCancel}
|
||||
{...testIdBuilder?.getChild('cancel').getTestId()}
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
<a data-tooltip={__('confirm')} onClick={this.handleConfirm}>
|
||||
<a
|
||||
data-tooltip={__('confirm')}
|
||||
onClick={this.handleConfirm}
|
||||
{...testIdBuilder?.getChild('confirm').getTestId()}
|
||||
>
|
||||
<Icon icon="check" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
@ -1127,7 +1139,8 @@ export class TreeSelector extends React.Component<
|
||||
loadingConfig,
|
||||
enableDefaultIcon,
|
||||
valueField,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const item = this.state.flattenedOptions[index];
|
||||
@ -1142,6 +1155,9 @@ export class TreeSelector extends React.Component<
|
||||
const disabled = this.isItemDisabled(item, checked);
|
||||
const partial = this.isItemChildrenPartialChecked(item, checked);
|
||||
const checkedInValue = !!~this.state.value.indexOf(item);
|
||||
const itemTestBuilder = testIdBuilder?.getChild(
|
||||
`item-${item[valueField] || item[labelField] || index}`
|
||||
);
|
||||
|
||||
const checkbox: JSX.Element | null = multiple ? (
|
||||
<Checkbox
|
||||
@ -1150,6 +1166,7 @@ export class TreeSelector extends React.Component<
|
||||
checked={checked || partial}
|
||||
partial={partial}
|
||||
onChange={this.handleCheck.bind(this, item, !checked)}
|
||||
testIdBuilder={itemTestBuilder?.getChild('chekbx')}
|
||||
/>
|
||||
) : showRadio ? (
|
||||
<Checkbox
|
||||
@ -1157,6 +1174,7 @@ export class TreeSelector extends React.Component<
|
||||
disabled={disabled}
|
||||
checked={checked}
|
||||
onChange={this.handleSelect.bind(this, item)}
|
||||
testIdBuilder={itemTestBuilder?.getChild('chekbx')}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@ -1174,12 +1192,14 @@ export class TreeSelector extends React.Component<
|
||||
let body = null;
|
||||
|
||||
if (isEditing && editingItem === item) {
|
||||
body = this.renderInput(checkbox);
|
||||
body = this.renderInput(checkbox, itemTestBuilder?.getChild('edit'));
|
||||
} else if (item.isAdding) {
|
||||
body = this.renderInput(
|
||||
<span className={cx('Tree-itemArrowPlaceholder')} />
|
||||
<span className={cx('Tree-itemArrowPlaceholder')} />,
|
||||
itemTestBuilder?.getChild('add')
|
||||
);
|
||||
} else {
|
||||
const isFolded = !this.isUnfolded(item);
|
||||
body = (
|
||||
<div
|
||||
className={cx('Tree-itemLabel', {
|
||||
@ -1197,7 +1217,10 @@ export class TreeSelector extends React.Component<
|
||||
onDragEnd={this.onDragEnd(item)}
|
||||
>
|
||||
{draggable && (
|
||||
<a className={cx('Tree-itemDrager drag-bar')}>
|
||||
<a
|
||||
className={cx('Tree-itemDrager drag-bar')}
|
||||
{...itemTestBuilder?.getChild('drag-bar').getTestId()}
|
||||
>
|
||||
<Icon icon="drag-bar" className="icon" />
|
||||
</a>
|
||||
)}
|
||||
@ -1214,8 +1237,11 @@ export class TreeSelector extends React.Component<
|
||||
<div
|
||||
onClick={() => this.toggleUnfolded(item)}
|
||||
className={cx('Tree-itemArrow', {
|
||||
'is-folded': !this.isUnfolded(item)
|
||||
'is-folded': isFolded
|
||||
})}
|
||||
{...itemTestBuilder
|
||||
?.getChild(isFolded ? 'open' : 'fold')
|
||||
.getTestId()}
|
||||
>
|
||||
<Icon icon="down-arrow-bold" className="icon" />
|
||||
</div>
|
||||
@ -1225,7 +1251,10 @@ export class TreeSelector extends React.Component<
|
||||
|
||||
{checkbox}
|
||||
|
||||
<div className={cx('Tree-itemLabel-item', {'is-mobile': mobileUI})}>
|
||||
<div
|
||||
className={cx('Tree-itemLabel-item', {'is-mobile': mobileUI})}
|
||||
{...itemTestBuilder?.getChild('content').getTestId()}
|
||||
>
|
||||
{showIcon ? (
|
||||
<i
|
||||
className={cx(
|
||||
@ -1263,6 +1292,7 @@ export class TreeSelector extends React.Component<
|
||||
: this.handleSelect(item))
|
||||
}
|
||||
title={item[labelField]}
|
||||
{...itemTestBuilder?.getChild('text').getTestId()}
|
||||
>
|
||||
{itemRender
|
||||
? itemRender(item, {
|
||||
@ -1291,7 +1321,10 @@ export class TreeSelector extends React.Component<
|
||||
trigger={'hover'}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<a onClick={this.handleAdd.bind(this, item)}>
|
||||
<a
|
||||
onClick={this.handleAdd.bind(this, item)}
|
||||
{...itemTestBuilder?.getChild('add').getTestId()}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
</a>
|
||||
</TooltipWrapper>
|
||||
@ -1304,7 +1337,10 @@ export class TreeSelector extends React.Component<
|
||||
trigger={'hover'}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<a onClick={this.handleRemove.bind(this, item)}>
|
||||
<a
|
||||
onClick={this.handleRemove.bind(this, item)}
|
||||
{...itemTestBuilder?.getChild('remove').getTestId()}
|
||||
>
|
||||
<Icon icon="minus" className="icon" />
|
||||
</a>
|
||||
</TooltipWrapper>
|
||||
@ -1317,7 +1353,10 @@ export class TreeSelector extends React.Component<
|
||||
trigger={'hover'}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<a onClick={this.handleEdit.bind(this, item)}>
|
||||
<a
|
||||
onClick={this.handleEdit.bind(this, item)}
|
||||
{...itemTestBuilder?.getChild('edit').getTestId()}
|
||||
>
|
||||
<Icon icon="new-edit" className="icon" />
|
||||
</a>
|
||||
</TooltipWrapper>
|
||||
@ -1340,6 +1379,7 @@ export class TreeSelector extends React.Component<
|
||||
...style,
|
||||
paddingLeft: `calc(${level} * var(--Tree-indent))`
|
||||
}}
|
||||
{...itemTestBuilder?.getTestId()}
|
||||
>
|
||||
{body}
|
||||
</li>
|
||||
@ -1473,7 +1513,8 @@ export class TreeSelector extends React.Component<
|
||||
rootCreateTip,
|
||||
disabled,
|
||||
draggable,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {
|
||||
value,
|
||||
@ -1493,6 +1534,7 @@ export class TreeSelector extends React.Component<
|
||||
'is-disabled': isAdding || isEditing
|
||||
})}
|
||||
onClick={this.handleAdd.bind(this, null)}
|
||||
{...testIdBuilder?.getChild('add').getTestId()}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>{__(rootCreateTip)}</span>
|
||||
@ -1508,6 +1550,7 @@ export class TreeSelector extends React.Component<
|
||||
'is-draggable': draggable
|
||||
})}
|
||||
ref={this.root}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{(flattenedOptions && flattenedOptions.length) ||
|
||||
addBtn ||
|
||||
@ -1532,6 +1575,7 @@ export class TreeSelector extends React.Component<
|
||||
<span
|
||||
className={cx('Tree-itemText')}
|
||||
onClick={this.clearSelect}
|
||||
{...testIdBuilder?.getChild(`root-item`).getTestId()}
|
||||
>
|
||||
{showIcon ? (
|
||||
<i className={cx('Tree-itemIcon Tree-rootIcon')}>
|
||||
@ -1551,6 +1595,7 @@ export class TreeSelector extends React.Component<
|
||||
onClick={this.handleAdd.bind(this, null)}
|
||||
data-tooltip={rootCreateTip}
|
||||
data-position="left"
|
||||
{...testIdBuilder?.getChild(`root-add`).getTestId()}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
</a>
|
||||
|
@ -15,7 +15,7 @@ import {PickerOption} from '../PickerColumn';
|
||||
import 'moment/locale/zh-cn';
|
||||
import 'moment/locale/de';
|
||||
|
||||
import type {RendererEnv} from 'amis-core';
|
||||
import type {RendererEnv, TestIdBuilder} from 'amis-core';
|
||||
import type {unitOfTime} from 'moment';
|
||||
|
||||
/** 视图模式 */
|
||||
@ -132,6 +132,7 @@ interface BaseDatePickerProps {
|
||||
timeConstraints?: any;
|
||||
timeRangeHeader?: string;
|
||||
status?: ChangeEventViewStatus;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
interface BaseDatePickerState {
|
||||
@ -456,7 +457,8 @@ class BaseDatePicker extends React.Component<
|
||||
'mobileUI',
|
||||
'showToolbar',
|
||||
'embed',
|
||||
'env'
|
||||
'env',
|
||||
'testIdBuilder'
|
||||
].forEach(key => (props[key] = (this.props as any)[key]));
|
||||
|
||||
return props;
|
||||
@ -721,8 +723,14 @@ class BaseDatePicker extends React.Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
const {viewMode, timeFormat, dateFormat, timeRangeHeader, mobileUI} =
|
||||
this.props;
|
||||
const {
|
||||
viewMode,
|
||||
timeFormat,
|
||||
dateFormat,
|
||||
timeRangeHeader,
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const Component = CustomCalendarContainer as any;
|
||||
const viewProps = this.getComponentProps();
|
||||
|
||||
@ -757,6 +765,7 @@ class BaseDatePicker extends React.Component<
|
||||
? 'rdtTime'
|
||||
: ''
|
||||
)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<div
|
||||
key="dt"
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
ClassNamesFn,
|
||||
convertArrayValueToMoment
|
||||
} from 'amis-core';
|
||||
import type {RendererEnv} from 'amis-core';
|
||||
import type {RendererEnv, TestIdBuilder} from 'amis-core';
|
||||
import Picker from '../Picker';
|
||||
import {PickerOption} from '../PickerColumn';
|
||||
import {DateType} from './Calendar';
|
||||
@ -89,6 +89,7 @@ interface CustomDaysViewProps extends LocaleProps {
|
||||
getColumns: (types: DateType[], dateBoundary: void) => any;
|
||||
getDateBoundary: (currentDate: moment.Moment) => any;
|
||||
timeConstraints?: any;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
@ -422,7 +423,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
|
||||
renderDay = (props: any, currentDate: moment.Moment) => {
|
||||
const {todayActiveStyle} = props; /** 只有today才会传入这个属性 */
|
||||
const {classnames: cx, translate: __, env} = this.props;
|
||||
const {classnames: cx, translate: __, testIdBuilder} = this.props;
|
||||
const injectedProps = omit(props, ['todayActiveStyle']);
|
||||
/** 某些情况下需要用inline style覆盖动态class,需要hack important的样式 */
|
||||
const todayDomRef = (node: HTMLSpanElement | null) => {
|
||||
@ -611,7 +612,11 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
|
||||
return (
|
||||
<td {...injectedProps}>
|
||||
<span style={todayActiveStyle} ref={todayDomRef}>
|
||||
<span
|
||||
style={todayActiveStyle}
|
||||
ref={todayDomRef}
|
||||
{...testIdBuilder?.getChild(props.key)?.getTestId()}
|
||||
>
|
||||
{currentDate.date()}
|
||||
</span>
|
||||
</td>
|
||||
@ -637,7 +642,8 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
selectedDate,
|
||||
viewDate,
|
||||
isEndDate,
|
||||
classnames: cx
|
||||
classnames: cx,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const date = selectedDate || (isEndDate ? viewDate.endOf('day') : viewDate);
|
||||
@ -685,6 +691,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
)
|
||||
)
|
||||
});
|
||||
const itemTIB = testIdBuilder?.getChild(type);
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
@ -718,6 +725,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
? option.value === date.format(formatMap[type])
|
||||
: option.value === options?.[0]?.value
|
||||
})}
|
||||
{...itemTIB?.getChild(option.value)?.getTestId()}
|
||||
onClick={() => {
|
||||
this.setTime(type, parseInt(option.value, 10));
|
||||
this.scrollToTop(
|
||||
@ -834,7 +842,8 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
mobileUI,
|
||||
embed,
|
||||
timeFormat,
|
||||
classnames: cx
|
||||
classnames: cx,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const locale = date.localeData();
|
||||
const __ = this.props.translate;
|
||||
@ -851,6 +860,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
<a
|
||||
className="rdtPrev"
|
||||
onClick={this.props.subtractTime(1, 'years')}
|
||||
{...testIdBuilder?.getChild('prev-year').getTestId()}
|
||||
>
|
||||
<Icon
|
||||
icon="right-double-arrow"
|
||||
@ -860,6 +870,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
<a
|
||||
className="rdtPrev"
|
||||
onClick={this.props.subtractTime(1, 'months')}
|
||||
{...testIdBuilder?.getChild('prev-month').getTestId()}
|
||||
>
|
||||
<Icon
|
||||
icon="right-arrow"
|
||||
@ -871,12 +882,14 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
<a
|
||||
className="rdtSwitch"
|
||||
onClick={this.props.showView('years')}
|
||||
{...testIdBuilder?.getChild('switch-years').getTestId()}
|
||||
>
|
||||
{date.format(__('dateformat.year'))}
|
||||
</a>
|
||||
<a
|
||||
className="rdtSwitch"
|
||||
onClick={this.props.showView('months')}
|
||||
{...testIdBuilder?.getChild('switch-months').getTestId()}
|
||||
>
|
||||
{date.format(__('MMM'))}
|
||||
</a>
|
||||
@ -885,10 +898,15 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
<a
|
||||
className="rdtNext"
|
||||
onClick={this.props.addTime(1, 'months')}
|
||||
{...testIdBuilder?.getChild('next-month').getTestId()}
|
||||
>
|
||||
<Icon icon="right-arrow" className="icon date-icon-arrow" />
|
||||
</a>
|
||||
<a className="rdtNext" onClick={this.props.addTime(1, 'years')}>
|
||||
<a
|
||||
className="rdtNext"
|
||||
onClick={this.props.addTime(1, 'years')}
|
||||
{...testIdBuilder?.getChild('next-year').getTestId()}
|
||||
>
|
||||
<Icon
|
||||
icon="right-double-arrow"
|
||||
className="icon date-icon-arrow"
|
||||
|
@ -4,6 +4,7 @@ import {LocaleProps, localeable, getRange} from 'amis-core';
|
||||
import Picker from '../Picker';
|
||||
import {PickerOption} from '../PickerColumn';
|
||||
import {DateType} from './Calendar';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface OtherProps {
|
||||
inputFormat?: string;
|
||||
@ -47,6 +48,7 @@ export interface CustomMonthsViewProps extends LocaleProps {
|
||||
timeCell: (value: number, type: DateType) => string;
|
||||
getDateBoundary: (currentDate: moment.Moment) => any;
|
||||
mobileUI: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
@ -71,6 +73,7 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
}
|
||||
|
||||
renderMonths() {
|
||||
const {testIdBuilder} = this.props;
|
||||
let date = this.props.selectedDate,
|
||||
month = this.props.viewDate.month(),
|
||||
year = this.props.viewDate.year(),
|
||||
@ -159,7 +162,7 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
year: number,
|
||||
date: moment.Moment
|
||||
) => {
|
||||
const {translate: __} = this.props;
|
||||
const {translate: __, testIdBuilder} = this.props;
|
||||
const {viewDate: localMoment, ...rest} = props;
|
||||
const monthStr = localMoment.month(month).format(__('MMM'));
|
||||
const strLength = 3;
|
||||
@ -169,7 +172,9 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
|
||||
return (
|
||||
<td {...rest}>
|
||||
<span>{monthStrFixedLength}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key).getTestId()}>
|
||||
{monthStrFixedLength}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
};
|
||||
@ -248,6 +253,7 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
|
||||
render() {
|
||||
const __ = this.props.translate;
|
||||
const {testIdBuilder} = this.props;
|
||||
const showYearHead =
|
||||
!/^mm$/i.test(this.props.inputFormat || '') && !this.props.hideHeader;
|
||||
const canClick = /yy/i.test(this.props.inputFormat || '');
|
||||
@ -264,6 +270,7 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
<th
|
||||
className="rdtPrev"
|
||||
onClick={this.props.subtractTime(1, 'years')}
|
||||
{...testIdBuilder?.getChild('prev-year').getTestId()}
|
||||
>
|
||||
«
|
||||
</th>
|
||||
@ -271,6 +278,7 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
<th
|
||||
className="rdtSwitch"
|
||||
onClick={this.props.showView('years')}
|
||||
{...testIdBuilder?.getChild('switch-year').getTestId()}
|
||||
>
|
||||
{this.props.viewDate.format(__('dateformat.year'))}
|
||||
</th>
|
||||
@ -283,6 +291,7 @@ export class CustomMonthsView extends React.Component<CustomMonthsViewProps> {
|
||||
<th
|
||||
className="rdtNext"
|
||||
onClick={this.props.addTime(1, 'years')}
|
||||
{...testIdBuilder?.getChild('next-year').getTestId()}
|
||||
>
|
||||
»
|
||||
</th>
|
||||
|
@ -5,6 +5,7 @@ import Picker from '../Picker';
|
||||
import {PickerColumnItem} from '../PickerColumn';
|
||||
import {getRange} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface QuarterViewProps extends LocaleProps, ThemeProps {
|
||||
viewDate: moment.Moment;
|
||||
@ -29,6 +30,7 @@ export interface QuarterViewProps extends LocaleProps, ThemeProps {
|
||||
hideHeader?: boolean;
|
||||
onConfirm?: (value: number[], types?: string[]) => void;
|
||||
onClose?: () => void;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class QuarterView extends React.Component<QuarterViewProps> {
|
||||
@ -38,7 +40,7 @@ export class QuarterView extends React.Component<QuarterViewProps> {
|
||||
};
|
||||
|
||||
renderYear() {
|
||||
const __ = this.props.translate;
|
||||
const {translate: __, testIdBuilder} = this.props;
|
||||
const showYearHead = !/^mm$/i.test(this.props.inputFormat || '');
|
||||
|
||||
if (!showYearHead) {
|
||||
@ -54,11 +56,16 @@ export class QuarterView extends React.Component<QuarterViewProps> {
|
||||
<th
|
||||
className="rdtPrev"
|
||||
onClick={this.props.subtractTime(1, 'years')}
|
||||
{...testIdBuilder?.getChild('prev-year').getTestId()}
|
||||
>
|
||||
«
|
||||
</th>
|
||||
{canClick ? (
|
||||
<th className="rdtSwitch" onClick={this.props.showView('years')}>
|
||||
<th
|
||||
className="rdtSwitch"
|
||||
onClick={this.props.showView('years')}
|
||||
{...testIdBuilder?.getChild('switch-year').getTestId()}
|
||||
>
|
||||
{this.props.viewDate.format(__('dateformat.year'))}
|
||||
</th>
|
||||
) : (
|
||||
@ -67,7 +74,11 @@ export class QuarterView extends React.Component<QuarterViewProps> {
|
||||
</th>
|
||||
)}
|
||||
|
||||
<th className="rdtNext" onClick={this.props.addTime(1, 'years')}>
|
||||
<th
|
||||
className="rdtNext"
|
||||
onClick={this.props.addTime(1, 'years')}
|
||||
{...testIdBuilder?.getChild('next-year').getTestId()}
|
||||
>
|
||||
»
|
||||
</th>
|
||||
</tr>
|
||||
@ -135,9 +146,12 @@ export class QuarterView extends React.Component<QuarterViewProps> {
|
||||
year: number,
|
||||
date: moment.Moment
|
||||
) => {
|
||||
const {testIdBuilder} = this.props;
|
||||
return (
|
||||
<td {...props}>
|
||||
<span>Q{quartar}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key).getTestId()}>
|
||||
Q{quartar}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ import {PickerColumnItem} from '../PickerColumn';
|
||||
import Downshift from 'downshift';
|
||||
|
||||
import type {Moment} from 'moment';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
interface CustomTimeViewProps extends LocaleProps {
|
||||
viewDate: moment.Moment;
|
||||
@ -52,6 +53,7 @@ interface CustomTimeViewProps extends LocaleProps {
|
||||
onChange: (value: moment.Moment) => void;
|
||||
timeConstraints?: any;
|
||||
timeRangeHeader?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
interface CustomTimeViewState {
|
||||
@ -689,7 +691,8 @@ export class CustomTimeView extends React.Component<
|
||||
isEndDate,
|
||||
classnames: cx,
|
||||
timeRangeHeader,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const __ = this.props.translate;
|
||||
@ -745,6 +748,7 @@ export class CustomTimeView extends React.Component<
|
||||
)
|
||||
)
|
||||
});
|
||||
const itemTIB = testIdBuilder?.getChild(type);
|
||||
return (
|
||||
<div className={cx('CalendarInputWrapper')}>
|
||||
<div
|
||||
@ -767,6 +771,7 @@ export class CustomTimeView extends React.Component<
|
||||
: option.value === options?.[0]?.value &&
|
||||
!mobileUI
|
||||
})}
|
||||
{...itemTIB?.getChild(option.value).getTestId()}
|
||||
onClick={() => {
|
||||
this.setTime(type, parseInt(option.value, 10));
|
||||
this.scrollToTop(
|
||||
@ -793,7 +798,11 @@ export class CustomTimeView extends React.Component<
|
||||
inputs.length && inputs.pop();
|
||||
|
||||
const quickLists = [
|
||||
<a key="select-now" onClick={this.selectNowTime}>
|
||||
<a
|
||||
key="select-now"
|
||||
onClick={this.selectNowTime}
|
||||
{...testIdBuilder?.getChild('select-now').getTestId()}
|
||||
>
|
||||
{__('TimeNow')}
|
||||
</a>
|
||||
];
|
||||
@ -809,6 +818,7 @@ export class CustomTimeView extends React.Component<
|
||||
<a
|
||||
className={cx('Button', 'Button--primary', 'Button--size-sm')}
|
||||
onClick={this.confirm}
|
||||
{...testIdBuilder?.getChild('confirm').getTestId()}
|
||||
>
|
||||
{__('confirm')}
|
||||
</a>
|
||||
|
@ -2,6 +2,7 @@ import moment from 'moment';
|
||||
import React from 'react';
|
||||
import {LocaleProps, localeable, utils, getRange} from 'amis-core';
|
||||
import Picker from '../Picker';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
interface CustomYearsViewProps extends LocaleProps {
|
||||
viewDate: moment.Moment;
|
||||
@ -31,6 +32,7 @@ interface CustomYearsViewProps extends LocaleProps {
|
||||
currentDate: moment.Moment,
|
||||
selected?: moment.Moment
|
||||
) => boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class CustomYearsView extends React.Component<CustomYearsViewProps> {
|
||||
@ -124,9 +126,10 @@ export class CustomYearsView extends React.Component<CustomYearsViewProps> {
|
||||
}
|
||||
|
||||
renderYear = (props: any, year: number, date?: moment.Moment) => {
|
||||
const {testIdBuilder} = this.props;
|
||||
return (
|
||||
<td {...props}>
|
||||
<span>{year}</span>
|
||||
<span {...testIdBuilder?.getChild(props.key).getTestId()}>{year}</span>
|
||||
</td>
|
||||
);
|
||||
};
|
||||
@ -179,7 +182,7 @@ export class CustomYearsView extends React.Component<CustomYearsViewProps> {
|
||||
render() {
|
||||
let year = this.props.viewDate.year();
|
||||
year = year - (year % 10);
|
||||
const __ = this.props.translate;
|
||||
const {testIdBuilder, translate: __} = this.props;
|
||||
if (this.props.mobileUI) {
|
||||
return <div className="rdtYears">{this.renderYearPicker()}</div>;
|
||||
}
|
||||
@ -191,13 +194,18 @@ export class CustomYearsView extends React.Component<CustomYearsViewProps> {
|
||||
<th
|
||||
className="rdtPrev"
|
||||
onClick={this.props.subtractTime(10, 'years')}
|
||||
{...testIdBuilder?.getChild('prev-year').getTestId()}
|
||||
>
|
||||
«
|
||||
</th>
|
||||
<th className="rdtSwitch">
|
||||
{__('year-to-year', {from: year, to: year + 9})}
|
||||
</th>
|
||||
<th className="rdtNext" onClick={this.props.addTime(10, 'years')}>
|
||||
<th
|
||||
className="rdtNext"
|
||||
onClick={this.props.addTime(10, 'years')}
|
||||
{...testIdBuilder?.getChild('next-year').getTestId()}
|
||||
>
|
||||
»
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -7,7 +7,13 @@
|
||||
import React from 'react';
|
||||
import pick from 'lodash/pick';
|
||||
import {Item as RcItem, MenuItemProps as RcMenuItemProps} from 'rc-menu';
|
||||
import {ClassNamesFn, themeable, createObject, buildTestId} from 'amis-core';
|
||||
import {
|
||||
ClassNamesFn,
|
||||
themeable,
|
||||
createObject,
|
||||
TestIdBuilder,
|
||||
filter
|
||||
} from 'amis-core';
|
||||
|
||||
import {Badge} from '../Badge';
|
||||
import {getIcon} from '../icons';
|
||||
@ -30,6 +36,7 @@ export interface MenuItemProps
|
||||
tooltipTrigger?: Trigger | Array<Trigger>;
|
||||
renderLink: Function;
|
||||
testid?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
extra?: React.ReactNode;
|
||||
}
|
||||
|
||||
@ -91,7 +98,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||
renderLink,
|
||||
extra,
|
||||
disabled,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
id,
|
||||
data: defaultData
|
||||
} = this.props;
|
||||
@ -168,7 +175,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||
data-id={link?.__id || id}
|
||||
data-depth={depth}
|
||||
onDragStart={onDragStart?.(link)}
|
||||
{...buildTestId(testid, link)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{isCollapsedNode ? (
|
||||
<>{iconNode || labelNode}</>
|
||||
|
@ -12,8 +12,8 @@ import {
|
||||
themeable,
|
||||
autobind,
|
||||
createObject,
|
||||
filter,
|
||||
buildTestId
|
||||
TestIdBuilder,
|
||||
filter
|
||||
} from 'amis-core';
|
||||
|
||||
import {getIcon, Icon} from '../icons';
|
||||
@ -42,6 +42,7 @@ export interface SubMenuProps
|
||||
onTitleClick?: (e: MenuItemTitleInfo) => void;
|
||||
renderLink: Function;
|
||||
[propName: string]: any;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class SubMenu extends React.Component<SubMenuProps> {
|
||||
@ -111,7 +112,7 @@ export class SubMenu extends React.Component<SubMenuProps> {
|
||||
disabled,
|
||||
data: defaultData,
|
||||
extra,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
renderLink
|
||||
} = this.props;
|
||||
const isCollapsedNode = collapsed && depth === 1;
|
||||
@ -166,7 +167,11 @@ export class SubMenu extends React.Component<SubMenuProps> {
|
||||
) : null;
|
||||
const dragNode =
|
||||
!disabled && stacked && mode === 'inline' && !collapsed && draggable ? (
|
||||
<span className={cx('Nav-Menu-item-dragBar')} draggable>
|
||||
<span
|
||||
className={cx('Nav-Menu-item-dragBar')}
|
||||
draggable
|
||||
{...testIdBuilder?.getChild('drag-bar').getTestId()}
|
||||
>
|
||||
<DragIcon />
|
||||
</span>
|
||||
) : null;
|
||||
@ -181,7 +186,11 @@ export class SubMenu extends React.Component<SubMenuProps> {
|
||||
{labelNode}
|
||||
{labelExtra}
|
||||
{!stacked && depth === 1 ? (
|
||||
<span key="expand-toggle" className={cx('Nav-Menu-submenu-arrow')}>
|
||||
<span
|
||||
key="expand-toggle"
|
||||
className={cx('Nav-Menu-submenu-arrow')}
|
||||
{...testIdBuilder?.getChild('expand-toggle').getTestId()}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
) : null}
|
||||
@ -205,7 +214,7 @@ export class SubMenu extends React.Component<SubMenuProps> {
|
||||
data-id={link?.__id || id}
|
||||
data-depth={depth}
|
||||
onDragStart={onDragStart?.(link)}
|
||||
{...buildTestId(testid, link)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{renderContent()}
|
||||
</a>
|
||||
|
@ -18,7 +18,8 @@ import {
|
||||
autobind,
|
||||
filterTree,
|
||||
findTree,
|
||||
getTreeAncestors
|
||||
getTreeAncestors,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {ClassNamesFn, themeable} from 'amis-core';
|
||||
|
||||
@ -63,7 +64,7 @@ export interface MenuProps extends Omit<RcMenuProps, 'mode'> {
|
||||
*/
|
||||
navigations: Array<NavigationItem>;
|
||||
|
||||
testid?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
|
||||
/**
|
||||
* 导航排列方式 stacked为true垂直 默认为false
|
||||
@ -542,8 +543,9 @@ export class Menu extends React.Component<MenuProps, MenuState> {
|
||||
disabled?: boolean;
|
||||
[propName: string]: any;
|
||||
}) {
|
||||
const {classnames: cx, expandIcon} = this.props;
|
||||
|
||||
const navigations = this.state.navigations;
|
||||
const {classnames: cx, expandIcon, testIdBuilder} = this.props;
|
||||
const link = findTree(navigations, item => item.id === ctx.eventKey);
|
||||
return (
|
||||
<span
|
||||
key="expand-toggle"
|
||||
@ -552,6 +554,10 @@ export class Menu extends React.Component<MenuProps, MenuState> {
|
||||
this.handleToggleExpand(ctx);
|
||||
e.preventDefault();
|
||||
}}
|
||||
{...testIdBuilder
|
||||
?.getChild(link?.link?.testid || ctx.eventKey)
|
||||
.getChild('expand-toggle')
|
||||
.getTestId()}
|
||||
>
|
||||
{!React.isValidElement(expandIcon) ? (
|
||||
<Icon
|
||||
@ -580,7 +586,7 @@ export class Menu extends React.Component<MenuProps, MenuState> {
|
||||
overflowedIndicator,
|
||||
overflowMaxCount,
|
||||
popupClassName,
|
||||
testid
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return list.map((item: NavigationItem, index: number) => {
|
||||
@ -616,7 +622,7 @@ export class Menu extends React.Component<MenuProps, MenuState> {
|
||||
badge={badge}
|
||||
renderLink={renderLink}
|
||||
depth={level || 1}
|
||||
testid={testid}
|
||||
testIdBuilder={testIdBuilder?.getChild(link.testid || index)}
|
||||
popupClassName={popupClassName}
|
||||
>
|
||||
{this.renderMenuContent(item.children || [], item.depth + 1)}
|
||||
@ -638,7 +644,7 @@ export class Menu extends React.Component<MenuProps, MenuState> {
|
||||
renderLink={renderLink}
|
||||
badge={badge}
|
||||
data={data}
|
||||
testid={testid}
|
||||
testIdBuilder={testIdBuilder?.getChild(link.testid || index)}
|
||||
depth={level || 1}
|
||||
order={index}
|
||||
overflowedIndicator={overflowedIndicator}
|
||||
|
@ -130,7 +130,7 @@ import {
|
||||
SchemaClassName,
|
||||
SchemaExpression
|
||||
} from 'amis-core';
|
||||
import type {FormSchemaBase} from 'amis-core';
|
||||
import type {FormSchemaBase, TestIdBuilder} from 'amis-core';
|
||||
import {MultilineTextSchema} from './renderers/MultilineText';
|
||||
import {DateRangeSchema} from './renderers/DateRange';
|
||||
import {PasswordSchema} from './renderers/Password';
|
||||
@ -726,6 +726,8 @@ export type SchemaFunction = string | Function;
|
||||
|
||||
export interface BaseSchema extends BaseSchemaWithoutType {
|
||||
type: SchemaType;
|
||||
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
|
@ -10,8 +10,7 @@ import {
|
||||
RendererProps,
|
||||
ScopedContext,
|
||||
uuid,
|
||||
setThemeClassName,
|
||||
getTestId
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {filter} from 'amis-core';
|
||||
import {BadgeObject, Button, SpinnerExtraProps} from 'amis-ui';
|
||||
@ -24,8 +23,6 @@ export interface ButtonSchema extends BaseSchema {
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
testid?: string;
|
||||
|
||||
/**
|
||||
* 是否为块状展示,默认为内联。
|
||||
*/
|
||||
@ -749,7 +746,7 @@ export class Action extends React.Component<ActionProps, ActionState> {
|
||||
wrapperCustomStyle,
|
||||
css,
|
||||
id,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
env
|
||||
} = this.props;
|
||||
|
||||
@ -849,7 +846,7 @@ export class Action extends React.Component<ActionProps, ActionState> {
|
||||
[activeClassName || 'is-active']: isActive
|
||||
}
|
||||
)}
|
||||
testid={getTestId(testid, data)}
|
||||
testIdBuilder={testIdBuilder}
|
||||
style={style}
|
||||
size={size}
|
||||
level={
|
||||
|
@ -7,6 +7,7 @@ import {BaseSchema, SchemaIcon, SchemaUrlPath} from '../Schema';
|
||||
import {filter} from 'amis-core';
|
||||
import {resolveVariableAndFilter} from 'amis-core';
|
||||
import {Breadcrumb} from 'amis-ui';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export type BreadcrumbBaseItemSchema = {
|
||||
/**
|
||||
@ -101,6 +102,8 @@ export interface BreadcrumbSchema extends BaseSchema {
|
||||
* 浮窗提示位置
|
||||
*/
|
||||
tooltipPosition?: TooltipPositionType;
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface BreadcrumbProps
|
||||
|
@ -7,8 +7,7 @@ import {
|
||||
RendererProps,
|
||||
evalExpressionWithConditionBuilder,
|
||||
filterTarget,
|
||||
mapTree,
|
||||
buildTestId
|
||||
mapTree
|
||||
} from 'amis-core';
|
||||
import {SchemaNode, Schema, ActionObject, PlainObject} from 'amis-core';
|
||||
import {CRUDStore, ICRUDStore} from 'amis-core';
|
||||
@ -1990,7 +1989,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
render,
|
||||
classnames: cx,
|
||||
alwaysShowPagination,
|
||||
perPageAvailable
|
||||
perPageAvailable,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {page, lastPage} = store;
|
||||
|
||||
@ -2038,7 +2038,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
{render(
|
||||
'pagination',
|
||||
{
|
||||
type: 'pagination'
|
||||
type: 'pagination',
|
||||
testIdBuilder: testIdBuilder?.getChild('pagination')
|
||||
},
|
||||
{
|
||||
...extraProps,
|
||||
@ -2085,7 +2086,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
perPageAvailable,
|
||||
classnames: cx,
|
||||
classPrefix: ns,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const items = childProps.items;
|
||||
@ -2116,13 +2118,20 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
onChange={(value: any) => this.handleChangePage(1, value.value)}
|
||||
clearable={false}
|
||||
popOverContainer={this.parentContainer}
|
||||
testIdBuilder={testIdBuilder?.getChild('perPage')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderLoadMore() {
|
||||
const {store, classPrefix: ns, classnames: cx, translate: __} = this.props;
|
||||
const {
|
||||
store,
|
||||
classPrefix: ns,
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {page, lastPage} = store;
|
||||
|
||||
return (
|
||||
@ -2135,6 +2144,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
this.search({page: page + 1, loadDataMode: 'load-more'})
|
||||
}
|
||||
size="sm"
|
||||
{...testIdBuilder?.getChild('loadMore').getTestId()}
|
||||
>
|
||||
{__('CRUD.loadMore')}
|
||||
</Button>
|
||||
@ -2213,7 +2223,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {render, store, mobileUI, translate: __} = this.props;
|
||||
const {render, store, mobileUI, translate: __, testIdBuilder} = this.props;
|
||||
const type = (toolbar as Schema).type || toolbar;
|
||||
|
||||
if (type === 'bulkActions' || type === 'bulk-actions') {
|
||||
@ -2258,7 +2268,11 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
const cx = this.props.classnames;
|
||||
if (len) {
|
||||
return (
|
||||
<div className={cx('Crud-toolbar')} key={index}>
|
||||
<div
|
||||
className={cx('Crud-toolbar')}
|
||||
key={index}
|
||||
{...testIdBuilder?.getChild('toolbar').getTestId()}
|
||||
>
|
||||
{children.map(({toolbar, dom: child}, index) => {
|
||||
const type = (toolbar as Schema).type || toolbar;
|
||||
let align =
|
||||
@ -2525,7 +2539,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
onSearchableFromInit,
|
||||
headerToolbarRender,
|
||||
footerToolbarRender,
|
||||
testid,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -2536,7 +2550,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
'is-mobile': isMobile()
|
||||
})}
|
||||
style={style}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder?.getChild('wrapper').getTestId()}
|
||||
>
|
||||
{filter && (!store.filterTogggable || store.filterVisible)
|
||||
? render(
|
||||
@ -2547,7 +2561,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
submitText: __('search'),
|
||||
...filter,
|
||||
type: 'form',
|
||||
api: null
|
||||
api: null,
|
||||
testIdBuilder: testIdBuilder?.getChild('filter')
|
||||
},
|
||||
{
|
||||
key: 'filter',
|
||||
|
@ -30,8 +30,7 @@ import {
|
||||
isApiOutdated,
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
parsePrimitiveQueryString,
|
||||
buildTestId
|
||||
parsePrimitiveQueryString
|
||||
} from 'amis-core';
|
||||
import {Html, SpinnerExtraProps} from 'amis-ui';
|
||||
import {
|
||||
@ -1324,7 +1323,6 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
||||
'is-loading': store.loading
|
||||
})}
|
||||
style={style}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
<div className={cx('Crud2-filter')}>
|
||||
{this.renderFilter(filterSchema)}
|
||||
|
@ -459,6 +459,7 @@ export class CardRenderer extends React.Component<CardProps> {
|
||||
level: 'link',
|
||||
type: 'button',
|
||||
...action,
|
||||
testid: action.testid ? filter(action.testid, data) : index,
|
||||
size
|
||||
},
|
||||
{
|
||||
|
@ -8,8 +8,7 @@ import {
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
CustomStyle,
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {DndContainer as DndWrapper} from 'amis-ui';
|
||||
import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema';
|
||||
@ -233,7 +232,6 @@ export default class Container<T> extends React.Component<
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
style={buildStyle(style, data)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderBody()}
|
||||
<CustomStyle
|
||||
|
@ -7,8 +7,7 @@ import {
|
||||
resolveVariableAndFilter,
|
||||
setVariable,
|
||||
setThemeClassName,
|
||||
ValidateError,
|
||||
getTestId
|
||||
ValidateError
|
||||
} from 'amis-core';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {SchemaNode, Schema, ActionObject} from 'amis-core';
|
||||
@ -54,8 +53,6 @@ export interface DialogSchema extends BaseSchema {
|
||||
*/
|
||||
actions?: Array<ActionSchema>;
|
||||
|
||||
testid?: string;
|
||||
|
||||
/**
|
||||
* 内容区域
|
||||
*/
|
||||
@ -246,7 +243,7 @@ export default class Dialog extends React.Component<DialogProps> {
|
||||
}
|
||||
|
||||
buildActions(): Array<ActionSchema> {
|
||||
const {actions, confirm, testid, translate: __} = this.props;
|
||||
const {actions, confirm, translate: __, testIdBuilder} = this.props;
|
||||
|
||||
if (typeof actions !== 'undefined') {
|
||||
return actions;
|
||||
@ -255,7 +252,7 @@ export default class Dialog extends React.Component<DialogProps> {
|
||||
let ret: Array<ActionSchema> = [];
|
||||
ret.push({
|
||||
type: 'button',
|
||||
testid: getTestId(testid && `${testid}-cancel`),
|
||||
testIdBuilder: testIdBuilder?.getChild('cancel'),
|
||||
actionType: 'cancel',
|
||||
label: __('cancel')
|
||||
});
|
||||
@ -263,7 +260,7 @@ export default class Dialog extends React.Component<DialogProps> {
|
||||
if (confirm) {
|
||||
ret.push({
|
||||
type: 'button',
|
||||
testid: getTestId(testid && `${testid}-confirm`),
|
||||
testIdBuilder: testIdBuilder?.getChild('confirm'),
|
||||
actionType: 'confirm',
|
||||
label: __('confirm'),
|
||||
primary: true
|
||||
|
@ -6,8 +6,7 @@ import {
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
setThemeClassName,
|
||||
ValidateError,
|
||||
getTestId
|
||||
ValidateError
|
||||
} from 'amis-core';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {SchemaNode, Schema, ActionObject} from 'amis-core';
|
||||
@ -155,8 +154,6 @@ export interface DrawerSchema extends BaseSchema {
|
||||
data?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
export type DrawerSchemaBase = Omit<DrawerSchema, 'type'>;
|
||||
@ -272,7 +269,7 @@ export default class Drawer extends React.Component<DrawerProps> {
|
||||
}
|
||||
|
||||
buildActions(): Array<ActionSchema> {
|
||||
const {actions, confirm, testid, translate: __} = this.props;
|
||||
const {actions, confirm, translate: __, testIdBuilder} = this.props;
|
||||
|
||||
if (typeof actions !== 'undefined') {
|
||||
return actions;
|
||||
@ -281,7 +278,7 @@ export default class Drawer extends React.Component<DrawerProps> {
|
||||
let ret: Array<ActionSchema> = [];
|
||||
ret.push({
|
||||
type: 'button',
|
||||
testid: getTestId(testid && `${testid}-cancel`),
|
||||
testIdBuilder: testIdBuilder?.getChild('cancel'),
|
||||
actionType: 'close',
|
||||
label: __('cancel')
|
||||
});
|
||||
@ -290,7 +287,7 @@ export default class Drawer extends React.Component<DrawerProps> {
|
||||
ret.push({
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
testid: getTestId(testid && `${testid}-confirm`),
|
||||
testIdBuilder: testIdBuilder?.getChild('confirm'),
|
||||
label: __('confirm'),
|
||||
primary: true
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ import {Overlay} from 'amis-core';
|
||||
import {PopOver} from 'amis-core';
|
||||
import {TooltipWrapper} from 'amis-ui';
|
||||
import {isDisabled, isVisible, noop, filterClassNameObject} from 'amis-core';
|
||||
import {filter, buildTestId, getTestId} from 'amis-core';
|
||||
import {filter} from 'amis-core';
|
||||
import {Icon, hasIcon} from 'amis-ui';
|
||||
import {
|
||||
BaseSchema,
|
||||
@ -52,8 +52,6 @@ export interface DropdownButtonSchema extends BaseSchema {
|
||||
*/
|
||||
buttons?: Array<DropdownButton>;
|
||||
|
||||
testid?: string;
|
||||
|
||||
/**
|
||||
* 内容区域
|
||||
*/
|
||||
@ -255,7 +253,13 @@ export default class DropDownButton extends React.Component<
|
||||
button: DropdownButton,
|
||||
index: number | string
|
||||
): React.ReactNode {
|
||||
const {render, classnames: cx, data, testid, ignoreConfirm} = this.props;
|
||||
const {
|
||||
render,
|
||||
classnames: cx,
|
||||
data,
|
||||
ignoreConfirm,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
index = typeof index === 'number' ? index.toString() : index;
|
||||
|
||||
if (typeof button !== 'string' && Array.isArray(button?.children)) {
|
||||
@ -302,11 +306,13 @@ export default class DropDownButton extends React.Component<
|
||||
`button/${index}`,
|
||||
{
|
||||
type: 'button',
|
||||
testid:
|
||||
testid &&
|
||||
`${getTestId(testid, data)}-${button.testid || index}`,
|
||||
...(button as any),
|
||||
className: ''
|
||||
className: '',
|
||||
// 防止dropdown中button没有 testid或者id
|
||||
testIdBuilder: testIdBuilder?.getChild(
|
||||
button.label || index,
|
||||
data
|
||||
)
|
||||
},
|
||||
{
|
||||
isMenuItem: true,
|
||||
@ -426,8 +432,8 @@ export default class DropDownButton extends React.Component<
|
||||
trigger,
|
||||
data,
|
||||
hideCaret,
|
||||
testid,
|
||||
env
|
||||
env,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -444,7 +450,6 @@ export default class DropDownButton extends React.Component<
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
{...buildTestId(testid, data)}
|
||||
onMouseEnter={trigger === 'hover' ? this.open : () => {}}
|
||||
onMouseLeave={trigger === 'hover' ? this.close : () => {}}
|
||||
ref={this.domRef}
|
||||
@ -459,6 +464,7 @@ export default class DropDownButton extends React.Component<
|
||||
<button
|
||||
onClick={this.toogle}
|
||||
disabled={disabled || btnDisabled}
|
||||
{...testIdBuilder?.getTestId(data)}
|
||||
className={cx(
|
||||
'Button',
|
||||
btnClassName,
|
||||
|
@ -8,8 +8,7 @@ import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
CustomStyle,
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {Schema} from 'amis-core';
|
||||
import {BaseSchema, SchemaCollection, SchemaObject} from '../Schema';
|
||||
@ -152,7 +151,6 @@ export default class Flex extends React.Component<FlexProps, object> {
|
||||
themeCss: wrapperCustomStyle
|
||||
})
|
||||
)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{(Array.isArray(items) ? items : items ? [items] : []).map(
|
||||
(item, key) =>
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
OptionsControlProps,
|
||||
FormOptionsControl
|
||||
} from 'amis-core';
|
||||
import type {Option} from 'amis-core';
|
||||
import {Option, TestIdBuilder} from 'amis-core';
|
||||
import {ActionObject, isObject} from 'amis-core';
|
||||
import type {BadgeObject} from 'amis-ui';
|
||||
import {getLevelFromClassName, autobind, isEmpty} from 'amis-core';
|
||||
@ -34,6 +34,7 @@ export interface ButtonGroupProps
|
||||
| 'btnClassName'
|
||||
> {
|
||||
options: Array<Option>;
|
||||
testIdBuilder: TestIdBuilder;
|
||||
}
|
||||
|
||||
export default class ButtonGroupControl extends React.Component<
|
||||
@ -102,6 +103,7 @@ export default class ButtonGroupControl extends React.Component<
|
||||
vertical,
|
||||
tiled,
|
||||
badge,
|
||||
testIdBuilder,
|
||||
translate: __
|
||||
} = props;
|
||||
|
||||
@ -137,6 +139,9 @@ export default class ButtonGroupControl extends React.Component<
|
||||
active && 'ButtonGroup-button--active'
|
||||
),
|
||||
disabled: option.disabled || disabled,
|
||||
testIdBuilder: testIdBuilder?.getChild(
|
||||
`item-${option[labelField || 'label'] || key}`
|
||||
),
|
||||
onClick: (e: React.UIEvent<any>) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
|
@ -319,6 +319,7 @@ export default class ChainedSelectControl extends React.Component<
|
||||
multiple,
|
||||
mobileUI,
|
||||
env,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
const arr = Array.isArray(value)
|
||||
@ -341,6 +342,7 @@ export default class ChainedSelectControl extends React.Component<
|
||||
}
|
||||
classPrefix={ns}
|
||||
key="base"
|
||||
testIdBuilder={testIdBuilder?.getChild('base')}
|
||||
options={Array.isArray(options) ? options : []}
|
||||
value={arr[0]}
|
||||
onChange={this.handleChange.bind(this, 0)}
|
||||
@ -361,6 +363,7 @@ export default class ChainedSelectControl extends React.Component<
|
||||
}
|
||||
classPrefix={ns}
|
||||
key={`x-${index + 1}`}
|
||||
testIdBuilder={testIdBuilder?.getChild(`x-${index + 1}`)}
|
||||
options={Array.isArray(options) ? options : []}
|
||||
value={arr[index + 1]}
|
||||
onChange={this.handleChange.bind(this, index + 1)}
|
||||
|
@ -12,6 +12,7 @@ import {autobind, createObject} from 'amis-core';
|
||||
import {ActionObject} from 'amis-core';
|
||||
import {BaseSchema, FormBaseControlSchema} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface SchemaMap {
|
||||
checkbox: CheckboxControlSchema;
|
||||
@ -49,6 +50,7 @@ export interface CheckboxControlSchema extends FormBaseControlSchema {
|
||||
partial?: boolean;
|
||||
optionType?: 'default' | 'button';
|
||||
checked?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface CheckboxProps
|
||||
@ -139,6 +141,7 @@ export default class CheckboxControl extends React.Component<
|
||||
optionType,
|
||||
checked,
|
||||
labelClassName,
|
||||
testIdBuilder,
|
||||
classPrefix: ns
|
||||
} = this.props;
|
||||
|
||||
@ -155,6 +158,7 @@ export default class CheckboxControl extends React.Component<
|
||||
optionType={optionType}
|
||||
checked={checked}
|
||||
labelClassName={labelClassName}
|
||||
testIdBuilder={testIdBuilder}
|
||||
>
|
||||
{option ? render('option', option) : null}
|
||||
</Checkbox>
|
||||
|
@ -12,6 +12,7 @@ import type {ActionObject, Api, OptionsControlProps, Option} from 'amis-core';
|
||||
import {Checkbox, Icon, Spinner} from 'amis-ui';
|
||||
import {FormOptionsSchema} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
@ -43,6 +44,7 @@ export interface CheckboxesControlSchema extends FormOptionsSchema {
|
||||
* 自定义选项展示
|
||||
*/
|
||||
menuTpl?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface CheckboxesProps
|
||||
@ -258,10 +260,14 @@ export default class CheckboxesControl extends React.Component<
|
||||
translate: __,
|
||||
optionType,
|
||||
menuTpl,
|
||||
data
|
||||
data,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const labelText = String(option[labelField || 'label']);
|
||||
const optionLabelClassName = option['labelClassName'];
|
||||
const itemTestIdBuilder = testIdBuilder?.getChild(
|
||||
'item-' + labelText || index
|
||||
);
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
@ -274,6 +280,7 @@ export default class CheckboxesControl extends React.Component<
|
||||
labelClassName={optionLabelClassName || labelClassName}
|
||||
description={option.description}
|
||||
optionType={optionType}
|
||||
testIdBuilder={itemTestIdBuilder}
|
||||
>
|
||||
{menuTpl
|
||||
? render(`checkboxes/${index}`, menuTpl, {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
FormItem,
|
||||
FormControlProps,
|
||||
@ -57,6 +59,8 @@ import type {SchemaTokenizeableString} from '../../Schema';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export type ComboCondition = {
|
||||
test: string;
|
||||
items: Array<ComboSubControl>;
|
||||
@ -75,6 +79,7 @@ export type ComboSubControl = SchemaObject & {
|
||||
* 列类名,可以用来修改这类宽度。
|
||||
*/
|
||||
columnClassName?: SchemaClassName;
|
||||
testid?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -283,6 +288,7 @@ export interface ComboControlSchema extends FormBaseControlSchema {
|
||||
maxLengthValidateFailed?: string;
|
||||
};
|
||||
updatePristineAfterStoreDataReInit?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export type ComboRendererEvent = 'add' | 'delete' | 'tabsChange';
|
||||
@ -1395,9 +1401,10 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
removable,
|
||||
deleteBtn,
|
||||
mobileUI,
|
||||
data
|
||||
data,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const delTestIdBuilder = testIdBuilder?.getChild(`delete-btn-${index}`);
|
||||
const finnalRemovable =
|
||||
store.removable !== false && // minLength ?
|
||||
!disabled && // 控件自身是否禁用
|
||||
@ -1424,6 +1431,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
'Combo-delController',
|
||||
deleteBtn ? deleteBtn.className : ''
|
||||
),
|
||||
testIdBuilder: delTestIdBuilder,
|
||||
onClick: (e: any) => {
|
||||
if (!deleteBtn.onClick) {
|
||||
this.deleteItem(index);
|
||||
@ -1461,7 +1469,8 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
type: 'button',
|
||||
className: cx('Combo-delController'),
|
||||
label: deleteBtn,
|
||||
onClick: this.deleteItem.bind(this, index)
|
||||
onClick: this.deleteItem.bind(this, index),
|
||||
testIdBuilder: delTestIdBuilder
|
||||
});
|
||||
}
|
||||
|
||||
@ -1473,6 +1482,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
className={cx(`Combo-delBtn ${!store.removable ? 'is-disabled' : ''}`)}
|
||||
data-tooltip={!mobileUI ? __('delete') : null}
|
||||
data-position="bottom"
|
||||
{...delTestIdBuilder?.getTestId()}
|
||||
>
|
||||
{deleteIcon ? (
|
||||
<i className={deleteIcon} />
|
||||
@ -1500,10 +1510,12 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
addIcon,
|
||||
conditions,
|
||||
translate: __,
|
||||
tabsMode
|
||||
tabsMode,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const hasConditions = Array.isArray(conditions) && conditions.length;
|
||||
const addBtnTestIdBuilder = testIdBuilder?.getChild('add-button');
|
||||
return (
|
||||
<>
|
||||
{store.addable &&
|
||||
@ -1518,7 +1530,8 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
level: 'info',
|
||||
size: 'sm',
|
||||
closeOnClick: true,
|
||||
btnClassName: addButtonClassName
|
||||
btnClassName: addButtonClassName,
|
||||
testIdBuilder: addBtnTestIdBuilder
|
||||
},
|
||||
{
|
||||
buttons: conditions?.map(item => ({
|
||||
@ -1539,12 +1552,14 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
render('add-button', {
|
||||
...addBtn,
|
||||
type: 'button',
|
||||
testIdBuilder: addBtnTestIdBuilder,
|
||||
onClick: () => this.addItem()
|
||||
})
|
||||
) : (
|
||||
<Button
|
||||
className={cx(`Combo-addBtn`, addButtonClassName)}
|
||||
onClick={this.addItem}
|
||||
testIdBuilder={addBtnTestIdBuilder}
|
||||
>
|
||||
{addIcon ? <Icon icon="plus-fine" className="icon" /> : null}
|
||||
<span>{__(addButtonText || 'add')}</span>
|
||||
@ -1804,8 +1819,19 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
lazyLoad,
|
||||
translate: __,
|
||||
static: isStatic,
|
||||
testIdBuilder,
|
||||
updatePristineAfterStoreDataReInit
|
||||
} = this.props;
|
||||
const finnalItems = Array.isArray(finnalControls)
|
||||
? finnalControls.map((item, itemIndex) => {
|
||||
const indexKey = index !== undefined && index >= 0 ? `-${index}` : '';
|
||||
const key = `item-${item.testid || item.id || itemIndex}` + indexKey;
|
||||
return {
|
||||
...item,
|
||||
testIdBuilder: testIdBuilder?.getChild(key)
|
||||
};
|
||||
})
|
||||
: finnalControls;
|
||||
|
||||
// 单个
|
||||
if (!multiple) {
|
||||
@ -1813,7 +1839,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
'single',
|
||||
{
|
||||
type: 'form',
|
||||
body: finnalControls,
|
||||
body: finnalItems,
|
||||
wrapperComponent: 'div',
|
||||
wrapWithPanel: false,
|
||||
mode: multiLine ? subFormMode || 'normal' : 'row',
|
||||
@ -1840,7 +1866,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
`multiple/${index}`,
|
||||
{
|
||||
type: 'form',
|
||||
body: finnalControls,
|
||||
body: finnalItems,
|
||||
wrapperComponent: 'div',
|
||||
wrapWithPanel: false,
|
||||
mode: tabsMode ? subFormMode : multiLine ? subFormMode : 'row',
|
||||
|
@ -16,6 +16,8 @@ import {localeable, LocaleProps} from 'amis-core';
|
||||
import {FormBaseControlSchema} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
/**
|
||||
* City 城市选择框。
|
||||
* 文档:https://aisuda.bce.baidu.com/amis/zh-CN/components/form/city
|
||||
@ -86,6 +88,7 @@ export interface CityPickerProps
|
||||
[propName: string]: any;
|
||||
};
|
||||
popOverContainer?: any;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface CityDb {
|
||||
@ -419,7 +422,8 @@ export class CityPicker extends React.Component<
|
||||
translate: __,
|
||||
loadingConfig,
|
||||
popOverContainer,
|
||||
itemClassName
|
||||
itemClassName,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const {provinceCode, cityCode, districtCode, street, db} = this.state;
|
||||
@ -437,6 +441,7 @@ export class CityPicker extends React.Component<
|
||||
value={provinceCode || ''}
|
||||
onChange={this.handleProvinceChange}
|
||||
popOverContainer={popOverContainer}
|
||||
testIdBuilder={testIdBuilder?.getChild('province')}
|
||||
/>
|
||||
|
||||
{allowCity && db.city[provinceCode] && db.city[provinceCode].length ? (
|
||||
@ -451,6 +456,7 @@ export class CityPicker extends React.Component<
|
||||
value={cityCode || ''}
|
||||
onChange={this.handleCityChange}
|
||||
popOverContainer={popOverContainer}
|
||||
testIdBuilder={testIdBuilder?.getChild('city')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -470,6 +476,7 @@ export class CityPicker extends React.Component<
|
||||
value={districtCode || ''}
|
||||
onChange={this.handleDistrictChange}
|
||||
popOverContainer={popOverContainer}
|
||||
testIdBuilder={testIdBuilder?.getChild('district')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -481,6 +488,7 @@ export class CityPicker extends React.Component<
|
||||
onBlur={this.handleStreetEnd}
|
||||
placeholder={__('City.street')}
|
||||
disabled={disabled}
|
||||
{...testIdBuilder?.getChild('street').getTestId()}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@ -578,7 +586,8 @@ export class LocationControl extends React.Component<LocationControlProps> {
|
||||
env,
|
||||
mobileUI,
|
||||
popOverContainer,
|
||||
itemClassName
|
||||
itemClassName,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return mobileUI ? (
|
||||
@ -607,6 +616,7 @@ export class LocationControl extends React.Component<LocationControlProps> {
|
||||
joinValues={joinValues}
|
||||
allowStreet={allowStreet}
|
||||
disabled={disabled}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import {ActionObject} from 'amis-core';
|
||||
import type {ShortCuts} from 'amis-ui/lib/components/DatePicker';
|
||||
import {FormBaseControlSchema} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
/**
|
||||
* DateRange 日期范围控件
|
||||
* 文档:https://aisuda.bce.baidu.com/amis/zh-CN/components/form/date-range
|
||||
@ -134,6 +134,7 @@ export interface DateRangeProps
|
||||
format: string;
|
||||
valueFormat: string;
|
||||
joinValues: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export default class DateRangeControl extends React.Component<DateRangeProps> {
|
||||
|
@ -10,7 +10,8 @@ import {
|
||||
autobind,
|
||||
isObject,
|
||||
resolveEventData,
|
||||
dataMapping
|
||||
dataMapping,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {FormBaseControlSchema, SchemaTokenizeableString} from '../../Schema';
|
||||
import type {CellValue, CellRichTextValue} from 'exceljs';
|
||||
@ -64,6 +65,8 @@ export interface InputExcelControlSchema extends FormBaseControlSchema {
|
||||
autoFill?: {
|
||||
[propName: string]: SchemaTokenizeableString;
|
||||
};
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface ExcelProps
|
||||
@ -401,7 +404,8 @@ export default class ExcelControl extends React.PureComponent<
|
||||
classPrefix: ns,
|
||||
disabled,
|
||||
translate: __,
|
||||
placeholder
|
||||
placeholder,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -415,8 +419,14 @@ export default class ExcelControl extends React.PureComponent<
|
||||
>
|
||||
{({getRootProps, getInputProps}) => (
|
||||
<section className={cx('ExcelControl-container', className)}>
|
||||
<div {...getRootProps({className: cx('ExcelControl-dropzone')})}>
|
||||
<input {...getInputProps()} />
|
||||
<div
|
||||
{...getRootProps({className: cx('ExcelControl-dropzone')})}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<input
|
||||
{...getInputProps()}
|
||||
{...testIdBuilder?.getChild('input').getTestId()}
|
||||
/>
|
||||
{this.state.filename ? (
|
||||
__('Excel.parsed', {
|
||||
filename: this.state.filename
|
||||
|
@ -3,8 +3,7 @@ import {
|
||||
FormItem,
|
||||
FormControlProps,
|
||||
prettyBytes,
|
||||
resolveEventData,
|
||||
buildTestId
|
||||
resolveEventData
|
||||
} from 'amis-core';
|
||||
import find from 'lodash/find';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
@ -247,8 +246,6 @@ export interface FileControlSchema extends FormBaseControlSchema {
|
||||
* 是否为拖拽上传
|
||||
*/
|
||||
drag?: boolean;
|
||||
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
export interface FileProps
|
||||
@ -1358,9 +1355,9 @@ export default class FileControl extends React.Component<FileProps, FileState> {
|
||||
data,
|
||||
documentation,
|
||||
documentLink,
|
||||
testid,
|
||||
env,
|
||||
container
|
||||
container,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
let {files, uploading, error} = this.state;
|
||||
const nameField = this.props.nameField || 'name';
|
||||
@ -1419,7 +1416,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
|
||||
disabled={disabled}
|
||||
{...getInputProps()}
|
||||
capture={capture as any}
|
||||
{...buildTestId(testid && `${testid}-input`)}
|
||||
{...testIdBuilder?.getChild('input').getTestId()}
|
||||
/>
|
||||
|
||||
{drag || isDragActive ? (
|
||||
@ -1458,13 +1455,13 @@ export default class FileControl extends React.Component<FileProps, FileState> {
|
||||
'is-disabled':
|
||||
multiple && !!maxLength && files.length >= maxLength
|
||||
})}
|
||||
testid={testid}
|
||||
tooltip={
|
||||
multiple && maxLength && files.length >= maxLength
|
||||
? __('File.maxLength', {maxLength})
|
||||
: ''
|
||||
}
|
||||
onClick={this.handleSelect}
|
||||
testIdBuilder={testIdBuilder?.getChild('select')}
|
||||
>
|
||||
<Icon icon="upload" className="icon" />
|
||||
<span>
|
||||
@ -1594,6 +1591,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
|
||||
{!autoUpload && !hideUploadButton && files.length ? (
|
||||
<Button
|
||||
level="default"
|
||||
testIdBuilder={testIdBuilder?.getChild('upload')}
|
||||
disabled={!hasPending}
|
||||
className={cx('FileControl-uploadBtn', btnUploadClassName)}
|
||||
onClick={this.toggleUpload}
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
resolveEventData,
|
||||
CustomStyle,
|
||||
formatInputThemeCss,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import cx from 'classnames';
|
||||
import {NumberInput, Select} from 'amis-ui';
|
||||
@ -158,6 +159,8 @@ export interface NumberProps extends FormControlProps {
|
||||
* 是否在清空内容时从数据域中删除该表单项对应的值
|
||||
*/
|
||||
clearValueOnEmpty?: boolean;
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
interface NumberState {
|
||||
@ -446,7 +449,8 @@ export default class NumberControl extends React.Component<
|
||||
inputControlClassName,
|
||||
id,
|
||||
env,
|
||||
name
|
||||
name,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {unit} = this.state;
|
||||
const finalPrecision = this.filterNum(precision);
|
||||
@ -525,6 +529,7 @@ export default class NumberControl extends React.Component<
|
||||
displayMode={displayMode}
|
||||
big={big}
|
||||
clearValueOnEmpty={clearValueOnEmpty}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
{Array.isArray(unitOptions) && unitOptions.length !== 0 ? (
|
||||
unitOptions.length > 1 ? (
|
||||
|
@ -12,6 +12,7 @@ import {isMobile} from 'amis-core';
|
||||
import {PopUp} from 'amis-ui';
|
||||
import {autobind} from 'amis-core';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
/**
|
||||
* SubForm 子表单
|
||||
* 文档:https://aisuda.bce.baidu.com/amis/zh-CN/components/form/subform
|
||||
@ -112,6 +113,7 @@ export interface SubFormProps extends FormControlProps {
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
labelField?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface SubFormState {
|
||||
@ -418,7 +420,8 @@ export default class SubFormControl extends React.PureComponent<
|
||||
addable,
|
||||
removable,
|
||||
minLength,
|
||||
addButtonText
|
||||
addButtonText,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -435,6 +438,7 @@ export default class SubFormControl extends React.PureComponent<
|
||||
itemClassName
|
||||
)}
|
||||
key={key}
|
||||
{...testIdBuilder?.getChild(`item-${key}`).getTestId()}
|
||||
>
|
||||
{draggable && value.length > 1 ? (
|
||||
<a className={cx('SubForm-valueDragBar')}>
|
||||
@ -502,6 +506,7 @@ export default class SubFormControl extends React.PureComponent<
|
||||
value.length >= maxLength
|
||||
)
|
||||
}
|
||||
{...testIdBuilder?.getChild('add-button').getTestId()}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>{__(addButtonText || 'SubForm.add')}</span>
|
||||
@ -530,9 +535,12 @@ export default class SubFormControl extends React.PureComponent<
|
||||
btnLabel,
|
||||
render,
|
||||
data,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const tIdBuilder = testIdBuilder?.getChild('edit-single');
|
||||
|
||||
return (
|
||||
<div className={cx('SubForm-values', itemsClassName)} key="values">
|
||||
<div
|
||||
@ -546,6 +554,7 @@ export default class SubFormControl extends React.PureComponent<
|
||||
onClick={this.editSingle}
|
||||
data-tooltip={__('SubForm.editDetail')}
|
||||
data-position="bottom"
|
||||
{...tIdBuilder?.getTestId()}
|
||||
>
|
||||
<span className={cx('SubForm-valueLabel')}>
|
||||
{btnLabel &&
|
||||
@ -566,7 +575,10 @@ export default class SubFormControl extends React.PureComponent<
|
||||
stripTag(value[labelField])) ||
|
||||
__(defaultLabel))}
|
||||
</span>
|
||||
<a className={cx('SubForm-valueEdit')}>
|
||||
<a
|
||||
className={cx('SubForm-valueEdit')}
|
||||
{...tIdBuilder?.getChild('icon').getTestId()}
|
||||
>
|
||||
<Icon icon="pencil" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
|
@ -980,7 +980,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
isCreateMode = false,
|
||||
editRowIndex?: number
|
||||
): Array<any> {
|
||||
const {env, enableStaticTransform} = this.props;
|
||||
const {env, enableStaticTransform, testIdBuilder} = this.props;
|
||||
let columns: Array<any> = Array.isArray(props.columns)
|
||||
? props.columns.concat()
|
||||
: [];
|
||||
@ -1016,6 +1016,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.addItem.bind(this, rowIndex + offset, undefined)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`addRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{props.addBtnIcon ? (
|
||||
<Icon
|
||||
@ -1051,6 +1054,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.copyItem.bind(this, rowIndex + offset, undefined)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`copyRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{props.copyBtnIcon ? (
|
||||
<Icon
|
||||
@ -1149,6 +1155,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
disabled={disabled}
|
||||
onClick={() => this.editItem(rowIndex + offset)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`editRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{/* 兼容之前的写法 */}
|
||||
{typeof props.updateBtnIcon !== 'undefined' ? (
|
||||
@ -1195,6 +1204,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
props.popOverContainer || env.getModalContainer
|
||||
}
|
||||
onClick={this.confirmEdit}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`confirmRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{props.confirmBtnIcon ? (
|
||||
<Icon
|
||||
@ -1232,6 +1244,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
props.popOverContainer || env.getModalContainer
|
||||
}
|
||||
onClick={this.cancelEdit}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`cancelRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{props.cancelBtnIcon ? (
|
||||
<Icon
|
||||
@ -1287,6 +1302,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.removeItem.bind(this, rowIndex + offset)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`delRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{props.deleteBtnIcon ? (
|
||||
<Icon
|
||||
@ -1606,7 +1624,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
showFooterAddBtn,
|
||||
footerAddBtn,
|
||||
toolbarClassName,
|
||||
onEvent
|
||||
onEvent,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const maxLength = this.resolveVariableProps(this.props, 'maxLength');
|
||||
|
||||
@ -1666,7 +1685,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
offset,
|
||||
rowClassName,
|
||||
rowClassNameExpr,
|
||||
onPristineChange: this.handlePristineChange
|
||||
onPristineChange: this.handlePristineChange,
|
||||
testIdBuilder: testIdBuilder?.getChild('table')
|
||||
}
|
||||
)}
|
||||
{(!isStatic &&
|
||||
@ -1689,7 +1709,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
},
|
||||
{
|
||||
disabled: this.computedAddBtnDisabled(),
|
||||
onClick: () => this.addItem(this.state.items.length)
|
||||
onClick: () => this.addItem(this.state.items.length),
|
||||
testIdBuilder: testIdBuilder?.getChild('add')
|
||||
}
|
||||
)
|
||||
: null}
|
||||
@ -1705,7 +1726,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
perPage,
|
||||
total: this.state.items.length,
|
||||
onPageChange: this.handlePageChange,
|
||||
className: 'InputTable-pager'
|
||||
className: 'InputTable-pager',
|
||||
testIdBuilder: testIdBuilder?.getChild('page')
|
||||
}
|
||||
)
|
||||
: null}
|
||||
|
@ -577,7 +577,8 @@ export default class TagControl extends React.PureComponent<
|
||||
valueField,
|
||||
env,
|
||||
mobileUI,
|
||||
labelField
|
||||
labelField,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const term = this.state.inputValue;
|
||||
@ -635,6 +636,7 @@ export default class TagControl extends React.PureComponent<
|
||||
popOverContainer={popOverContainer || env.getModalContainer}
|
||||
allowInput={!mobileUI || (mobileUI && !options?.length)}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('resule-box')}
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner loadingConfig={loadingConfig} size="sm" />
|
||||
@ -716,6 +718,7 @@ export default class TagControl extends React.PureComponent<
|
||||
options={finnalOptions}
|
||||
itemRender={this.renderItem}
|
||||
highlightIndex={highlightedIndex}
|
||||
testIdBuilder={testIdBuilder?.getChild('options')}
|
||||
getItemProps={({
|
||||
item,
|
||||
index
|
||||
|
@ -19,9 +19,7 @@ import {
|
||||
createObject,
|
||||
setVariable,
|
||||
ucFirst,
|
||||
isEffectiveApi,
|
||||
getTestId,
|
||||
buildTestId
|
||||
isEffectiveApi
|
||||
} from 'amis-core';
|
||||
import {Icon, SpinnerExtraProps, Input, Spinner, OverflowTpl} from 'amis-ui';
|
||||
import {ActionSchema} from '../Action';
|
||||
@ -119,8 +117,6 @@ export interface TextControlSchema extends FormOptionsSchema {
|
||||
|
||||
/** 在内容为空的时候清除值 */
|
||||
clearValueOnEmpty?: boolean;
|
||||
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
export type InputTextRendererEvent =
|
||||
@ -730,8 +726,8 @@ export default class TextControl extends React.PureComponent<
|
||||
themeCss,
|
||||
css,
|
||||
id,
|
||||
testid,
|
||||
nativeAutoComplete
|
||||
nativeAutoComplete,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
let type = this.props.type?.replace(/^(?:native|input)\-/, '');
|
||||
|
||||
@ -810,7 +806,7 @@ export default class TextControl extends React.PureComponent<
|
||||
}
|
||||
)}
|
||||
onClick={this.handleClick}
|
||||
{...buildTestId(testid, data)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<>
|
||||
{filteredPlaceholder &&
|
||||
@ -988,8 +984,8 @@ export default class TextControl extends React.PureComponent<
|
||||
themeCss,
|
||||
css,
|
||||
id,
|
||||
testid,
|
||||
nativeAutoComplete
|
||||
nativeAutoComplete,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const type = this.props.type?.replace(/^(?:native|input)\-/, '');
|
||||
@ -1017,7 +1013,7 @@ export default class TextControl extends React.PureComponent<
|
||||
inputControlClassName,
|
||||
inputOnly ? className : ''
|
||||
)}
|
||||
{...buildTestId(testid, data)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{prefix ? (
|
||||
<span className={cx('TextControl-inputPrefix')}>
|
||||
@ -1045,7 +1041,7 @@ export default class TextControl extends React.PureComponent<
|
||||
className={cx(nativeInputClassName, {
|
||||
'TextControl-input-password': type === 'password' && revealPassword
|
||||
})}
|
||||
{...buildTestId(testid && `${testid}-input`)}
|
||||
{...testIdBuilder?.getChild('input').getTestId()}
|
||||
/>
|
||||
{clearable && !disabled && !readOnly && value ? (
|
||||
<a onClick={this.clearValue} className={cx('TextControl-clear')}>
|
||||
|
@ -473,7 +473,8 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
searchable,
|
||||
searchConfig = {},
|
||||
heightAuto,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
let {highlightTxt} = this.props;
|
||||
const {filteredOptions, keyword} = this.state;
|
||||
@ -537,6 +538,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
itemRender={menuTpl ? this.renderOptionItem : undefined}
|
||||
enableDefaultIcon={enableDefaultIcon}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('tree')}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -546,6 +548,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
'is-sticky': searchable && searchConfig?.sticky,
|
||||
'h-auto': heightAuto
|
||||
})}
|
||||
{...testIdBuilder?.getChild('control').getTestId()}
|
||||
>
|
||||
<Spinner
|
||||
size="sm"
|
||||
@ -566,6 +569,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
{...omit(searchConfig, 'className', 'sticky')}
|
||||
onSearch={this.handleSearch}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('search')}
|
||||
/>
|
||||
{TreeCmpt}
|
||||
</>
|
||||
|
@ -180,7 +180,8 @@ export default class ListControl extends React.Component<ListProps, any> {
|
||||
data,
|
||||
labelField,
|
||||
listClassName,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
let body: JSX.Element | null = null;
|
||||
@ -202,6 +203,9 @@ export default class ListControl extends React.Component<ListProps, any> {
|
||||
? this.handleDBClick.bind(this, option)
|
||||
: undefined
|
||||
}
|
||||
{...testIdBuilder
|
||||
?.getChild(`options-${option.value || key}`)
|
||||
.getTestId()}
|
||||
>
|
||||
{itemSchema
|
||||
? render(
|
||||
|
@ -17,6 +17,8 @@ import {ApiObject, ActionObject, isMobile} from 'amis-core';
|
||||
import {FormBaseControlSchema, SchemaApi} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
/**
|
||||
* Matrix 选择控件。适合做权限勾选。
|
||||
* 文档:https://aisuda.bce.baidu.com/amis/zh-CN/components/form/matrix
|
||||
@ -88,6 +90,7 @@ export interface MatrixProps extends FormControlProps, SpinnerExtraProps {
|
||||
* 横向选择所有能力
|
||||
*/
|
||||
xCheckAll?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface MatrixState {
|
||||
@ -405,7 +408,8 @@ export default class MatrixCheckbox extends React.Component<
|
||||
multiple,
|
||||
textAlign,
|
||||
xCheckAll,
|
||||
yCheckAll
|
||||
yCheckAll,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const value = this.props.value || buildDefaultValue(columns, rows);
|
||||
@ -453,6 +457,7 @@ export default class MatrixCheckbox extends React.Component<
|
||||
onChange={(checked: boolean) =>
|
||||
this.toggleRowCheckAll(checked, value, y)
|
||||
}
|
||||
testIdBuilder={testIdBuilder?.getChild(y)}
|
||||
/>
|
||||
) : null}
|
||||
{row.label}
|
||||
@ -478,6 +483,7 @@ export default class MatrixCheckbox extends React.Component<
|
||||
onChange={(checked: boolean) =>
|
||||
this.toggleItem(checked, x, y)
|
||||
}
|
||||
testIdBuilder={testIdBuilder?.getChild(`${x}-${y}`)}
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
|
@ -680,7 +680,8 @@ export default class PickerControl extends React.PureComponent<
|
||||
valueField,
|
||||
embed,
|
||||
source,
|
||||
strictMode
|
||||
strictMode,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {maxTagCount, overflowTagPopoverInCRUD, displayPosition} =
|
||||
this.getOverflowConfig();
|
||||
@ -693,6 +694,7 @@ export default class PickerControl extends React.PureComponent<
|
||||
multiple,
|
||||
strictMode,
|
||||
onSelect: embed ? this.handleSelect : undefined,
|
||||
testIdBuilder: testIdBuilder?.getChild('body-schema'),
|
||||
ref: this.crudRef,
|
||||
popOverContainer,
|
||||
...(embed ||
|
||||
@ -727,7 +729,8 @@ export default class PickerControl extends React.PureComponent<
|
||||
themeCss,
|
||||
css,
|
||||
id,
|
||||
classPrefix: ns
|
||||
classPrefix: ns,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
return (
|
||||
<div className={cx(`PickerControl`, {'is-mobile': mobileUI}, className)}>
|
||||
@ -768,7 +771,10 @@ export default class PickerControl extends React.PureComponent<
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className={cx('Picker-valueWrap')}>
|
||||
<div
|
||||
className={cx('Picker-valueWrap')}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{this.renderValues()}
|
||||
|
||||
<input
|
||||
@ -788,7 +794,11 @@ export default class PickerControl extends React.PureComponent<
|
||||
</a>
|
||||
) : null}
|
||||
|
||||
<span onClick={this.open} className={cx('Picker-btn')}>
|
||||
<span
|
||||
onClick={this.open}
|
||||
className={cx('Picker-btn')}
|
||||
{...testIdBuilder?.getChild('picker-open-btn').getTestId()}
|
||||
>
|
||||
<Icon
|
||||
icon="window-restore"
|
||||
className={cx(
|
||||
@ -817,7 +827,8 @@ export default class PickerControl extends React.PureComponent<
|
||||
className: modalClassName,
|
||||
body: {
|
||||
children: this.renderBody
|
||||
}
|
||||
},
|
||||
testIdBuilder: testIdBuilder?.getChild('modal')
|
||||
},
|
||||
{
|
||||
key: 'modal',
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
OptionsControlProps,
|
||||
Option,
|
||||
FormOptionsControl,
|
||||
resolveEventData
|
||||
resolveEventData,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {autobind, isEmpty, createObject} from 'amis-core';
|
||||
import {ActionObject} from 'amis-core';
|
||||
@ -37,6 +38,7 @@ export interface RadiosProps extends OptionsControlProps {
|
||||
labelClassName?: string;
|
||||
/** 选项CSS类名 */
|
||||
optionClassName?: string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export default class RadiosControl extends React.Component<RadiosProps, any> {
|
||||
@ -126,7 +128,8 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
|
||||
data,
|
||||
translate: __,
|
||||
optionType,
|
||||
level
|
||||
level,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -151,6 +154,7 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
|
||||
itemClassName={itemClassName}
|
||||
optionType={optionType}
|
||||
level={level}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
isEffectiveApi,
|
||||
isApiOutdated,
|
||||
createObject,
|
||||
autobind
|
||||
autobind,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {TransferDropDown, Spinner, Select, SpinnerExtraProps} from 'amis-ui';
|
||||
import {FormOptionsSchema, SchemaApi} from '../../Schema';
|
||||
@ -154,9 +155,9 @@ export interface SelectControlSchema
|
||||
* 检索函数
|
||||
*/
|
||||
filterOption?: 'string';
|
||||
|
||||
testid?: string;
|
||||
};
|
||||
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface SelectProps extends OptionsControlProps, SpinnerExtraProps {
|
||||
@ -411,12 +412,15 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||
|
||||
@autobind
|
||||
renderMenu(option: Option, state: any) {
|
||||
const {menuTpl, render, data, optionClassName} = this.props;
|
||||
const {menuTpl, render, data, optionClassName, testIdBuilder} = this.props;
|
||||
|
||||
return render(`menu/${state.index}`, menuTpl, {
|
||||
showNativeTitle: true,
|
||||
className: cx('Select-option-content', optionClassName),
|
||||
data: createObject(createObject(data, state), option)
|
||||
data: createObject(createObject(data, state), option),
|
||||
testIdBuilder: testIdBuilder?.getChild(
|
||||
'option-' + option.value || state.index
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,6 @@ export interface SwitchControlSchema extends FormBaseControlSchema {
|
||||
|
||||
/** 是否处于加载状态 */
|
||||
loading?: boolean;
|
||||
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
export interface SwitchProps extends FormControlProps, SpinnerExtraProps {
|
||||
@ -147,7 +145,7 @@ export default class SwitchControl extends React.Component<SwitchProps, any> {
|
||||
disabled,
|
||||
loading,
|
||||
loadingConfig,
|
||||
testid
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const {on, off} = this.getResult();
|
||||
@ -167,7 +165,7 @@ export default class SwitchControl extends React.Component<SwitchProps, any> {
|
||||
size={size as any}
|
||||
loading={loading}
|
||||
loadingConfig={loadingConfig}
|
||||
testid={testid}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -308,7 +308,8 @@ export class TabsTransferRenderer extends BaseTabsTransferRenderer<TabsTransferP
|
||||
menuTpl,
|
||||
data,
|
||||
mobileUI,
|
||||
initiallyOpen = true
|
||||
initiallyOpen = true,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -343,6 +344,7 @@ export class TabsTransferRenderer extends BaseTabsTransferRenderer<TabsTransferP
|
||||
ctx={data}
|
||||
mobileUI={mobileUI}
|
||||
initiallyOpen={initiallyOpen}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
|
@ -59,8 +59,6 @@ export interface TextareaControlSchema extends FormBaseControlSchema {
|
||||
* 重置值
|
||||
*/
|
||||
resetValue?: string;
|
||||
|
||||
testid?: string;
|
||||
}
|
||||
|
||||
export type TextAreaRendererEvent = 'blur' | 'focus' | 'change';
|
||||
|
@ -637,7 +637,8 @@ export class BaseTransferRenderer<
|
||||
popOverContainer,
|
||||
data,
|
||||
autoCheckChildren = true,
|
||||
initiallyOpen = true
|
||||
initiallyOpen = true,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
// 目前 LeftOptions 没有接口可以动态加载
|
||||
@ -729,6 +730,7 @@ export class BaseTransferRenderer<
|
||||
onPageChange={this.handlePageChange}
|
||||
initiallyOpen={initiallyOpen}
|
||||
autoCheckChildren={autoCheckChildren}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
<Spinner
|
||||
overlay
|
||||
|
@ -33,6 +33,7 @@ import {FormOptionsSchema, SchemaApi} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import {TooltipWrapperSchema} from '../TooltipWrapper';
|
||||
import type {ItemRenderStates} from 'amis-ui/lib/components/Selection';
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
/**
|
||||
* Tree 下拉选择框。
|
||||
@ -130,6 +131,7 @@ export interface TreeSelectControlSchema extends FormOptionsSchema {
|
||||
* 是否为选项添加默认的Icon,默认值为true
|
||||
*/
|
||||
enableDefaultIcon?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface TreeSelectProps
|
||||
@ -677,7 +679,8 @@ export default class TreeSelectControl extends React.Component<
|
||||
itemHeight,
|
||||
menuTpl,
|
||||
enableDefaultIcon,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
let filtedOptions =
|
||||
@ -741,6 +744,7 @@ export default class TreeSelectControl extends React.Component<
|
||||
itemRender={menuTpl ? this.renderOptionItem : undefined}
|
||||
enableDefaultIcon={enableDefaultIcon}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -768,7 +772,8 @@ export default class TreeSelectControl extends React.Component<
|
||||
overflowTagPopover,
|
||||
translate: __,
|
||||
env,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {isOpened} = this.state;
|
||||
const resultValue = multiple
|
||||
@ -778,7 +783,11 @@ export default class TreeSelectControl extends React.Component<
|
||||
: '';
|
||||
|
||||
return (
|
||||
<div ref={this.container} className={cx(`TreeSelectControl`, className)}>
|
||||
<div
|
||||
ref={this.container}
|
||||
className={cx(`TreeSelectControl`, className)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<ResultBox
|
||||
popOverContainer={popOverContainer || env.getModalContainer}
|
||||
maxTagCount={maxTagCount}
|
||||
@ -815,6 +824,7 @@ export default class TreeSelectControl extends React.Component<
|
||||
hasDropDownArrow
|
||||
readOnly={mobileUI}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder?.getChild('result-box')}
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner loadingConfig={loadingConfig} size="sm" />
|
||||
|
@ -5,8 +5,7 @@ import {
|
||||
RendererProps,
|
||||
buildStyle,
|
||||
CustomStyle,
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import pick from 'lodash/pick';
|
||||
import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema';
|
||||
@ -241,7 +240,6 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||
})
|
||||
)}
|
||||
style={styleVar}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderColumns(this.props.columns)}
|
||||
<Spinner loadingConfig={loadingConfig} overlay show={loading} />
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {buildTestId, Renderer, RendererProps} from 'amis-core';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {Api, SchemaNode, Schema, ActionObject} from 'amis-core';
|
||||
import {isVisible} from 'amis-core';
|
||||
import {BaseSchema, SchemaObject} from '../Schema';
|
||||
@ -215,11 +215,7 @@ export default class Grid2D extends React.Component<Grid2DProps, object> {
|
||||
gridTemplateRows: templateRows.join(' ')
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={curStyle} {...buildTestId(testid)}>
|
||||
{this.renderGrids()}
|
||||
</div>
|
||||
);
|
||||
return <div style={curStyle}>{this.renderGrids()}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -959,7 +959,8 @@ export default class List extends React.Component<ListProps, object> {
|
||||
checkOnItemClick,
|
||||
itemAction,
|
||||
classnames: cx,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const hasClickActions =
|
||||
onEvent &&
|
||||
@ -979,6 +980,7 @@ export default class List extends React.Component<ListProps, object> {
|
||||
'is-modified': item.modified,
|
||||
'is-moved': item.moved
|
||||
}),
|
||||
testIdBuilder: testIdBuilder?.getChild(index),
|
||||
selectable: store.selectable,
|
||||
checkable: item.checkable,
|
||||
multiple,
|
||||
@ -1180,7 +1182,8 @@ export class ListItem extends React.Component<ListItemProps> {
|
||||
hideCheckToggler,
|
||||
checkOnItemClick,
|
||||
classnames: cx,
|
||||
classPrefix: ns
|
||||
classPrefix: ns,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
if (dragging) {
|
||||
@ -1199,6 +1202,7 @@ export class ListItem extends React.Component<ListItemProps> {
|
||||
checked={selected}
|
||||
onChange={this.handleCheck}
|
||||
inline
|
||||
testIdBuilder={testIdBuilder?.getChild('checkbox')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -175,8 +175,6 @@ export interface NavSchema extends BaseSchema {
|
||||
*/
|
||||
source?: SchemaApi;
|
||||
|
||||
testid?: string;
|
||||
|
||||
/**
|
||||
* 懒加载 api,如果不配置复用 source 接口。
|
||||
*/
|
||||
@ -854,8 +852,8 @@ export class Navigation extends React.Component<
|
||||
render,
|
||||
popOverContainer,
|
||||
env,
|
||||
testid,
|
||||
searchable
|
||||
searchable,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const {dropIndicator, filteredLinks} = this.state;
|
||||
|
||||
@ -919,7 +917,7 @@ export class Navigation extends React.Component<
|
||||
isOpen={(item: NavigationItem) => !!item.open}
|
||||
stacked={!!stacked}
|
||||
mode={mode}
|
||||
testid={testid}
|
||||
testIdBuilder={testIdBuilder}
|
||||
themeColor={themeColor}
|
||||
onSelect={this.handleClick}
|
||||
onToggle={this.toggleLink}
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import {BaseSchema, SchemaClassName} from '../Schema';
|
||||
import {SearchBox} from 'amis-ui';
|
||||
|
||||
import type {ListenerAction} from 'amis-core';
|
||||
import {ListenerAction, TestIdBuilder} from 'amis-core';
|
||||
import type {SpinnerExtraProps} from 'amis-ui';
|
||||
|
||||
/**
|
||||
@ -82,6 +82,7 @@ interface SearchBoxProps
|
||||
name: string;
|
||||
onQuery?: (query: {[propName: string]: string}) => any;
|
||||
loading?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface SearchBoxState {
|
||||
@ -212,7 +213,8 @@ export class SearchBoxRenderer extends React.Component<
|
||||
mobileUI,
|
||||
loading,
|
||||
loadingConfig,
|
||||
onEvent
|
||||
onEvent,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const value = this.state.value;
|
||||
/** 有可能通过Search事件处理 */
|
||||
@ -241,6 +243,7 @@ export class SearchBoxRenderer extends React.Component<
|
||||
onFocus={() => this.dispatchEvent('focus')}
|
||||
onBlur={() => this.dispatchEvent('blur')}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ import {
|
||||
isVisible,
|
||||
qsstringify,
|
||||
createObject,
|
||||
extendObject
|
||||
extendObject,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {
|
||||
BaseSchema,
|
||||
@ -151,6 +152,7 @@ export interface ServiceProps
|
||||
Omit<ServiceSchema, 'type' | 'className'> {
|
||||
store: IServiceStore;
|
||||
messages: SchemaMessage;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
export default class Service extends React.Component<ServiceProps> {
|
||||
timer: ReturnType<typeof setTimeout>;
|
||||
@ -800,11 +802,16 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
classPrefix: ns,
|
||||
classnames: cx,
|
||||
loadingConfig,
|
||||
showErrorMsg
|
||||
showErrorMsg,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className={cx(`${ns}Service`, className)} style={style}>
|
||||
<div
|
||||
className={cx(`${ns}Service`, className)}
|
||||
style={style}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
{!env.forceSilenceInsideError &&
|
||||
store.error &&
|
||||
showErrorMsg !== false ? (
|
||||
|
@ -39,7 +39,8 @@ export function AutoFilterForm({
|
||||
onSearchableFromReset,
|
||||
onSearchableFromSubmit,
|
||||
onSearchableFromInit,
|
||||
popOverContainer
|
||||
popOverContainer,
|
||||
testIdBuilder
|
||||
}: AutoFilterFormProps) {
|
||||
const schema = React.useMemo(() => {
|
||||
const {columnsNum, showBtnToolbar} =
|
||||
@ -58,11 +59,13 @@ export function AutoFilterForm({
|
||||
? {
|
||||
type: 'input-text',
|
||||
name: column.name,
|
||||
label: column.label
|
||||
label: column.label,
|
||||
testIdBuilder: testIdBuilder.getChild(column.name)
|
||||
}
|
||||
: {
|
||||
type: 'input-text',
|
||||
name: column.name,
|
||||
testIdBuilder: testIdBuilder.getChild(column.name),
|
||||
...column.searchable
|
||||
}),
|
||||
name: column.searchable?.name ?? column.name,
|
||||
@ -98,6 +101,8 @@ export function AutoFilterForm({
|
||||
tpl: ''
|
||||
});
|
||||
}
|
||||
|
||||
const moreTestIdBuilder = testIdBuilder?.getChild('more');
|
||||
lastGroup.body.push({
|
||||
type: 'container',
|
||||
className: 'AutoFilterToolbar',
|
||||
@ -112,6 +117,7 @@ export function AutoFilterForm({
|
||||
size: 'sm',
|
||||
align: 'right',
|
||||
visible: showBtnToolbar,
|
||||
testIdBuilder: moreTestIdBuilder,
|
||||
buttons: searchableColumns.map(column => {
|
||||
return {
|
||||
children: ({render}: any) =>
|
||||
@ -124,6 +130,7 @@ export function AutoFilterForm({
|
||||
inputClassName: cx('Table-searchableForm-checkbox-inner'),
|
||||
name: `__whatever_name`,
|
||||
option: column.searchable?.label ?? column.label,
|
||||
testIdBuilder: moreTestIdBuilder?.getChild(column.name),
|
||||
badge: {
|
||||
offset: [-10, 5],
|
||||
visibleOn: `${
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
ThemeProps,
|
||||
resolveVariable,
|
||||
buildTrackExpression,
|
||||
evalTrackExpression
|
||||
evalTrackExpression,
|
||||
TestIdBuilder
|
||||
} from 'amis-core';
|
||||
import {BadgeObject, Checkbox, Icon, Spinner} from 'amis-ui';
|
||||
import React from 'react';
|
||||
@ -33,6 +34,7 @@ export interface CellProps extends ThemeProps {
|
||||
quickEditFormRef: any;
|
||||
onImageEnlarge?: any;
|
||||
translate: (key: string, ...args: Array<any>) => string;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export default function Cell({
|
||||
@ -53,7 +55,8 @@ export default function Cell({
|
||||
popOverContainer,
|
||||
quickEditFormRef,
|
||||
onImageEnlarge,
|
||||
translate: __
|
||||
translate: __,
|
||||
testIdBuilder
|
||||
}: CellProps) {
|
||||
if (column.name && item.rowSpans[column.name] === 0) {
|
||||
return null;
|
||||
@ -77,6 +80,7 @@ export default function Cell({
|
||||
<td
|
||||
style={style}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
{...testIdBuilder?.getTestId()}
|
||||
>
|
||||
<Checkbox
|
||||
classPrefix={ns}
|
||||
@ -85,6 +89,7 @@ export default function Cell({
|
||||
checked={item.checked || item.partial}
|
||||
disabled={item.checkdisable || !item.checkable}
|
||||
onChange={onCheckboxChange}
|
||||
testIdBuilder={testIdBuilder?.getChild('chekbx')}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
@ -95,6 +100,7 @@ export default function Cell({
|
||||
className={cx(column.pristine.className, stickyClassName, {
|
||||
'is-dragDisabled': !item.draggable
|
||||
})}
|
||||
{...testIdBuilder?.getChild('drag').getTestId()}
|
||||
>
|
||||
{item.draggable ? <Icon icon="drag" className="icon" /> : null}
|
||||
</td>
|
||||
@ -111,6 +117,9 @@ export default function Cell({
|
||||
// data-tooltip="展开/收起"
|
||||
// data-position="top"
|
||||
onClick={item.toggleExpanded}
|
||||
{...testIdBuilder
|
||||
?.getChild(item.expanded ? 'fold' : 'expand')
|
||||
.getTestId()}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</a>
|
||||
@ -142,6 +151,7 @@ export default function Cell({
|
||||
key="retryBtn"
|
||||
onClick={item.resetDefered}
|
||||
data-tooltip={__('Options.retry', {reason: item.error})}
|
||||
{...testIdBuilder?.getChild('retry').getTestId()}
|
||||
>
|
||||
<Icon icon="retry" className="icon" />
|
||||
</a>
|
||||
@ -152,6 +162,9 @@ export default function Cell({
|
||||
// data-tooltip="展开/收起"
|
||||
// data-position="top"
|
||||
onClick={item.toggleExpanded}
|
||||
{...testIdBuilder
|
||||
?.getChild(item.expanded ? 'fold' : 'expand')
|
||||
.getTestId()}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</a>
|
||||
@ -174,6 +187,7 @@ export default function Cell({
|
||||
draggable
|
||||
onDragStart={onDragStart}
|
||||
className={cx('Table-dragBtn')}
|
||||
{...testIdBuilder?.getChild('drag').getTestId()}
|
||||
>
|
||||
<Icon icon="drag" className="icon" />
|
||||
</a>
|
||||
@ -240,7 +254,8 @@ export default function Cell({
|
||||
column.pristine.className,
|
||||
stickyClassName,
|
||||
addtionalClassName
|
||||
)
|
||||
),
|
||||
testIdBuilder: testIdBuilder?.getChild(column.name || column.value)
|
||||
};
|
||||
delete subProps.label;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {ClassNamesFn, RendererEvent} from 'amis-core';
|
||||
import {ClassNamesFn, RendererEvent, autobind} from 'amis-core';
|
||||
|
||||
import {SchemaNode, ActionObject} from 'amis-core';
|
||||
import TableRow from './TableRow';
|
||||
@ -9,7 +9,7 @@ import {trace, reaction} from 'mobx';
|
||||
import {createObject, flattenTree} from 'amis-core';
|
||||
import {LocaleProps} from 'amis-core';
|
||||
import {ActionSchema} from '../Action';
|
||||
import type {IColumn, IRow, ITableStore} from 'amis-core';
|
||||
import type {IColumn, IRow, ITableStore, TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface TableBodyProps extends LocaleProps {
|
||||
store: ITableStore;
|
||||
@ -60,6 +60,7 @@ export interface TableBodyProps extends LocaleProps {
|
||||
prefixRow?: Array<any>;
|
||||
affixRow?: Array<any>;
|
||||
itemAction?: ActionSchema;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -68,10 +69,16 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
this.props.store.initTableWidth();
|
||||
}
|
||||
|
||||
@autobind
|
||||
testIdBuilder(rowPath: string) {
|
||||
return this.props.testIdBuilder?.getChild(`row-${rowPath}`);
|
||||
}
|
||||
|
||||
renderRows(
|
||||
rows: Array<any>,
|
||||
columns = this.props.columns,
|
||||
rowProps: any = {}
|
||||
rowProps: any = {},
|
||||
indexPath?: string
|
||||
): any {
|
||||
const {
|
||||
rowClassName,
|
||||
@ -97,15 +104,19 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
|
||||
return rows.map((item: IRow, rowIndex: number) => {
|
||||
const itemProps = buildItemProps ? buildItemProps(item, rowIndex) : null;
|
||||
const rowPath = `${indexPath ? indexPath + '/' : ''}${rowIndex}`;
|
||||
|
||||
const doms = [
|
||||
<TableRow
|
||||
{...itemProps}
|
||||
testIdBuilder={this.testIdBuilder}
|
||||
store={store}
|
||||
itemAction={itemAction}
|
||||
classnames={cx}
|
||||
checkOnItemClick={checkOnItemClick}
|
||||
key={item.id}
|
||||
itemIndex={rowIndex}
|
||||
rowPath={rowPath}
|
||||
item={item}
|
||||
itemClassName={cx(
|
||||
rowClassNameExpr
|
||||
@ -144,6 +155,7 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
checkOnItemClick={checkOnItemClick}
|
||||
key={`foot-${item.id}`}
|
||||
itemIndex={rowIndex}
|
||||
rowPath={rowPath}
|
||||
item={item}
|
||||
itemClassName={cx(
|
||||
rowClassNameExpr
|
||||
@ -164,16 +176,22 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
onQuickChange={onQuickChange}
|
||||
ignoreFootableContent={ignoreFootableContent}
|
||||
{...rowProps}
|
||||
testIdBuilder={this.testIdBuilder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (item.children.length && item.expanded) {
|
||||
// 嵌套表格
|
||||
doms.push(
|
||||
...this.renderRows(item.children, columns, {
|
||||
...rowProps,
|
||||
parent: item
|
||||
})
|
||||
...this.renderRows(
|
||||
item.children,
|
||||
columns,
|
||||
{
|
||||
...rowProps,
|
||||
parent: item
|
||||
},
|
||||
rowPath
|
||||
)
|
||||
);
|
||||
}
|
||||
return doms;
|
||||
|
@ -10,10 +10,13 @@ import {Badge} from 'amis-ui';
|
||||
import {ColorScale} from 'amis-core';
|
||||
import {isPureVariable, resolveVariableAndFilter} from 'amis-core';
|
||||
|
||||
import type {TestIdBuilder} from 'amis-core';
|
||||
|
||||
export interface TableCellProps extends RendererProps {
|
||||
wrapperComponent?: React.ElementType;
|
||||
column: any;
|
||||
contentsOnly?: boolean;
|
||||
testIdBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export class TableCell extends React.Component<TableCellProps> {
|
||||
@ -64,6 +67,7 @@ export class TableCell extends React.Component<TableCellProps> {
|
||||
row,
|
||||
showBadge,
|
||||
itemBadge,
|
||||
testIdBuilder,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -158,6 +162,7 @@ export class TableCell extends React.Component<TableCellProps> {
|
||||
className={cx(className)}
|
||||
tabIndex={tabIndex}
|
||||
onKeyUp={onKeyUp}
|
||||
{...testIdBuilder?.getChild('cell').getTestId()}
|
||||
>
|
||||
{showBadge ? (
|
||||
<Badge
|
||||
|
@ -15,13 +15,14 @@ import ItemActionsWrapper from './ItemActionsWrapper';
|
||||
import {SchemaTpl} from '../../Schema';
|
||||
import {Icon} from 'amis-ui';
|
||||
|
||||
import type {IColumn, IRow} from 'amis-core';
|
||||
import type {IColumn, IRow, TestIdBuilder} from 'amis-core';
|
||||
import ColGroup from './ColGroup';
|
||||
|
||||
export interface TableContentProps extends LocaleProps {
|
||||
className?: string;
|
||||
tableClassName?: string;
|
||||
classnames: ClassNamesFn;
|
||||
testIdBuilder: TestIdBuilder;
|
||||
columns: Array<IColumn>;
|
||||
columnsGroup: Array<{
|
||||
label: string;
|
||||
@ -173,7 +174,8 @@ export class TableContent extends React.PureComponent<TableContentProps> {
|
||||
store,
|
||||
dispatchEvent,
|
||||
onEvent,
|
||||
loading
|
||||
loading,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const tableClassName = cx('Table-table', this.props.tableClassName);
|
||||
@ -299,6 +301,7 @@ export class TableContent extends React.PureComponent<TableContentProps> {
|
||||
prefixRow={prefixRow}
|
||||
affixRow={affixRow}
|
||||
data={data}
|
||||
testIdBuilder={testIdBuilder}
|
||||
rowsProps={{
|
||||
dispatchEvent,
|
||||
onEvent
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
ITableStore,
|
||||
RendererEvent,
|
||||
RendererProps,
|
||||
TestIdBuilder,
|
||||
autobind,
|
||||
setVariable,
|
||||
traceProps
|
||||
@ -44,6 +45,8 @@ interface TableRowProps extends Pick<RendererProps, 'render'> {
|
||||
regionPrefix?: string;
|
||||
checkOnItemClick?: boolean;
|
||||
ignoreFootableContent?: boolean;
|
||||
testIdBuilder?: (key: string) => TestIdBuilder;
|
||||
rowPath: string; // 整体行的路径,树形时需要父行序号/当前展开层级下的行序号
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@ -198,7 +201,8 @@ export class TableRow extends React.PureComponent<
|
||||
checkdisable,
|
||||
trRef,
|
||||
isNested,
|
||||
|
||||
testIdBuilder,
|
||||
rowPath,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -261,6 +265,7 @@ export class TableRow extends React.PureComponent<
|
||||
width: null,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
rowPath,
|
||||
key: column.index,
|
||||
onAction: this.handleAction,
|
||||
onQuickChange: this.handleQuickChange,
|
||||
@ -314,6 +319,7 @@ export class TableRow extends React.PureComponent<
|
||||
},
|
||||
`Table-tr--${depth}th`
|
||||
)}
|
||||
{...testIdBuilder?.(rowPath).getTestId()}
|
||||
>
|
||||
{columns.map(column =>
|
||||
appeard ? (
|
||||
@ -321,6 +327,7 @@ export class TableRow extends React.PureComponent<
|
||||
...rest,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
rowPath,
|
||||
key: column.id,
|
||||
onAction: this.handleAction,
|
||||
onQuickChange: this.handleQuickChange,
|
||||
|
@ -41,8 +41,7 @@ import {
|
||||
resizeSensor,
|
||||
offset,
|
||||
getStyleNumber,
|
||||
getPropValue,
|
||||
buildTestId
|
||||
getPropValue
|
||||
} from 'amis-core';
|
||||
import {
|
||||
Button,
|
||||
@ -1677,7 +1676,8 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
translate: __,
|
||||
query,
|
||||
data,
|
||||
autoGenerateFilter
|
||||
autoGenerateFilter,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const searchableColumns = store.searchableColumns;
|
||||
@ -1697,6 +1697,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
onSearchableFromSubmit={onSearchableFromSubmit}
|
||||
onSearchableFromInit={onSearchableFromInit}
|
||||
popOverContainer={this.getPopOverContainer}
|
||||
testIdBuilder={testIdBuilder.getChild('filter')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -2096,7 +2097,8 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
classnames: cx,
|
||||
canAccessSuperData,
|
||||
itemBadge,
|
||||
translate
|
||||
translate,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -2120,6 +2122,9 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
quickEditFormRef={this.subFormRef}
|
||||
onImageEnlarge={this.handleImageEnlarge}
|
||||
translate={translate}
|
||||
testIdBuilder={testIdBuilder.getChild(
|
||||
`cell-${props.rowPath}-${column.index}`
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -2685,7 +2690,9 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
itemActions,
|
||||
dispatchEvent,
|
||||
onEvent,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
testIdBuilder,
|
||||
data
|
||||
} = this.props;
|
||||
|
||||
// 理论上来说 store.rows 应该也行啊
|
||||
@ -2701,6 +2708,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
itemActions
|
||||
})}
|
||||
<TableContent
|
||||
testIdBuilder={testIdBuilder}
|
||||
tableClassName={cx(
|
||||
{
|
||||
'Table-table--checkOnItemClick': checkOnItemClick,
|
||||
@ -2805,7 +2813,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
autoFillHeight,
|
||||
autoGenerateFilter,
|
||||
mobileUI,
|
||||
testid
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
|
||||
@ -2824,7 +2832,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
'Table--autoFillHeight': autoFillHeight
|
||||
})}
|
||||
style={store.buildStyles(style)}
|
||||
{...buildTestId(testid)}
|
||||
{...testIdBuilder.getTestId()}
|
||||
>
|
||||
{autoGenerateFilter ? this.renderAutoFilterForm() : null}
|
||||
{this.renderAffixHeader(tableClassName)}
|
||||
|
@ -8,8 +8,7 @@ import {
|
||||
RendererProps,
|
||||
resolveMappingObject,
|
||||
CustomStyle,
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {BaseSchema, SchemaObject} from '../Schema';
|
||||
|
||||
@ -300,7 +299,6 @@ export default class TableView extends React.Component<TableViewProps, object> {
|
||||
})
|
||||
)}
|
||||
style={{width: width, borderCollapse: 'collapse'}}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderCaption()}
|
||||
{this.renderCols()}
|
||||
|
@ -769,7 +769,8 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||
collapseBtnLabel,
|
||||
disabled,
|
||||
mobileUI,
|
||||
swipeable
|
||||
swipeable,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
|
||||
const mode = tabsMode || dMode;
|
||||
@ -815,6 +816,9 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||
: unmountOnExit
|
||||
}
|
||||
onSelect={this.handleSelect}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`tab-${typeof tab.title === 'string' ? tab.title : index}`
|
||||
)}
|
||||
>
|
||||
{render(
|
||||
`item/${index}`,
|
||||
@ -856,6 +860,9 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||
: unmountOnExit
|
||||
}
|
||||
onSelect={this.handleSelect}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`tab-${typeof tab.title === 'string' ? tab.title : index}`
|
||||
)}
|
||||
>
|
||||
{this.renderTab
|
||||
? this.renderTab(tab, this.props, index)
|
||||
@ -903,6 +910,7 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||
collapseOnExceed={collapseOnExceed}
|
||||
collapseBtnLabel={collapseBtnLabel}
|
||||
mobileUI={mobileUI}
|
||||
testIdBuilder={testIdBuilder}
|
||||
>
|
||||
{children}
|
||||
</CTabs>
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {filter, asyncFilter} from 'amis-core';
|
||||
import {filter, asyncFilter, TestIdBuilder} from 'amis-core';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import {anyChanged, getPropValue} from 'amis-core';
|
||||
import {escapeHtml} from 'amis-core';
|
||||
@ -52,6 +52,8 @@ export interface TplSchema extends BaseSchema {
|
||||
* 角标
|
||||
*/
|
||||
badge?: BadgeObject;
|
||||
|
||||
testidBuilder?: TestIdBuilder;
|
||||
}
|
||||
|
||||
export interface TplProps extends RendererProps, TplSchema {
|
||||
@ -200,7 +202,8 @@ export class Tpl extends React.Component<TplProps, TplState> {
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss
|
||||
themeCss,
|
||||
testIdBuilder
|
||||
} = this.props;
|
||||
const Component = wrapperComponent || (inline ? 'span' : 'div');
|
||||
const {content} = this.state;
|
||||
@ -236,6 +239,7 @@ export class Tpl extends React.Component<TplProps, TplState> {
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
{...testIdBuilder?.getChild('tpl')?.getTestId()}
|
||||
>
|
||||
<span
|
||||
className={cln ? cx(cln) : undefined}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {buildTestId, Renderer, RendererProps} from 'amis-core';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {BaseSchema, SchemaCollection} from '../Schema';
|
||||
import {resolveVariable} from 'amis-core';
|
||||
import {SchemaNode} from 'amis-core';
|
||||
@ -82,7 +82,6 @@ export default class Wrapper extends React.Component<WrapperProps, object> {
|
||||
className
|
||||
)}
|
||||
style={buildStyle(style, data)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderBody()}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user