feat: action select nav dialog input-text input-file 增加 testid (#9462)

This commit is contained in:
Allen 2024-01-17 19:39:48 +08:00 committed by GitHub
parent 59145cc2e8
commit 0c59ce4f12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 107 additions and 19 deletions

View File

@ -144,4 +144,4 @@
"printBasicPrototype": false
}
}
}
}

View File

@ -14,7 +14,7 @@ import {compile} from 'path-to-regexp';
import type {Schema, PlainObject, FunctionPropertyNames} from '../types';
import {evalExpression} from './tpl';
import {evalExpression, filter} from './tpl';
import {IIRendererStore} from '../store';
import {IFormStore} from '../store/form';
import {autobindMethod} from './autobind';
@ -2285,3 +2285,21 @@ export function replaceUrlParams(path: string, params: Record<string, any>) {
return path;
}
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 function getTestId(testid?: string, data?: PlainObject) {
if (!testid) {
return undefined;
}
return buildTestId(testid, data)[TEST_ID_KEY];
}

View File

@ -1,4 +1,4 @@
import {Html, render, TooltipWrapper, hasIcon} from 'amis';
import {Html, render, TooltipWrapper, buildTestId} from 'amis';
import {observer} from 'mobx-react';
import React from 'react';
import cx from 'classnames';
@ -173,6 +173,7 @@ export default class RenderersPanel extends React.Component<
{items.map((item: any) => {
const key = `${index}_${item.id}`;
const usePluginIcon = isHasPluginIcon(item);
const testid = `editor-renderer-${item.plugin.rendererName}`;
return (
<div
@ -188,6 +189,7 @@ export default class RenderersPanel extends React.Component<
onDragStart={(e: React.DragEvent) =>
this.handleDragStart(e, item.name)
}
{...buildTestId(testid)}
>
<div
className="icon-box"

View File

@ -6,7 +6,7 @@
import React from 'react';
import {mapTree} from 'amis-core';
import {ClassNamesFn, themeable} from 'amis-core';
import {ClassNamesFn, themeable, buildTestId} from 'amis-core';
export type LinkItem = LinkItemProps;
interface LinkItemProps {
@ -19,6 +19,7 @@ interface LinkItemProps {
children?: Array<LinkItem>;
path?: string;
icon?: string;
testid?: string;
component?: React.ElementType;
}
@ -54,7 +55,9 @@ interface AsideNavState {
export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
static defaultProps = {
renderLink: (item: LinkItemProps) => <a>{item.label}</a>,
renderLink: (item: LinkItemProps) => (
<a {...buildTestId(item.testid, item)}>{item.label}</a>
),
renderSubLinks: (
link: LinkItemProps,
renderLink: Function,

View File

@ -6,7 +6,7 @@
import React from 'react';
import TooltipWrapper, {TooltipObject, Trigger} from './TooltipWrapper';
import {pickEventsProps} from 'amis-core';
import {ClassNamesFn, themeable} from 'amis-core';
import {ClassNamesFn, themeable, buildTestId} from 'amis-core';
import Spinner, {SpinnerExtraProps} from './Spinner';
export interface ButtonProps
@ -14,6 +14,7 @@ export interface ButtonProps
SpinnerExtraProps {
id?: string;
className?: string;
testid?: string;
style?: any;
href?: string;
title?: string;
@ -77,6 +78,7 @@ export class Button extends React.Component<ButtonProps> {
loadingClassName,
overrideClassName,
loadingConfig,
testid,
...rest
} = this.props;
@ -92,6 +94,7 @@ export class Button extends React.Component<ButtonProps> {
{...pickEventsProps(rest)}
onClick={rest.onClick && disabled ? () => {} : rest.onClick}
href={href}
{...buildTestId(testid)}
className={cx(
overrideClassName
? ''

View File

@ -9,7 +9,8 @@ import {
getOptionValue,
getOptionValueBindField,
labelToString,
uncontrollable
uncontrollable,
buildTestId
} from 'amis-core';
import React from 'react';
import isInteger from 'lodash/isInteger';
@ -322,6 +323,7 @@ export interface SelectProps
LocaleProps,
SpinnerExtraProps {
className?: string;
testid?: string;
popoverClassName?: string;
showInvalidMatch?: boolean;
creatable: boolean;
@ -1319,6 +1321,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
borderMode,
mobileUI,
hasError,
testid,
loadingConfig
} = this.props;
@ -1350,6 +1353,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
onClick={this.toggle}
onFocus={this.onFocus}
onBlur={this.onBlur}
{...buildTestId(testid)}
className={cx(
`Select`,
{

View File

@ -7,7 +7,7 @@
import React from 'react';
import pick from 'lodash/pick';
import {Item as RcItem, MenuItemProps as RcMenuItemProps} from 'rc-menu';
import {ClassNamesFn, themeable, createObject} from 'amis-core';
import {ClassNamesFn, themeable, createObject, buildTestId} from 'amis-core';
import {Badge} from '../Badge';
import {getIcon} from '../icons';
@ -29,6 +29,7 @@ export interface MenuItemProps
tooltipContainer?: HTMLElement | (() => HTMLElement);
tooltipTrigger?: Trigger | Array<Trigger>;
renderLink: Function;
testid?: string;
extra?: React.ReactNode;
}
@ -90,6 +91,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
renderLink,
extra,
disabled,
testid,
id,
data: defaultData
} = this.props;
@ -166,6 +168,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
data-id={link?.__id || id}
data-depth={depth}
onDragStart={onDragStart?.(link)}
{...buildTestId(testid, link)}
>
{isCollapsedNode ? (
<>{iconNode || labelNode}</>

View File

@ -7,7 +7,14 @@
import React from 'react';
import pick from 'lodash/pick';
import {SubMenu as RcSubMenu, SubMenuProps as RcSubMenuProps} from 'rc-menu';
import {ClassNamesFn, themeable, autobind, createObject} from 'amis-core';
import {
ClassNamesFn,
themeable,
autobind,
createObject,
filter,
buildTestId
} from 'amis-core';
import {getIcon, Icon} from '../icons';
import {Badge} from '../Badge';
@ -104,6 +111,7 @@ export class SubMenu extends React.Component<SubMenuProps> {
disabled,
data: defaultData,
extra,
testid,
renderLink
} = this.props;
const isCollapsedNode = collapsed && depth === 1;
@ -197,6 +205,7 @@ export class SubMenu extends React.Component<SubMenuProps> {
data-id={link?.__id || id}
data-depth={depth}
onDragStart={onDragStart?.(link)}
{...buildTestId(testid, link)}
>
{renderContent()}
</a>

View File

@ -63,6 +63,8 @@ export interface MenuProps extends Omit<RcMenuProps, 'mode'> {
*/
navigations: Array<NavigationItem>;
testid?: string;
/**
* stacked为true垂直 false
*/
@ -577,7 +579,8 @@ export class Menu extends React.Component<MenuProps, MenuState> {
collapsed,
overflowedIndicator,
overflowMaxCount,
popupClassName
popupClassName,
testid
} = this.props;
return list.map((item: NavigationItem, index: number) => {
@ -613,6 +616,7 @@ export class Menu extends React.Component<MenuProps, MenuState> {
badge={badge}
renderLink={renderLink}
depth={level || 1}
testid={testid}
popupClassName={popupClassName}
>
{this.renderMenuContent(item.children || [], item.depth + 1)}
@ -634,6 +638,7 @@ export class Menu extends React.Component<MenuProps, MenuState> {
renderLink={renderLink}
badge={badge}
data={data}
testid={testid}
depth={level || 1}
order={index}
overflowedIndicator={overflowedIndicator}

View File

@ -10,7 +10,8 @@ import {
RendererProps,
ScopedContext,
uuid,
setThemeClassName
setThemeClassName,
getTestId
} from 'amis-core';
import {filter} from 'amis-core';
import {BadgeObject, Button, SpinnerExtraProps} from 'amis-ui';
@ -23,6 +24,8 @@ export interface ButtonSchema extends BaseSchema {
*/
id?: string;
testid?: string;
/**
*
*/
@ -736,6 +739,7 @@ export class Action extends React.Component<ActionProps, ActionState> {
wrapperCustomStyle,
css,
id,
testid,
env
} = this.props;
@ -815,6 +819,7 @@ export class Action extends React.Component<ActionProps, ActionState> {
[activeClassName || 'is-active']: isActive
}
)}
testid={getTestId(testid, data)}
style={style}
size={size}
level={

View File

@ -7,7 +7,8 @@ import {
resolveVariableAndFilter,
setVariable,
setThemeClassName,
ValidateError
ValidateError,
getTestId
} from 'amis-core';
import {Renderer, RendererProps} from 'amis-core';
import {SchemaNode, Schema, ActionObject} from 'amis-core';
@ -48,6 +49,8 @@ export interface DialogSchema extends BaseSchema {
*/
actions?: Array<ActionSchema>;
testid?: string;
/**
*
*/
@ -231,7 +234,7 @@ export default class Dialog extends React.Component<DialogProps> {
}
buildActions(): Array<ActionSchema> {
const {actions, confirm, translate: __} = this.props;
const {actions, confirm, testid, translate: __} = this.props;
if (typeof actions !== 'undefined') {
return actions;
@ -240,6 +243,7 @@ export default class Dialog extends React.Component<DialogProps> {
let ret: Array<ActionSchema> = [];
ret.push({
type: 'button',
testid: getTestId(testid && `${testid}-cancel`),
actionType: 'cancel',
label: __('cancel')
});
@ -247,6 +251,7 @@ export default class Dialog extends React.Component<DialogProps> {
if (confirm) {
ret.push({
type: 'button',
testid: getTestId(testid && `${testid}-confirm`),
actionType: 'confirm',
label: __('confirm'),
primary: true

View File

@ -6,7 +6,8 @@ import {
isPureVariable,
resolveVariableAndFilter,
setThemeClassName,
ValidateError
ValidateError,
getTestId
} from 'amis-core';
import {Renderer, RendererProps} from 'amis-core';
import {SchemaNode, Schema, ActionObject} from 'amis-core';
@ -142,6 +143,8 @@ export interface DrawerSchema extends BaseSchema {
*
*/
showErrorMsg?: boolean;
testid?: string;
}
export type DrawerSchemaBase = Omit<DrawerSchema, 'type'>;
@ -257,7 +260,7 @@ export default class Drawer extends React.Component<DrawerProps> {
}
buildActions(): Array<ActionSchema> {
const {actions, confirm, translate: __} = this.props;
const {actions, confirm, testid, translate: __} = this.props;
if (typeof actions !== 'undefined') {
return actions;
@ -266,6 +269,7 @@ export default class Drawer extends React.Component<DrawerProps> {
let ret: Array<ActionSchema> = [];
ret.push({
type: 'button',
testid: getTestId(testid && `${testid}-cancel`),
actionType: 'close',
label: __('cancel')
});
@ -274,6 +278,7 @@ export default class Drawer extends React.Component<DrawerProps> {
ret.push({
type: 'button',
actionType: 'confirm',
testid: getTestId(testid && `${testid}-confirm`),
label: __('confirm'),
primary: true
});

View File

@ -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} from 'amis-core';
import {filter, buildTestId, getTestId} from 'amis-core';
import {Icon, hasIcon} from 'amis-ui';
import {
BaseSchema,
@ -52,6 +52,8 @@ export interface DropdownButtonSchema extends BaseSchema {
*/
buttons?: Array<DropdownButton>;
testid?: string;
/**
*
*/
@ -249,7 +251,7 @@ export default class DropDownButton extends React.Component<
button: DropdownButton,
index: number | string
): React.ReactNode {
const {render, classnames: cx, data, ignoreConfirm} = this.props;
const {render, classnames: cx, data, testid, ignoreConfirm} = this.props;
index = typeof index === 'number' ? index.toString() : index;
if (typeof button !== 'string' && Array.isArray(button?.children)) {
@ -296,6 +298,9 @@ export default class DropDownButton extends React.Component<
`button/${index}`,
{
type: 'button',
testid:
testid &&
`${getTestId(testid, data)}-${button.testid || index}`,
...(button as any),
className: ''
},
@ -417,6 +422,7 @@ export default class DropDownButton extends React.Component<
trigger,
data,
hideCaret,
testid,
env
} = this.props;
@ -434,6 +440,7 @@ 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}

View File

@ -3,7 +3,8 @@ import {
FormItem,
FormControlProps,
prettyBytes,
resolveEventData
resolveEventData,
buildTestId
} from 'amis-core';
import find from 'lodash/find';
import isPlainObject from 'lodash/isPlainObject';
@ -246,6 +247,8 @@ export interface FileControlSchema extends FormBaseControlSchema {
*
*/
drag?: boolean;
testid?: string;
}
export interface FileProps
@ -1355,6 +1358,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
data,
documentation,
documentLink,
testid,
env,
container
} = this.props;
@ -1415,6 +1419,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
disabled={disabled}
{...getInputProps()}
capture={capture as any}
{...buildTestId(testid && `${testid}-input`)}
/>
{drag || isDragActive ? (
@ -1453,6 +1458,7 @@ 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})

View File

@ -19,7 +19,9 @@ import {
createObject,
setVariable,
ucFirst,
isEffectiveApi
isEffectiveApi,
getTestId,
buildTestId
} from 'amis-core';
import {Icon, SpinnerExtraProps, Input, Spinner, OverflowTpl} from 'amis-ui';
import {ActionSchema} from '../Action';
@ -117,6 +119,8 @@ export interface TextControlSchema extends FormOptionsSchema {
/** 在内容为空的时候清除值 */
clearValueOnEmpty?: boolean;
testid?: string;
}
export type InputTextRendererEvent =
@ -721,6 +725,7 @@ export default class TextControl extends React.PureComponent<
themeCss,
css,
id,
testid,
nativeAutoComplete
} = this.props;
let type = this.props.type?.replace(/^(?:native|input)\-/, '');
@ -794,6 +799,7 @@ export default class TextControl extends React.PureComponent<
}
)}
onClick={this.handleClick}
{...buildTestId(testid, data)}
>
<>
{filteredPlaceholder &&
@ -971,6 +977,7 @@ export default class TextControl extends React.PureComponent<
themeCss,
css,
id,
testid,
nativeAutoComplete
} = this.props;
@ -993,6 +1000,7 @@ export default class TextControl extends React.PureComponent<
inputControlClassName,
inputOnly ? className : ''
)}
{...buildTestId(testid, data)}
>
{prefix ? (
<span className={cx('TextControl-inputPrefix')}>
@ -1020,6 +1028,7 @@ export default class TextControl extends React.PureComponent<
className={cx(nativeInputClassName, {
'TextControl-input-password': type === 'password' && revealPassword
})}
{...buildTestId(testid && `${testid}-input`)}
/>
{clearable && !disabled && !readOnly && value ? (
<a onClick={this.clearValue} className={cx('TextControl-clear')}>

View File

@ -175,6 +175,8 @@ export interface NavSchema extends BaseSchema {
*/
source?: SchemaApi;
testid?: string;
/**
* api source
*/
@ -852,6 +854,7 @@ export class Navigation extends React.Component<
render,
popOverContainer,
env,
testid,
searchable
} = this.props;
const {dropIndicator, filteredLinks} = this.state;
@ -916,6 +919,7 @@ export class Navigation extends React.Component<
isOpen={(item: NavigationItem) => !!item.open}
stacked={!!stacked}
mode={mode}
testid={testid}
themeColor={themeColor}
onSelect={this.handleClick}
onToggle={this.toggleLink}