mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: InputText多选模式下的tag自适应展示tooltip(OverflowTpl) (#9055)
This commit is contained in:
parent
4ccb9d8bf6
commit
0364aa57a4
@ -230,7 +230,7 @@ order: 56
|
||||
"value": "a"
|
||||
},
|
||||
{
|
||||
"label": "OptionB",
|
||||
"label": "OptionB (with long suffix for testing ellipsis)",
|
||||
"value": "b"
|
||||
},
|
||||
{
|
||||
|
@ -46,11 +46,11 @@
|
||||
"qs": "6.9.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@babel/generator": "^7.22.9",
|
||||
"@babel/parser": "^7.22.7",
|
||||
"@babel/traverse": "^7.22.8",
|
||||
"@babel/types": "^7.22.5",
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@rollup/plugin-replace": "^5.0.1",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/jest": "^28.1.0",
|
||||
@ -143,4 +143,4 @@
|
||||
"printBasicPrototype": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ Object.defineProperty(window, 'DragEvent', {
|
||||
value: class DragEvent {}
|
||||
});
|
||||
|
||||
// Mock ResizeObserver in jest env
|
||||
global.ResizeObserver = require('resize-observer-polyfill');
|
||||
|
||||
global.__buildVersion = '';
|
||||
|
||||
global.beforeAll(() => {
|
||||
|
@ -25,6 +25,7 @@
|
||||
"moment-timezone": "^0.5.34",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
|
@ -18,6 +18,11 @@ const pages: TreeArray = [
|
||||
label: '按钮',
|
||||
path: '/basic/button',
|
||||
component: React.lazy(() => import('./basic/Button'))
|
||||
},
|
||||
{
|
||||
label: '文字提示',
|
||||
path: '/basic/overflow-tpl',
|
||||
component: React.lazy(() => import('./basic/OverflowTpl'))
|
||||
}
|
||||
]
|
||||
},
|
||||
|
29
packages/amis-ui/examples/basic/OverflowTpl.tsx
Normal file
29
packages/amis-ui/examples/basic/OverflowTpl.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import {OverflowTpl} from 'amis-ui';
|
||||
|
||||
export default function ButtonExamples() {
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<div className="flex justify-items-start items-center">
|
||||
{[
|
||||
{text: 'Transforming business'},
|
||||
{
|
||||
text: "Innovating, creating, succeeding. Let's make a difference together."
|
||||
},
|
||||
{text: 'Bringing technology to the forefront'},
|
||||
{text: 'Driving change in the industry'},
|
||||
{text: 'Enriching the journey with'}
|
||||
].map((item, index) => (
|
||||
<div
|
||||
className="text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-blue-200 text-blue-700 rounded-full"
|
||||
style={{maxWidth: '190px'}}
|
||||
>
|
||||
<OverflowTpl key={index} tooltip={item.text}>
|
||||
{item.text}
|
||||
</OverflowTpl>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
5
packages/amis-ui/scss/components/_overflow-tpl.scss
Normal file
5
packages/amis-ui/scss/components/_overflow-tpl.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.#{$ns}OverflowTpl {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
@ -141,3 +141,4 @@
|
||||
|
||||
@import '../components/debug';
|
||||
@import '../components/menu';
|
||||
@import '../components/overflow-tpl';
|
||||
|
@ -1046,6 +1046,7 @@ export class DateRangePicker extends React.Component<
|
||||
) {
|
||||
newState.editState = 'end';
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
|
181
packages/amis-ui/src/components/OverflowTpl.tsx
Normal file
181
packages/amis-ui/src/components/OverflowTpl.tsx
Normal file
@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @file OverflowTpl
|
||||
* @desc 文字提示组件,会基于内部文字是否触发ellipsis,动态展示tooltip提示, 默认使用子节点文本作为tooltip内容
|
||||
*/
|
||||
|
||||
import React, {useState, useEffect, useRef, useCallback} from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import omit from 'lodash/omit';
|
||||
import {themeable, isObject} from 'amis-core';
|
||||
import TooltipWrapper from './TooltipWrapper';
|
||||
|
||||
import type {ThemeProps} from 'amis-core';
|
||||
import type {TooltipWrapperProps} from './TooltipWrapper';
|
||||
|
||||
export interface OverflowTplProps extends ThemeProps {
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* tooltip相关配置
|
||||
*/
|
||||
tooltip?: TooltipWrapperProps['tooltip'];
|
||||
|
||||
/**
|
||||
* 是否使用行内标签,默认为 true,使用 span 标签,否则使用 div 标签
|
||||
*/
|
||||
inline?: boolean;
|
||||
|
||||
/**
|
||||
* 目标元素的selector,通常不需要指定
|
||||
*/
|
||||
targetSelector?: string;
|
||||
|
||||
/**
|
||||
* 内部节点
|
||||
*/
|
||||
children?: React.ReactNode | React.ReactNode[];
|
||||
}
|
||||
|
||||
const OverflowTpl: React.FC<OverflowTplProps> = props => {
|
||||
const {
|
||||
classnames: cx,
|
||||
children,
|
||||
className,
|
||||
targetSelector,
|
||||
tooltip,
|
||||
inline = true
|
||||
} = props;
|
||||
const [ellipsisAvtived, setEllipsisAvtived] = useState(false);
|
||||
const [innerText, setInnerText] = useState('');
|
||||
const innerRef = useRef<Element | React.ReactInstance>(null);
|
||||
const defaultTooltip = tooltip
|
||||
? typeof tooltip === 'string'
|
||||
? {content: tooltip}
|
||||
: isObject(tooltip)
|
||||
? tooltip
|
||||
: undefined
|
||||
: typeof children === 'string'
|
||||
? {content: children}
|
||||
: undefined; /** 默认使用子节点文本 */
|
||||
const normalizedTooltip =
|
||||
innerText && (!defaultTooltip || defaultTooltip?.content == null)
|
||||
? {...defaultTooltip, content: innerText}
|
||||
: defaultTooltip;
|
||||
const updateEllipsisActivation = useCallback(
|
||||
(el?: Element) => {
|
||||
setEllipsisAvtived(
|
||||
el
|
||||
? el.scrollWidth > el.clientWidth || el.scrollHeight > el.scrollHeight
|
||||
: false
|
||||
);
|
||||
},
|
||||
[innerRef.current]
|
||||
);
|
||||
const onMutation = useCallback(
|
||||
(mutations: MutationRecord[]) => {
|
||||
const dom = (
|
||||
targetSelector
|
||||
? document.querySelector(targetSelector)
|
||||
: mutations?.[0].target
|
||||
) as Element | undefined;
|
||||
|
||||
updateEllipsisActivation(dom);
|
||||
if (
|
||||
dom?.textContent &&
|
||||
typeof dom.textContent === 'string' &&
|
||||
(!defaultTooltip || defaultTooltip?.content == null)
|
||||
) {
|
||||
setInnerText(dom.textContent);
|
||||
}
|
||||
},
|
||||
[targetSelector]
|
||||
);
|
||||
const onResize = useCallback(
|
||||
(entries: ResizeObserverEntry[]) => {
|
||||
const dom = (
|
||||
targetSelector
|
||||
? document.querySelector(targetSelector)
|
||||
: entries?.[0].target
|
||||
) as Element | undefined;
|
||||
|
||||
updateEllipsisActivation(dom);
|
||||
if (
|
||||
dom?.textContent &&
|
||||
typeof dom.textContent === 'string' &&
|
||||
(!defaultTooltip || !defaultTooltip?.content == null)
|
||||
) {
|
||||
setInnerText(dom.textContent);
|
||||
}
|
||||
},
|
||||
[targetSelector]
|
||||
);
|
||||
|
||||
/** 监听目标元素的DOM变化 */
|
||||
useEffect(() => {
|
||||
const element =
|
||||
innerRef.current instanceof React.Component
|
||||
? (findDOMNode(innerRef.current) as Element)
|
||||
: innerRef.current;
|
||||
|
||||
if (element) {
|
||||
const observer = new MutationObserver(onMutation);
|
||||
observer.observe(element, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterDataOldValue: true,
|
||||
characterData: true
|
||||
});
|
||||
return () => observer.disconnect();
|
||||
}
|
||||
|
||||
return;
|
||||
}, [innerRef.current]);
|
||||
|
||||
/** 监听目标元素的尺寸变化 */
|
||||
useEffect(() => {
|
||||
const element =
|
||||
innerRef.current instanceof React.Component
|
||||
? (findDOMNode(innerRef.current) as Element)
|
||||
: innerRef.current;
|
||||
|
||||
if (element) {
|
||||
const observer = new ResizeObserver(onResize);
|
||||
observer.observe(element);
|
||||
return () => observer.disconnect();
|
||||
}
|
||||
|
||||
return;
|
||||
}, [innerRef.current]);
|
||||
|
||||
const WrapComponent = inline !== false ? 'span' : 'div';
|
||||
const showTooltip = ellipsisAvtived && normalizedTooltip;
|
||||
const Body = React.isValidElement(children) ? (
|
||||
React.cloneElement(children as React.ReactElement, {ref: innerRef})
|
||||
) : (
|
||||
<WrapComponent
|
||||
ref={innerRef as React.RefObject<HTMLDivElement>}
|
||||
className={cx('OverflowTpl', className, {
|
||||
'OverflowTpl--with-tooltip': showTooltip
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</WrapComponent>
|
||||
);
|
||||
|
||||
return showTooltip ? (
|
||||
<TooltipWrapper
|
||||
{...omit(props, ['className', 'inline', 'targetSelector', 'children'])}
|
||||
tooltip={normalizedTooltip}
|
||||
>
|
||||
{Body}
|
||||
</TooltipWrapper>
|
||||
) : (
|
||||
Body
|
||||
);
|
||||
};
|
||||
|
||||
OverflowTpl.defaultProps = {
|
||||
inline: true
|
||||
};
|
||||
|
||||
export default themeable(OverflowTpl);
|
@ -129,6 +129,7 @@ import Menu from './menu';
|
||||
import InputBoxWithSuggestion from './InputBoxWithSuggestion';
|
||||
import {CodeMirrorEditor} from './CodeMirror';
|
||||
import type CodeMirror from 'codemirror';
|
||||
import OverflowTpl from './OverflowTpl';
|
||||
|
||||
export {
|
||||
NotFound,
|
||||
@ -261,5 +262,6 @@ export {
|
||||
DndContainer,
|
||||
Menu,
|
||||
CodeMirror,
|
||||
CodeMirrorEditor
|
||||
CodeMirrorEditor,
|
||||
OverflowTpl
|
||||
};
|
||||
|
@ -1493,7 +1493,7 @@ exports[`Renderer:text with options 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:text with options and multiple and delimiter: first option selected 1`] = `
|
||||
exports[`Renderer:text with options and multiple Renderer:text with options and multiple and delimiter: first option selected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Panel cxd-Panel--default cxd-Panel--form"
|
||||
@ -1556,7 +1556,7 @@ exports[`Renderer:text with options and multiple and delimiter: first option sel
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
OptionA
|
||||
</span>
|
||||
@ -1738,7 +1738,7 @@ exports[`Renderer:text with options and multiple and delimiter: first option sel
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:text with options and multiple and delimiter: options is opened 1`] = `
|
||||
exports[`Renderer:text with options and multiple Renderer:text with options and multiple and delimiter: options is opened 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Panel cxd-Panel--default cxd-Panel--form"
|
||||
@ -1984,7 +1984,7 @@ exports[`Renderer:text with options and multiple and delimiter: options is opene
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:text with options and multiple and delimiter: options is opened again, and first option already selected 1`] = `
|
||||
exports[`Renderer:text with options and multiple Renderer:text with options and multiple and delimiter: options is opened again, and first option already selected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Panel cxd-Panel--default cxd-Panel--form"
|
||||
@ -2047,7 +2047,7 @@ exports[`Renderer:text with options and multiple and delimiter: options is opene
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
OptionA
|
||||
</span>
|
||||
@ -2229,7 +2229,7 @@ exports[`Renderer:text with options and multiple and delimiter: options is opene
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:text with options and multiple and delimiter: second option selected 1`] = `
|
||||
exports[`Renderer:text with options and multiple Renderer:text with options and multiple and delimiter: second option selected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Panel cxd-Panel--default cxd-Panel--form"
|
||||
@ -2292,7 +2292,7 @@ exports[`Renderer:text with options and multiple and delimiter: second option se
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
OptionA
|
||||
</span>
|
||||
@ -2305,7 +2305,7 @@ exports[`Renderer:text with options and multiple and delimiter: second option se
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
OptionB
|
||||
</span>
|
||||
@ -2477,7 +2477,7 @@ exports[`Renderer:text with options and multiple and delimiter: second option se
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:text with options and multiple and delimiter: thrid option create 1`] = `
|
||||
exports[`Renderer:text with options and multiple Renderer:text with options and multiple and delimiter: thrid option create 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Panel cxd-Panel--default cxd-Panel--form"
|
||||
@ -2538,7 +2538,7 @@ exports[`Renderer:text with options and multiple and delimiter: thrid option cre
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
OptionA
|
||||
</span>
|
||||
@ -2551,7 +2551,7 @@ exports[`Renderer:text with options and multiple and delimiter: thrid option cre
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
OptionB
|
||||
</span>
|
||||
@ -2564,7 +2564,7 @@ exports[`Renderer:text with options and multiple and delimiter: thrid option cre
|
||||
class="cxd-TextControl-value"
|
||||
>
|
||||
<span
|
||||
class="cxd-TextControl-valueLabel"
|
||||
class="cxd-OverflowTpl cxd-TextControl-valueLabel"
|
||||
>
|
||||
AbCd
|
||||
</span>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React = require('react');
|
||||
import {render, cleanup, fireEvent, waitFor} from '@testing-library/react';
|
||||
import {render, cleanup, fireEvent, waitFor, screen} from '@testing-library/react';
|
||||
import '../../../src';
|
||||
import {render as amisRender} from '../../../src';
|
||||
import {makeEnv, replaceReactAriaIds, wait} from '../../helper';
|
||||
@ -248,106 +248,153 @@ test('Renderer:text with options', async () => {
|
||||
expect(container).toMatchSnapshot('select first option');
|
||||
});
|
||||
|
||||
/**
|
||||
* 选择器模式,多选、分隔符、提取值
|
||||
*/
|
||||
test('Renderer:text with options and multiple and delimiter', async () => {
|
||||
const {container, input} = await setup(
|
||||
{
|
||||
multiple: true,
|
||||
options: [
|
||||
{
|
||||
label: 'OptionA',
|
||||
value: 'a'
|
||||
},
|
||||
{
|
||||
label: 'OptionB',
|
||||
value: 'b'
|
||||
},
|
||||
{
|
||||
label: 'OptionC',
|
||||
value: 'c'
|
||||
},
|
||||
{
|
||||
label: 'OptionD',
|
||||
value: 'd'
|
||||
}
|
||||
],
|
||||
delimiter: '-',
|
||||
joinValues: true,
|
||||
creatable: true
|
||||
},
|
||||
{},
|
||||
[
|
||||
describe('Renderer:text with options and multiple', () => {
|
||||
/**
|
||||
* 选择器模式,多选、分隔符、提取值
|
||||
*/
|
||||
test('Renderer:text with options and multiple and delimiter', async () => {
|
||||
const {container, input} = await setup(
|
||||
{
|
||||
type: 'static',
|
||||
name: 'text'
|
||||
}
|
||||
]
|
||||
);
|
||||
multiple: true,
|
||||
options: [
|
||||
{
|
||||
label: 'OptionA',
|
||||
value: 'a'
|
||||
},
|
||||
{
|
||||
label: 'OptionB',
|
||||
value: 'b'
|
||||
},
|
||||
{
|
||||
label: 'OptionC',
|
||||
value: 'c'
|
||||
},
|
||||
{
|
||||
label: 'OptionD',
|
||||
value: 'd'
|
||||
}
|
||||
],
|
||||
delimiter: '-',
|
||||
joinValues: true,
|
||||
creatable: true
|
||||
},
|
||||
{},
|
||||
[
|
||||
{
|
||||
type: 'static',
|
||||
name: 'text'
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
const textControl = container.querySelector(
|
||||
'.cxd-TextControl-input'
|
||||
) as HTMLElement;
|
||||
const textControl = container.querySelector(
|
||||
'.cxd-TextControl-input'
|
||||
) as HTMLElement;
|
||||
|
||||
// 展开 options
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
// 展开 options
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('options is opened');
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('options is opened');
|
||||
|
||||
// 选中第一项
|
||||
fireEvent.click(
|
||||
container.querySelector(
|
||||
'.cxd-TextControl-sugs .cxd-TextControl-sugItem'
|
||||
) as HTMLElement
|
||||
);
|
||||
await wait(300);
|
||||
// expect(input.value).toBe('a');
|
||||
// 选中第一项
|
||||
fireEvent.click(
|
||||
container.querySelector(
|
||||
'.cxd-TextControl-sugs .cxd-TextControl-sugItem'
|
||||
) as HTMLElement
|
||||
);
|
||||
await wait(300);
|
||||
// expect(input.value).toBe('a');
|
||||
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('first option selected');
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('first option selected');
|
||||
|
||||
// 再次打开 options
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
// 再次打开 options
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot(
|
||||
'options is opened again, and first option already selected'
|
||||
);
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot(
|
||||
'options is opened again, and first option already selected'
|
||||
);
|
||||
|
||||
// 选中 options 中的第一项
|
||||
fireEvent.click(
|
||||
container.querySelector(
|
||||
'.cxd-TextControl-sugs .cxd-TextControl-sugItem'
|
||||
) as HTMLElement
|
||||
);
|
||||
await wait(300);
|
||||
// 选中 options 中的第一项
|
||||
fireEvent.click(
|
||||
container.querySelector(
|
||||
'.cxd-TextControl-sugs .cxd-TextControl-sugItem'
|
||||
) as HTMLElement
|
||||
);
|
||||
await wait(300);
|
||||
|
||||
// 分隔符
|
||||
expect(
|
||||
(container.querySelector('.cxd-PlainField') as Element).innerHTML
|
||||
).toBe('a-b');
|
||||
// 分隔符
|
||||
expect(
|
||||
(container.querySelector('.cxd-PlainField') as Element).innerHTML
|
||||
).toBe('a-b');
|
||||
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('second option selected');
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('second option selected');
|
||||
|
||||
// 可创建
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
fireEvent.change(input, {target: {value: 'AbCd'}});
|
||||
await wait(500);
|
||||
fireEvent.keyDown(input, {key: 'Enter', code: 13});
|
||||
await wait(500);
|
||||
// 可创建
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
fireEvent.change(input, {target: {value: 'AbCd'}});
|
||||
await wait(500);
|
||||
fireEvent.keyDown(input, {key: 'Enter', code: 13});
|
||||
await wait(500);
|
||||
|
||||
expect(
|
||||
(container.querySelector('.cxd-PlainField') as Element).innerHTML
|
||||
).toBe('a-b-AbCd');
|
||||
expect(
|
||||
(container.querySelector('.cxd-PlainField') as Element).innerHTML
|
||||
).toBe('a-b-AbCd');
|
||||
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('thrid option create');
|
||||
replaceReactAriaIds(container);
|
||||
expect(container).toMatchSnapshot('thrid option create');
|
||||
});
|
||||
|
||||
test('Renderer:text with options auto ellipsis', async () => {
|
||||
const longText = 'OptionB (with long suffix for testing ellipsis)';
|
||||
const {container} = await setup({
|
||||
"name": "text",
|
||||
"type": "input-text",
|
||||
"label": "text",
|
||||
"multiple": true,
|
||||
"options": [
|
||||
{
|
||||
"label": "OptionA",
|
||||
"value": "a"
|
||||
},
|
||||
{
|
||||
"label": longText,
|
||||
"value": "b"
|
||||
},
|
||||
{
|
||||
"label": "OptionC",
|
||||
"value": "c"
|
||||
},
|
||||
{
|
||||
"label": "OptionD",
|
||||
"value": "d"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const textControl = container.querySelector('.cxd-TextControl-input') as HTMLElement;
|
||||
|
||||
fireEvent.click(textControl);
|
||||
await wait(300);
|
||||
replaceReactAriaIds(container);
|
||||
const listItems = container.querySelectorAll('.cxd-TextControl-sugs .cxd-TextControl-sugItem');
|
||||
expect(listItems.length).toBe(4);
|
||||
|
||||
// 选中长文本项
|
||||
fireEvent.click(listItems[1]);
|
||||
await wait(300);
|
||||
|
||||
const valueLabel = screen.getByText(longText);
|
||||
// FIXME: ResizeObserver的 polyfill 在 jest 环境中不好使,先这样测吧
|
||||
expect(valueLabel).toBeInTheDocument();
|
||||
expect(valueLabel.classList).toContain('cxd-OverflowTpl');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -1,28 +1,27 @@
|
||||
import React from 'react';
|
||||
import Downshift, {StateChangeOptions} from 'downshift';
|
||||
import {matchSorter} from 'match-sorter';
|
||||
import debouce from 'lodash/debounce';
|
||||
import find from 'lodash/find';
|
||||
import {
|
||||
OptionsControl,
|
||||
OptionsControlProps,
|
||||
highlight,
|
||||
FormOptionsControl,
|
||||
resolveEventData,
|
||||
CustomStyle,
|
||||
getValueByPath,
|
||||
PopOver,
|
||||
Overlay,
|
||||
formatInputThemeCss,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
ActionObject,
|
||||
filter,
|
||||
autobind,
|
||||
createObject,
|
||||
setVariable,
|
||||
ucFirst,
|
||||
isEffectiveApi
|
||||
} from 'amis-core';
|
||||
import {ActionObject} from 'amis-core';
|
||||
import Downshift, {StateChangeOptions} from 'downshift';
|
||||
import {matchSorter} from 'match-sorter';
|
||||
import debouce from 'lodash/debounce';
|
||||
import {filter} from 'amis-core';
|
||||
import find from 'lodash/find';
|
||||
import {Icon, SpinnerExtraProps} from 'amis-ui';
|
||||
import {Input} from 'amis-ui';
|
||||
import {autobind, createObject, setVariable, ucFirst} from 'amis-core';
|
||||
import {isEffectiveApi} from 'amis-core';
|
||||
import {Spinner} from 'amis-ui';
|
||||
import {Icon, SpinnerExtraProps, Input, Spinner, OverflowTpl} from 'amis-ui';
|
||||
import {ActionSchema} from '../Action';
|
||||
import {FormOptionsSchema, SchemaApi} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
@ -800,9 +799,12 @@ export default class TextControl extends React.PureComponent<
|
||||
{selectedOptions.map((item, index) =>
|
||||
multiple ? (
|
||||
<div className={cx('TextControl-value')} key={index}>
|
||||
<span className={cx('TextControl-valueLabel')}>
|
||||
<OverflowTpl
|
||||
className={cx('TextControl-valueLabel')}
|
||||
tooltip={`${item[labelField || 'label']}`}
|
||||
>
|
||||
{`${item[labelField || 'label']}`}
|
||||
</span>
|
||||
</OverflowTpl>
|
||||
<Icon
|
||||
icon="close"
|
||||
className={cx('TextControl-valueIcon', 'icon')}
|
||||
|
Loading…
Reference in New Issue
Block a user