mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-19 14:53:16 +08:00
commit
b9de155fdf
2
.jest.js
2
.jest.js
@ -27,7 +27,7 @@ const transformIgnorePatterns = [
|
||||
|
||||
function getTestRegex(libDir) {
|
||||
if (['dist', 'lib', 'es'].includes(libDir)) {
|
||||
return 'demo\\.test\\.(j|t)s$';
|
||||
return 'demo\\.test\\.(j|t)sx?$';
|
||||
}
|
||||
return '.*\\.test\\.(j|t)sx?$';
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ jest.mock('../../tests/shared/demoTest', () => {
|
||||
(global as any).testConfig[name] = option;
|
||||
}
|
||||
|
||||
fakeDemoTest.rootPropsTest = () => {};
|
||||
|
||||
return fakeDemoTest;
|
||||
});
|
||||
|
||||
|
@ -19,11 +19,12 @@ export interface AdjustOverflow {
|
||||
}
|
||||
|
||||
export interface PlacementsConfig {
|
||||
arrowWidth?: number;
|
||||
arrowWidth: number;
|
||||
horizontalArrowShift?: number;
|
||||
verticalArrowShift?: number;
|
||||
arrowPointAtCenter?: boolean;
|
||||
autoAdjustOverflow?: boolean | AdjustOverflow;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export function getOverflowOptions(autoAdjustOverflow?: boolean | AdjustOverflow) {
|
||||
@ -36,73 +37,114 @@ export function getOverflowOptions(autoAdjustOverflow?: boolean | AdjustOverflow
|
||||
};
|
||||
}
|
||||
|
||||
type PlacementType = keyof BuildInPlacements;
|
||||
|
||||
function getArrowOffset(type: PlacementType, arrowWidth: number, offset: number): number[] {
|
||||
switch (type) {
|
||||
case 'top':
|
||||
case 'topLeft':
|
||||
case 'topRight':
|
||||
return [0, -(arrowWidth / 2 + offset)];
|
||||
case 'bottom':
|
||||
case 'bottomLeft':
|
||||
case 'bottomRight':
|
||||
return [0, arrowWidth / 2 + offset];
|
||||
case 'left':
|
||||
case 'leftTop':
|
||||
case 'leftBottom':
|
||||
return [-(arrowWidth / 2 + offset), 0];
|
||||
case 'right':
|
||||
case 'rightTop':
|
||||
case 'rightBottom':
|
||||
return [arrowWidth / 2 + offset, 0];
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
|
||||
function vertexCalc(point1: number[], point2: number[]): number[] {
|
||||
return [point1[0] + point2[0], point1[1] + point2[1]];
|
||||
}
|
||||
|
||||
export default function getPlacements(config: PlacementsConfig) {
|
||||
const {
|
||||
arrowWidth = 4,
|
||||
arrowWidth,
|
||||
horizontalArrowShift = 16,
|
||||
verticalArrowShift = 8,
|
||||
autoAdjustOverflow,
|
||||
arrowPointAtCenter,
|
||||
offset,
|
||||
} = config;
|
||||
const halfArrowWidth = arrowWidth / 2;
|
||||
|
||||
const placementMap: BuildInPlacements = {
|
||||
left: {
|
||||
points: ['cr', 'cl'],
|
||||
offset: [-4, 0],
|
||||
offset: [-offset, 0],
|
||||
},
|
||||
right: {
|
||||
points: ['cl', 'cr'],
|
||||
offset: [4, 0],
|
||||
offset: [offset, 0],
|
||||
},
|
||||
top: {
|
||||
points: ['bc', 'tc'],
|
||||
offset: [0, -4],
|
||||
offset: [0, -offset],
|
||||
},
|
||||
bottom: {
|
||||
points: ['tc', 'bc'],
|
||||
offset: [0, 4],
|
||||
offset: [0, offset],
|
||||
},
|
||||
topLeft: {
|
||||
points: ['bl', 'tc'],
|
||||
offset: [-(horizontalArrowShift + arrowWidth), -4],
|
||||
offset: [-(horizontalArrowShift + halfArrowWidth), -offset],
|
||||
},
|
||||
leftTop: {
|
||||
points: ['tr', 'cl'],
|
||||
offset: [-4, -(verticalArrowShift + arrowWidth)],
|
||||
offset: [-offset, -(verticalArrowShift + halfArrowWidth)],
|
||||
},
|
||||
topRight: {
|
||||
points: ['br', 'tc'],
|
||||
offset: [horizontalArrowShift + arrowWidth, -4],
|
||||
offset: [horizontalArrowShift + halfArrowWidth, -offset],
|
||||
},
|
||||
rightTop: {
|
||||
points: ['tl', 'cr'],
|
||||
offset: [4, -(verticalArrowShift + arrowWidth)],
|
||||
offset: [offset, -(verticalArrowShift + halfArrowWidth)],
|
||||
},
|
||||
bottomRight: {
|
||||
points: ['tr', 'bc'],
|
||||
offset: [horizontalArrowShift + arrowWidth, 4],
|
||||
offset: [horizontalArrowShift + halfArrowWidth, offset],
|
||||
},
|
||||
rightBottom: {
|
||||
points: ['bl', 'cr'],
|
||||
offset: [4, verticalArrowShift + arrowWidth],
|
||||
offset: [offset, verticalArrowShift + halfArrowWidth],
|
||||
},
|
||||
bottomLeft: {
|
||||
points: ['tl', 'bc'],
|
||||
offset: [-(horizontalArrowShift + arrowWidth), 4],
|
||||
offset: [-(horizontalArrowShift + halfArrowWidth), offset],
|
||||
},
|
||||
leftBottom: {
|
||||
points: ['br', 'cl'],
|
||||
offset: [-4, verticalArrowShift + arrowWidth],
|
||||
offset: [-offset, verticalArrowShift + halfArrowWidth],
|
||||
},
|
||||
};
|
||||
Object.keys(placementMap).forEach((key) => {
|
||||
placementMap[key] = arrowPointAtCenter
|
||||
? {
|
||||
...placementMap[key],
|
||||
offset: vertexCalc(
|
||||
placementMap[key].offset!,
|
||||
getArrowOffset(key as PlacementType, arrowWidth, offset),
|
||||
),
|
||||
overflow: getOverflowOptions(autoAdjustOverflow),
|
||||
targetOffset,
|
||||
}
|
||||
: {
|
||||
...placements[key],
|
||||
offset: vertexCalc(
|
||||
placements[key].offset!,
|
||||
getArrowOffset(key as PlacementType, arrowWidth, offset),
|
||||
),
|
||||
overflow: getOverflowOptions(autoAdjustOverflow),
|
||||
};
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('affix');
|
33
components/affix/__tests__/demo.test.tsx
Normal file
33
components/affix/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { spyElementPrototype } from 'rc-util/lib/test/domHook';
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('affix', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest(
|
||||
'affix',
|
||||
(Affix, props) => (
|
||||
<Affix {...props} className="fixed" target={() => document.querySelector('#holder')}>
|
||||
Bamboo
|
||||
</Affix>
|
||||
),
|
||||
{
|
||||
beforeRender: () => {
|
||||
spyElementPrototype(HTMLElement, 'getBoundingClientRect', function getBoundingClientRect() {
|
||||
if (this.id === 'holder') {
|
||||
return { top: 0, bottom: 100 };
|
||||
}
|
||||
|
||||
if (this.className === 'fixed') {
|
||||
return { top: -100, bottom: -100 };
|
||||
}
|
||||
|
||||
return { top: 0, bottom: 0 };
|
||||
});
|
||||
},
|
||||
findRootElements: () => document.querySelectorAll('.ant-affix'),
|
||||
expectCount: 1,
|
||||
},
|
||||
);
|
@ -32,12 +32,12 @@ export interface AffixProps {
|
||||
target?: () => Window | HTMLElement | null;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface InternalAffixProps extends AffixProps {
|
||||
affixPrefixCls: string;
|
||||
rootClassName: string;
|
||||
}
|
||||
|
||||
enum AffixStatus {
|
||||
@ -262,8 +262,7 @@ class Affix extends React.Component<InternalAffixProps, AffixState> {
|
||||
render() {
|
||||
const { affixStyle, placeholderStyle } = this.state;
|
||||
const { affixPrefixCls, rootClassName, children } = this.props;
|
||||
const className = classNames({
|
||||
[rootClassName]: !!affixStyle,
|
||||
const className = classNames(affixStyle && rootClassName, {
|
||||
[affixPrefixCls]: !!affixStyle,
|
||||
});
|
||||
|
||||
@ -297,7 +296,7 @@ class Affix extends React.Component<InternalAffixProps, AffixState> {
|
||||
export type InternalAffixClass = Affix;
|
||||
|
||||
const AffixFC = forwardRef<Affix, AffixProps>((props, ref) => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const { prefixCls: customizePrefixCls, rootClassName } = props;
|
||||
const { getPrefixCls } = useContext<ConfigConsumerProps>(ConfigContext);
|
||||
const affixPrefixCls = getPrefixCls('affix', customizePrefixCls);
|
||||
|
||||
@ -306,7 +305,7 @@ const AffixFC = forwardRef<Affix, AffixProps>((props, ref) => {
|
||||
const AffixProps: InternalAffixProps = {
|
||||
...props,
|
||||
affixPrefixCls,
|
||||
rootClassName: hashId,
|
||||
rootClassName: classNames(rootClassName, hashId),
|
||||
};
|
||||
|
||||
return wrapSSR(<Affix {...AffixProps} ref={ref} />);
|
||||
|
@ -37,6 +37,7 @@ export interface AlertProps {
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
banner?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
/** Custom closeIcon */
|
||||
@ -100,7 +101,8 @@ const Alert: CompoundedComponent = ({
|
||||
prefixCls: customizePrefixCls,
|
||||
message,
|
||||
banner,
|
||||
className = '',
|
||||
className,
|
||||
rootClassName,
|
||||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
@ -151,6 +153,7 @@ const Alert: CompoundedComponent = ({
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
|
||||
import Affix from '../affix';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import warning from '../_util/warning';
|
||||
import AnchorContext from './context';
|
||||
import type { AnchorLinkBaseProps } from './AnchorLink';
|
||||
import AnchorLink from './AnchorLink';
|
||||
import AnchorContext from './context';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
@ -51,6 +53,7 @@ interface Section {
|
||||
export interface AnchorProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
/**
|
||||
* @deprecated Please use `items` instead.
|
||||
@ -72,6 +75,7 @@ export interface AnchorProps {
|
||||
/** Listening event when scrolling change active link */
|
||||
onChange?: (currentActiveLink: string) => void;
|
||||
items?: AnchorLinkItemProps[];
|
||||
direction?: AnchorDirection;
|
||||
}
|
||||
|
||||
interface InternalAnchorProps extends AnchorProps {
|
||||
@ -90,6 +94,8 @@ export interface AnchorDefaultProps extends AnchorProps {
|
||||
getContainer: () => AnchorContainer;
|
||||
}
|
||||
|
||||
export type AnchorDirection = 'vertical' | 'horizontal';
|
||||
|
||||
export interface AntAnchor {
|
||||
registerLink: (link: string) => void;
|
||||
unregisterLink: (link: string) => void;
|
||||
@ -99,6 +105,7 @@ export interface AntAnchor {
|
||||
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,
|
||||
link: { title: React.ReactNode; href: string },
|
||||
) => void;
|
||||
direction: AnchorDirection;
|
||||
}
|
||||
|
||||
const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
@ -112,6 +119,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
showInkInFixed = false,
|
||||
children,
|
||||
items,
|
||||
direction: anchorDirection = 'vertical',
|
||||
bounds,
|
||||
targetOffset,
|
||||
onClick,
|
||||
@ -125,6 +133,14 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
warning(!children, 'Anchor', '`Anchor children` is deprecated. Please use `items` instead.');
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
!(anchorDirection === 'horizontal' && items?.some((n) => 'children' in n)),
|
||||
'Anchor',
|
||||
'`Anchor items#children` is not supported when `Anchor` direction is horizontal.',
|
||||
);
|
||||
}
|
||||
|
||||
const [links, setLinks] = React.useState<string[]>([]);
|
||||
const [activeLink, setActiveLink] = React.useState<string | null>(null);
|
||||
const activeLinkRef = React.useRef<string | null>(activeLink);
|
||||
@ -162,8 +178,17 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
`.${prefixCls}-link-title-active`,
|
||||
);
|
||||
if (linkNode && spanLinkNode.current) {
|
||||
spanLinkNode.current.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2}px`;
|
||||
spanLinkNode.current.style.height = `${linkNode.clientHeight}px`;
|
||||
if (anchorDirection !== 'horizontal') {
|
||||
spanLinkNode.current.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2}px`;
|
||||
spanLinkNode.current.style.height = `${linkNode.clientHeight}px`;
|
||||
} else {
|
||||
spanLinkNode.current.style.left = `${linkNode.offsetLeft}px`;
|
||||
spanLinkNode.current.style.width = `${linkNode.clientWidth}px`;
|
||||
scrollIntoView(linkNode, {
|
||||
scrollMode: 'if-needed',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -249,17 +274,11 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
[targetOffset, offsetTop],
|
||||
);
|
||||
|
||||
const inkClass = classNames(
|
||||
{
|
||||
[`${prefixCls}-ink-ball-visible`]: activeLink,
|
||||
},
|
||||
`${prefixCls}-ink-ball`,
|
||||
);
|
||||
|
||||
const wrapperClass = classNames(
|
||||
rootClassName,
|
||||
`${prefixCls}-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-wrapper-horizontal`]: anchorDirection === 'horizontal',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
@ -269,6 +288,10 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
[`${prefixCls}-fixed`]: !affix && !showInkInFixed,
|
||||
});
|
||||
|
||||
const inkClass = classNames(`${prefixCls}-ink`, {
|
||||
[`${prefixCls}-ink-visible`]: activeLink,
|
||||
});
|
||||
|
||||
const wrapperStyle: React.CSSProperties = {
|
||||
maxHeight: offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh',
|
||||
...style,
|
||||
@ -278,7 +301,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
Array.isArray(options)
|
||||
? options.map((item) => (
|
||||
<AnchorLink {...item} key={item.key}>
|
||||
{createNestedLink(item.children)}
|
||||
{anchorDirection === 'vertical' && createNestedLink(item.children)}
|
||||
</AnchorLink>
|
||||
))
|
||||
: null;
|
||||
@ -286,9 +309,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
const anchorContent = (
|
||||
<div ref={wrapperRef} className={wrapperClass} style={wrapperStyle}>
|
||||
<div className={anchorClass}>
|
||||
<div className={`${prefixCls}-ink`}>
|
||||
<span className={inkClass} ref={spanLinkNode} />
|
||||
</div>
|
||||
<span className={inkClass} ref={spanLinkNode} />
|
||||
{'items' in props ? createNestedLink(items) : children}
|
||||
</div>
|
||||
</div>
|
||||
@ -311,7 +332,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
|
||||
React.useEffect(() => {
|
||||
updateInk();
|
||||
}, [getCurrentAnchor, dependencyListItem, activeLink]);
|
||||
}, [anchorDirection, getCurrentAnchor, dependencyListItem, activeLink]);
|
||||
|
||||
const memoizedContextValue = React.useMemo<AntAnchor>(
|
||||
() => ({
|
||||
@ -320,8 +341,9 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
scrollTo: handleScrollTo,
|
||||
activeLink,
|
||||
onClick,
|
||||
direction: anchorDirection,
|
||||
}),
|
||||
[activeLink, onClick, handleScrollTo],
|
||||
[activeLink, onClick, handleScrollTo, anchorDirection],
|
||||
);
|
||||
|
||||
return (
|
||||
@ -338,14 +360,18 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
};
|
||||
|
||||
const Anchor: React.FC<AnchorProps> = (props) => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const { prefixCls: customizePrefixCls, rootClassName } = props;
|
||||
const { getPrefixCls } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
const anchorPrefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
|
||||
const [wrapSSR, hashId] = useStyle(anchorPrefixCls);
|
||||
|
||||
return wrapSSR(
|
||||
<AnchorContent {...props} rootClassName={hashId} anchorPrefixCls={anchorPrefixCls} />,
|
||||
<AnchorContent
|
||||
{...props}
|
||||
rootClassName={classNames(hashId, rootClassName)}
|
||||
anchorPrefixCls={anchorPrefixCls}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { ConfigConsumer } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import type { AntAnchor } from './Anchor';
|
||||
import AnchorContext from './context';
|
||||
|
||||
@ -22,7 +23,7 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
|
||||
const context = React.useContext<AntAnchor | undefined>(AnchorContext);
|
||||
|
||||
const { registerLink, unregisterLink, scrollTo, onClick, activeLink } = context || {};
|
||||
const { registerLink, unregisterLink, scrollTo, onClick, activeLink, direction } = context || {};
|
||||
|
||||
React.useEffect(() => {
|
||||
registerLink?.(href);
|
||||
@ -36,31 +37,42 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
scrollTo?.(href);
|
||||
};
|
||||
|
||||
const { getPrefixCls } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, className, {
|
||||
[`${prefixCls}-link-active`]: activeLink === href,
|
||||
});
|
||||
|
||||
const titleClassName = classNames(`${prefixCls}-link-title`, {
|
||||
[`${prefixCls}-link-title-active`]: activeLink === href,
|
||||
});
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
!children || direction !== 'horizontal',
|
||||
'Anchor.Link',
|
||||
'`Anchor.Link children` is not supported when `Anchor` direction is horizontal',
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<a
|
||||
className={titleClassName}
|
||||
href={href}
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
target={target}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
{children}
|
||||
</div>
|
||||
<ConfigConsumer>
|
||||
{({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
const active = activeLink === href;
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, className, {
|
||||
[`${prefixCls}-link-active`]: active,
|
||||
});
|
||||
const titleClassName = classNames(`${prefixCls}-link-title`, {
|
||||
[`${prefixCls}-link-title-active`]: active,
|
||||
});
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<a
|
||||
className={titleClassName}
|
||||
href={href}
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
target={target}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
{direction !== 'horizontal' ? children : null}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ConfigConsumer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
|
||||
import Anchor from '..';
|
||||
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
|
||||
@ -13,12 +16,15 @@ function createDiv() {
|
||||
let idCounter = 0;
|
||||
const getHashUrl = () => `Anchor-API-${idCounter++}`;
|
||||
|
||||
jest.mock('scroll-into-view-if-needed', () => jest.fn());
|
||||
|
||||
describe('Anchor Render', () => {
|
||||
const getBoundingClientRectMock = jest.spyOn(
|
||||
HTMLHeadingElement.prototype,
|
||||
'getBoundingClientRect',
|
||||
);
|
||||
const getClientRectsMock = jest.spyOn(HTMLHeadingElement.prototype, 'getClientRects');
|
||||
const scrollIntoViewMock = jest.createMockFromModule<any>('scroll-into-view-if-needed');
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
@ -27,11 +33,12 @@ describe('Anchor Render', () => {
|
||||
height: 100,
|
||||
top: 1000,
|
||||
} as DOMRect);
|
||||
getClientRectsMock.mockReturnValue({ length: 1 } as DOMRectList);
|
||||
getClientRectsMock.mockReturnValue([1] as unknown as DOMRectList);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
scrollIntoViewMock.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -46,22 +53,133 @@ describe('Anchor Render', () => {
|
||||
getClientRectsMock.mockRestore();
|
||||
});
|
||||
|
||||
it('renders correctly', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
it('renders items correctly', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
children: [
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(5);
|
||||
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!).map((n) =>
|
||||
(n as HTMLElement).querySelector('.ant-anchor-link-title'),
|
||||
);
|
||||
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
|
||||
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
|
||||
expect((linkTitles[3] as HTMLAnchorElement).href).toContain('#api');
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#anchor-props');
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#link-props');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders items correctly#horizontal', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(3);
|
||||
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!).map((n) =>
|
||||
(n as HTMLElement).querySelector('.ant-anchor-link-title'),
|
||||
);
|
||||
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
|
||||
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
|
||||
expect((linkTitles[3] as HTMLAnchorElement).href).toContain('#api');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render items and ignore jsx children', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Link href="#api" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(container.querySelector(`a[href="#${hash}"]`)).not.toBe(null);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(1);
|
||||
expect(
|
||||
(container.querySelector('.ant-anchor .ant-anchor-link-title') as HTMLAnchorElement).href,
|
||||
).toContain('#components-anchor-demo-basic');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('actives the target when clicking a link', async () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor prefixCls="ant-anchor">
|
||||
<Link href={`http://www.example.com/#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
prefixCls="ant-anchor"
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: hash,
|
||||
title: hash,
|
||||
href: `http://www.example.com/#${hash}`,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!;
|
||||
fireEvent.click(link);
|
||||
@ -74,9 +192,7 @@ describe('Anchor Render', () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
render(<div id="/faq?locale=en#Q1">Q1</div>, { container: root });
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="/#/faq?locale=en#Q1" title="Q1" />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: 'Q1', title: 'Q1', href: '/#/faq?locale=en#Q1' }]} />,
|
||||
);
|
||||
const link = container.querySelector(`a[href="/#/faq?locale=en#Q1"]`)!;
|
||||
fireEvent.click(link);
|
||||
@ -97,10 +213,13 @@ describe('Anchor Render', () => {
|
||||
{ container: root },
|
||||
);
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
onChange={onChange}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
onChange.mockClear();
|
||||
|
||||
@ -119,9 +238,7 @@ describe('Anchor Render', () => {
|
||||
it('should update DOM when children are unmounted', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} />,
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.ant-anchor-link-title')).toHaveLength(1);
|
||||
@ -134,11 +251,7 @@ describe('Anchor Render', () => {
|
||||
it('should update DOM when link href is changed', async () => {
|
||||
const hash = getHashUrl();
|
||||
function AnchorUpdate({ href }: { href: string }) {
|
||||
return (
|
||||
<Anchor>
|
||||
<Link href={href} title={hash} />
|
||||
</Anchor>
|
||||
);
|
||||
return <Anchor items={[{ key: hash, href, title: hash }]} />;
|
||||
}
|
||||
const { container, rerender } = render(<AnchorUpdate href={`#${hash}`} />);
|
||||
|
||||
@ -154,17 +267,11 @@ describe('Anchor Render', () => {
|
||||
const root = createDiv();
|
||||
render(<h1 id={hash}>Hello</h1>, { container: root });
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} />,
|
||||
);
|
||||
|
||||
const setProps = (props: Record<string, any>) =>
|
||||
rerender(
|
||||
<Anchor {...props}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
rerender(<Anchor {...props} items={[{ key: hash, href: `#${hash}`, title: hash }]} />);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
@ -191,17 +298,11 @@ describe('Anchor Render', () => {
|
||||
const root = createDiv();
|
||||
render(<h1 id={hash}>Hello</h1>, { container: root });
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} />,
|
||||
);
|
||||
|
||||
const setProps = (props: Record<string, any>) =>
|
||||
rerender(
|
||||
<Anchor {...props}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
rerender(<Anchor {...props} items={[{ key: hash, href: `#${hash}`, title: hash }]} />);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
@ -233,9 +334,7 @@ describe('Anchor Render', () => {
|
||||
const href = `#${hash}`;
|
||||
const title = hash;
|
||||
const { container } = render(
|
||||
<Anchor onClick={handleClick}>
|
||||
<Link href={href} title={title} />
|
||||
</Anchor>,
|
||||
<Anchor onClick={handleClick} items={[{ key: hash, href, title }]} />,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="${href}"]`)!);
|
||||
@ -248,10 +347,21 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
onChange={onChange}
|
||||
items={[
|
||||
{
|
||||
key: hash1,
|
||||
href: `#${hash1}`,
|
||||
title: hash1,
|
||||
},
|
||||
{
|
||||
key: hash2,
|
||||
href: `#${hash2}`,
|
||||
title: hash2,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
|
||||
{ legacyRoot: true },
|
||||
);
|
||||
@ -264,9 +374,7 @@ describe('Anchor Render', () => {
|
||||
|
||||
it('handles invalid hash correctly', () => {
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="notexsited" title="title" />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: 'title', href: 'notexsited', title: 'title' }]} />,
|
||||
);
|
||||
|
||||
const link = container.querySelector(`a[href="notexsited"]`)!;
|
||||
@ -356,10 +464,13 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const getCurrentAnchor = () => `#${hash2}`;
|
||||
const { container } = render(
|
||||
<Anchor getCurrentAnchor={getCurrentAnchor}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
getCurrentAnchor={getCurrentAnchor}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash2);
|
||||
@ -371,10 +482,14 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange} getCurrentAnchor={() => hash1}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
onChange={onChange}
|
||||
getCurrentAnchor={() => hash1}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
|
||||
{ legacyRoot: true },
|
||||
);
|
||||
@ -391,10 +506,13 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const getCurrentAnchor = jest.fn();
|
||||
const { container } = render(
|
||||
<Anchor getCurrentAnchor={getCurrentAnchor}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
getCurrentAnchor={getCurrentAnchor}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash1}"]`)!);
|
||||
@ -408,10 +526,13 @@ describe('Anchor Render', () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const Demo: React.FC<{ current: string }> = ({ current }) => (
|
||||
<Anchor getCurrentAnchor={() => `#${current}`}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>
|
||||
<Anchor
|
||||
getCurrentAnchor={() => `#${current}`}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
const { container, rerender } = render(<Demo current={hash1} />);
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash1);
|
||||
@ -422,72 +543,348 @@ describe('Anchor Render', () => {
|
||||
it('should render correctly when href is null', () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<Anchor>
|
||||
<Link href={null as unknown as string} title="test" />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: 'test', href: null as unknown as string, title: 'test' }]} />,
|
||||
);
|
||||
fireEvent.scroll(window || document);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders items correctly', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
describe('horizontal anchor', () => {
|
||||
describe('scroll x', () => {
|
||||
it('targetOffset horizontal', async () => {
|
||||
const hash = getHashUrl();
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const root = createDiv();
|
||||
render(<h1 id={hash}>Hello</h1>, { container: root });
|
||||
const { container, rerender } = render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
children: [
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
key: hash,
|
||||
href: `#${hash}`,
|
||||
title: hash,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(5);
|
||||
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!)
|
||||
.slice(1)
|
||||
.map((n) => (n as HTMLElement).querySelector('.ant-anchor-link-title'));
|
||||
expect((linkTitles[0] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
|
||||
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
|
||||
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#api');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#anchor-props');
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#link-props');
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const setProps = (props: Record<string, any>) =>
|
||||
rerender(
|
||||
<Anchor
|
||||
{...props}
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: hash,
|
||||
href: `#${hash}`,
|
||||
title: hash,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(scrollIntoView).toHaveBeenCalled();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
|
||||
setProps({ offsetTop: 100 });
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
|
||||
setProps({ targetOffset: 200 });
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
});
|
||||
});
|
||||
|
||||
it('test direction prop', () => {
|
||||
const { container } = render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor-ink').length).toBe(1);
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-anchor-wrapper')
|
||||
?.classList.contains('ant-anchor-wrapper-horizontal'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('nested children via items should be filtered out when direction is horizontal', () => {
|
||||
const { container } = render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor-link').length).toBe(3);
|
||||
});
|
||||
|
||||
it('nested children via jsx should be filtered out when direction is horizontal', () => {
|
||||
const { container } = render(
|
||||
<Anchor direction="horizontal">
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<Link href="#api" title="API">
|
||||
<Link href="#anchor-props" title="Anchor Props" />
|
||||
<Link href="#link-props" title="Link Props" />
|
||||
</Link>
|
||||
</Anchor>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor-link').length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deprecated/legacy jsx syntax', () => {
|
||||
it('renders jsx correctly', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(container.querySelector(`a[href="#${hash}"]`)).not.toBe(null);
|
||||
});
|
||||
|
||||
it('actives the target when clicking a link', async () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor prefixCls="ant-anchor">
|
||||
<Link href={`http://www.example.com/#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!;
|
||||
fireEvent.click(link);
|
||||
await waitFakeTimer();
|
||||
expect(link.classList).toContain('ant-anchor-link-title-active');
|
||||
});
|
||||
|
||||
it('scrolls the page when clicking a link', async () => {
|
||||
const root = createDiv();
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
render(<div id="/faq?locale=en#Q1">Q1</div>, { container: root });
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="/#/faq?locale=en#Q1" title="Q1" />
|
||||
</Anchor>,
|
||||
);
|
||||
const link = container.querySelector(`a[href="/#/faq?locale=en#Q1"]`)!;
|
||||
fireEvent.click(link);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handleScroll should not be triggered when scrolling caused by clicking a link', async () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const root = createDiv();
|
||||
const onChange = jest.fn();
|
||||
render(
|
||||
<div>
|
||||
<div id={hash1}>Hello</div>
|
||||
<div id={hash2}>World</div>
|
||||
</div>,
|
||||
{ container: root },
|
||||
);
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
);
|
||||
onChange.mockClear();
|
||||
|
||||
const link = container.querySelector(`a[href="#${hash2}"]`)!;
|
||||
// this will trigger 1 onChange
|
||||
fireEvent.click(link);
|
||||
// smooth scroll caused by clicking needs time to finish.
|
||||
// we scroll the window before it finish, the scroll listener should not be triggered,
|
||||
fireEvent.scroll(window);
|
||||
|
||||
await waitFakeTimer();
|
||||
// if the scroll listener is triggered, we will get 2 onChange, now we expect only 1.
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should update DOM when children are unmounted', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.ant-anchor-link-title')).toHaveLength(1);
|
||||
expect(container.querySelector('.ant-anchor-link-title')).toHaveAttribute('href', `#${hash}`);
|
||||
|
||||
rerender(<Anchor />);
|
||||
expect(container.querySelector('.ant-anchor-link-title')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update DOM when link href is changed', async () => {
|
||||
const hash = getHashUrl();
|
||||
function AnchorUpdate({ href }: { href: string }) {
|
||||
return (
|
||||
<Anchor>
|
||||
<Link href={href} title={hash} />
|
||||
</Anchor>
|
||||
);
|
||||
}
|
||||
const { container, rerender } = render(<AnchorUpdate href={`#${hash}`} />);
|
||||
|
||||
expect(container.querySelector(`a[href="#${hash}"]`)).toBeTruthy();
|
||||
rerender(<AnchorUpdate href={`#${hash}_1`} />);
|
||||
expect(container.querySelector(`a[href="#${hash}_1"]`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('handles invalid hash correctly', () => {
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="notexsited" title="title" />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
const link = container.querySelector(`a[href="notexsited"]`)!;
|
||||
fireEvent.click(link);
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe('title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('warning', () => {
|
||||
let errSpy: jest.SpyInstance;
|
||||
beforeEach(() => {
|
||||
resetWarned();
|
||||
errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('warning nested children when direction is horizontal ', () => {
|
||||
render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor items#children` is not supported when `Anchor` direction is horizontal.',
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated jsx style', () => {
|
||||
render(
|
||||
<Anchor direction="horizontal">
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor children` is deprecated. Please use `items` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated jsx style for direction#vertical', () => {
|
||||
render(
|
||||
<Anchor>
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor children` is deprecated. Please use `items` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated jsx style for direction#vertical 1: with nested children', () => {
|
||||
render(
|
||||
<Anchor direction="horizontal">
|
||||
<Link href="#api" title="API">
|
||||
<Link href="#anchor-props" title="Anchor Props" />
|
||||
</Link>
|
||||
</Anchor>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor children` is deprecated. Please use `items` instead.',
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor.Link] `Anchor.Link children` is not supported when `Anchor` direction is horizontal',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Anchor Render render items and ignore jsx children 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Anchor Render renders items correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
@ -12,13 +44,9 @@ exports[`Anchor Render renders items correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -79,3 +107,57 @@ exports[`Anchor Render renders items correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Anchor Render renders items correctly#horizontal 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -34,13 +34,9 @@ exports[`renders ./components/anchor/demo/basic.tsx extend context correctly 1`]
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -90,13 +86,9 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx extend context
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -156,82 +148,191 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx extend context
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx extend context correctly 1`] = `
|
||||
<div>
|
||||
exports[`renders ./components/anchor/demo/horizontal.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class=""
|
||||
style="padding:20px"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-ink"
|
||||
class="ant-anchor-wrapper ant-anchor-wrapper-horizontal"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
class="ant-anchor"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-4"
|
||||
title="Part 4"
|
||||
>
|
||||
Part 4
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-5"
|
||||
title="Part 5"
|
||||
>
|
||||
Part 5
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-6"
|
||||
title="Part 6"
|
||||
>
|
||||
Part 6
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#FFFBE9"
|
||||
/>
|
||||
<div
|
||||
id="part-4"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#F4EAD5"
|
||||
/>
|
||||
<div
|
||||
id="part-5"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#DAE2B6"
|
||||
/>
|
||||
<div
|
||||
id="part-6"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#CCD6A6"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -244,13 +345,9 @@ exports[`renders ./components/anchor/demo/onChange.tsx extend context correctly
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -318,13 +415,9 @@ exports[`renders ./components/anchor/demo/onClick.tsx extend context correctly 1
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -392,13 +485,9 @@ exports[`renders ./components/anchor/demo/static.tsx extend context correctly 1`
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -499,13 +588,9 @@ exports[`renders ./components/anchor/demo/targetOffset.tsx extend context correc
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
|
@ -34,13 +34,9 @@ exports[`renders ./components/anchor/demo/basic.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -90,13 +86,9 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx correctly 1`] =
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -156,82 +148,191 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx correctly 1`] =
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx correctly 1`] = `
|
||||
<div>
|
||||
exports[`renders ./components/anchor/demo/horizontal.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class=""
|
||||
style="padding:20px"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-ink"
|
||||
class="ant-anchor-wrapper ant-anchor-wrapper-horizontal"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
class="ant-anchor"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-4"
|
||||
title="Part 4"
|
||||
>
|
||||
Part 4
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-5"
|
||||
title="Part 5"
|
||||
>
|
||||
Part 5
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-6"
|
||||
title="Part 6"
|
||||
>
|
||||
Part 6
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#FFFBE9"
|
||||
/>
|
||||
<div
|
||||
id="part-4"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#F4EAD5"
|
||||
/>
|
||||
<div
|
||||
id="part-5"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#DAE2B6"
|
||||
/>
|
||||
<div
|
||||
id="part-6"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#CCD6A6"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -244,13 +345,9 @@ exports[`renders ./components/anchor/demo/onChange.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -318,13 +415,9 @@ exports[`renders ./components/anchor/demo/onClick.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -392,13 +485,9 @@ exports[`renders ./components/anchor/demo/static.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -499,13 +588,9 @@ exports[`renders ./components/anchor/demo/targetOffset.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('anchor');
|
25
components/anchor/__tests__/demo.test.tsx
Normal file
25
components/anchor/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('anchor', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest(
|
||||
'anchor',
|
||||
(Anchor, props) => (
|
||||
<Anchor
|
||||
{...props}
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
{
|
||||
findRootElements: () => document.querySelector('.ant-anchor-wrapper')!,
|
||||
},
|
||||
);
|
7
components/anchor/demo/horizontal.md
Normal file
7
components/anchor/demo/horizontal.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
横向 Anchor。
|
||||
|
||||
## en-US
|
||||
|
||||
Horizontally aligned anchors
|
82
components/anchor/demo/horizontal.tsx
Normal file
82
components/anchor/demo/horizontal.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<div style={{ padding: '20px' }}>
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
{
|
||||
key: 'part-4',
|
||||
href: '#part-4',
|
||||
title: 'Part 4',
|
||||
},
|
||||
{
|
||||
key: 'part-5',
|
||||
href: '#part-5',
|
||||
title: 'Part 5',
|
||||
},
|
||||
{
|
||||
key: 'part-6',
|
||||
href: '#part-6',
|
||||
title: 'Part 6',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(0,255,0,0.02)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(0,0,255,0.02)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#FFFBE9' }}
|
||||
/>
|
||||
<div
|
||||
id="part-4"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#F4EAD5' }}
|
||||
/>
|
||||
<div
|
||||
id="part-5"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#DAE2B6' }}
|
||||
/>
|
||||
<div
|
||||
id="part-6"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#CCD6A6' }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
@ -4,7 +4,7 @@ import { Anchor } from 'antd';
|
||||
const { Link } = Anchor;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor>
|
||||
<Anchor affix={false}>
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<Link href="#api" title="API">
|
||||
|
@ -3,7 +3,6 @@ category: Components
|
||||
title: Anchor
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
|
||||
demo:
|
||||
cols: 2
|
||||
group:
|
||||
title: Navigation
|
||||
order: 3
|
||||
@ -23,7 +22,8 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx" iframe="200">Basic</code>
|
||||
<code src="./demo/static.tsx">Static Anchor</code>
|
||||
<code src="./demo/horizontal.tsx" iframe="200">Horizontal Anchor</code>
|
||||
<code src="./demo/static.tsx" >Static Anchor</code>
|
||||
<code src="./demo/onClick.tsx">Customize the onClick event</code>
|
||||
<code src="./demo/customizeHighlight.tsx">Customize the anchor highlight</code>
|
||||
<code src="./demo/targetOffset.tsx" iframe="200">Set Anchor scroll offset</code>
|
||||
@ -45,10 +45,23 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetoffset) | number | - | |
|
||||
| onChange | Listening for anchor link change | (currentActiveLink: string) => void | | |
|
||||
| onClick | Set the handler to handle `click` event | (e: MouseEvent, link: object) => void | - | |
|
||||
| items | Data configuration option content, support nesting through children | { href, title, target, children }\[] | - | |
|
||||
| items | Data configuration option content, support nesting through children | { key, href, title, target, children }\[] [see](#anchoritem) | - | 5.1.0 |
|
||||
| direction | Set Anchor direction | `vertical` \| `horizontal` | `vertical` | 5.2.0 |
|
||||
|
||||
### AnchorItem
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| key | The unique identifier of the Anchor Link | string \| number | - | |
|
||||
| href | The target of hyperlink | string | | |
|
||||
| target | Specifies where to display the linked URL | string | | |
|
||||
| title | The content of hyperlink | ReactNode | | |
|
||||
| children | Nested Anchor Link, `Attention: This attribute does not support horizontal orientation` | [AnchorItem](#anchoritem)\[] | - | |
|
||||
|
||||
### Link Props
|
||||
|
||||
We recommend using the items form instead.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| -------- | ----------------------------------------- | --------- | ------- | ------- |
|
||||
| href | The target of hyperlink | string | | |
|
||||
|
@ -4,7 +4,6 @@ title: Anchor
|
||||
subtitle: 锚点
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
|
||||
demo:
|
||||
cols: 2
|
||||
group:
|
||||
title: 导航
|
||||
order: 3
|
||||
@ -24,6 +23,7 @@ group:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx" iframe="200">基本</code>
|
||||
<code src="./demo/horizontal.tsx" iframe="200">横向 Anchor</code>
|
||||
<code src="./demo/static.tsx">静态位置</code>
|
||||
<code src="./demo/onClick.tsx">自定义 onClick 事件</code>
|
||||
<code src="./demo/customizeHighlight.tsx">自定义锚点高亮</code>
|
||||
@ -46,12 +46,25 @@ group:
|
||||
| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetoffset) | number | - | |
|
||||
| onChange | 监听锚点链接改变 | (currentActiveLink: string) => void | - | |
|
||||
| onClick | `click` 事件的 handler | (e: MouseEvent, link: object) => void | - | |
|
||||
| items | 数据化配置选项内容,支持通过 children 嵌套 | { href, title, target, children }\[] | - | |
|
||||
| items | 数据化配置选项内容,支持通过 children 嵌套 | { key, href, title, target, children }\[] [具体见](#anchoritem) | - | 5.1.0 |
|
||||
| direction | 设置导航方向 | `vertical` \| `horizontal` | `vertical` | 5.2.0 |
|
||||
|
||||
### AnchorItem
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| key | 唯一标志 | string \| number | - | |
|
||||
| href | 锚点链接 | string | - | |
|
||||
| target | 该属性指定在何处显示链接的资源 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
| children | 嵌套的 Anchor Link,`注意:水平方向该属性不支持` | [AnchorItem](#anchoritem)\[] | - | |
|
||||
|
||||
### Link Props
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| ------ | -------------------------------- | --------- | ------ | ---- |
|
||||
| href | 锚点链接 | string | - | |
|
||||
| target | 该属性指定在何处显示链接的资源。 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
建议使用 items 形式。
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| ------ | ------------------------------ | --------- | ------ | ---- |
|
||||
| href | 锚点链接 | string | - | |
|
||||
| target | 该属性指定在何处显示链接的资源 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
|
@ -16,8 +16,15 @@ interface AnchorToken extends FullToken<'Anchor'> {
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
const { componentCls, holderOffsetBlock, motionDurationSlow, lineWidthBold, colorPrimary } =
|
||||
token;
|
||||
const {
|
||||
componentCls,
|
||||
holderOffsetBlock,
|
||||
motionDurationSlow,
|
||||
lineWidthBold,
|
||||
colorPrimary,
|
||||
lineType,
|
||||
colorSplit,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
[`${componentCls}-wrapper`]: {
|
||||
@ -34,40 +41,6 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
position: 'relative',
|
||||
paddingInlineStart: lineWidthBold,
|
||||
|
||||
[`${componentCls}-ink`]: {
|
||||
position: 'absolute',
|
||||
insetBlockStart: 0,
|
||||
insetInlineStart: 0,
|
||||
height: '100%',
|
||||
|
||||
'&::before': {
|
||||
position: 'relative',
|
||||
display: 'block',
|
||||
width: lineWidthBold,
|
||||
height: '100%',
|
||||
margin: '0 auto',
|
||||
backgroundColor: token.colorSplit,
|
||||
content: '" "',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-ink-ball`]: {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
display: 'none',
|
||||
transform: 'translateY(-50%)',
|
||||
transition: `top ${motionDurationSlow} ease-in-out`,
|
||||
width: lineWidthBold,
|
||||
backgroundColor: colorPrimary,
|
||||
|
||||
[`&${componentCls}-ink-ball-visible`]: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-link`]: {
|
||||
paddingBlock: token.anchorPaddingBlock,
|
||||
paddingInline: `${token.anchorPaddingInline}px 0`,
|
||||
@ -96,13 +69,94 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-fixed ${componentCls}-ink ${componentCls}-ink-ball`]: {
|
||||
[`&:not(${componentCls}-horizontal)`]: {
|
||||
[componentCls]: {
|
||||
'&::before': {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
top: 0,
|
||||
height: '100%',
|
||||
borderInlineStart: `${lineWidthBold}px ${lineType} ${colorSplit}`,
|
||||
content: '" "',
|
||||
},
|
||||
|
||||
[`${componentCls}-ink`]: {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
display: 'none',
|
||||
transform: 'translateY(-50%)',
|
||||
transition: `top ${motionDurationSlow} ease-in-out`,
|
||||
width: lineWidthBold,
|
||||
backgroundColor: colorPrimary,
|
||||
|
||||
[`&${componentCls}-ink-visible`]: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-fixed ${componentCls}-ink ${componentCls}-ink`]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
const { componentCls, motionDurationSlow, lineWidthBold, colorPrimary } = token;
|
||||
|
||||
return {
|
||||
[`${componentCls}-wrapper-horizontal`]: {
|
||||
position: 'relative',
|
||||
|
||||
'&::before': {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
right: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
bottom: 0,
|
||||
borderBottom: `1px ${token.lineType} ${token.colorSplit}`,
|
||||
content: '" "',
|
||||
},
|
||||
|
||||
[componentCls]: {
|
||||
overflowX: 'scroll',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
scrollbarWidth: 'none' /* Firefox */,
|
||||
|
||||
'&::-webkit-scrollbar': {
|
||||
display: 'none' /* Safari and Chrome */,
|
||||
},
|
||||
|
||||
[`${componentCls}-link:first-of-type`]: {
|
||||
paddingInline: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-ink`]: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
transition: `left ${motionDurationSlow} ease-in-out, width ${motionDurationSlow} ease-in-out`,
|
||||
height: lineWidthBold,
|
||||
backgroundColor: colorPrimary,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook('Anchor', (token) => {
|
||||
const { fontSize, fontSizeLG, padding, paddingXXS } = token;
|
||||
@ -115,5 +169,5 @@ export default genComponentStyleHook('Anchor', (token) => {
|
||||
anchorTitleBlock: (fontSize / 14) * 3,
|
||||
anchorBallSize: fontSizeLG / 2,
|
||||
});
|
||||
return [genSharedAnchorStyle(anchorToken)];
|
||||
return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)];
|
||||
});
|
||||
|
@ -1,17 +1,18 @@
|
||||
import React, { useContext } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style';
|
||||
import useMessage from '../message/useMessage';
|
||||
import useNotification from '../notification/useNotification';
|
||||
import useModal from '../modal/useModal';
|
||||
import AppContext from './context';
|
||||
import useNotification from '../notification/useNotification';
|
||||
import type { useAppProps } from './context';
|
||||
import AppContext from './context';
|
||||
import useStyle from './style';
|
||||
|
||||
export type AppProps = {
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
prefixCls?: string;
|
||||
children?: ReactNode;
|
||||
};
|
||||
@ -19,11 +20,11 @@ export type AppProps = {
|
||||
const useApp = () => React.useContext<useAppProps>(AppContext);
|
||||
|
||||
const App: React.FC<AppProps> & { useApp: () => useAppProps } = (props) => {
|
||||
const { prefixCls: customizePrefixCls, children, className } = props;
|
||||
const { prefixCls: customizePrefixCls, children, className, rootClassName } = props;
|
||||
const { getPrefixCls } = useContext<ConfigConsumerProps>(ConfigContext);
|
||||
const prefixCls = getPrefixCls('app', customizePrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
const customClassName = classNames(hashId, prefixCls, className);
|
||||
const customClassName = classNames(hashId, prefixCls, className, rootClassName);
|
||||
|
||||
const [messageApi, messageContextHolder] = useMessage();
|
||||
const [notificationApi, notificationContextHolder] = useNotification();
|
||||
|
@ -27,8 +27,8 @@ demo:
|
||||
App 组件通过 `Context` 提供上下文方法调用,因而 useApp 需要作为子组件才能使用,我们推荐在应用中顶层包裹 App。
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { App } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const MyPage: React.FC = () => {
|
||||
const { message, notification, modal } = App.useApp();
|
||||
@ -78,11 +78,10 @@ App 组件只能在 `ConfigProvider` 之下才能使用 Design Token, 如果
|
||||
|
||||
```tsx
|
||||
// Entry component
|
||||
import React, { useEffect } from 'react';
|
||||
import { App } from 'antd';
|
||||
import type { MessageInstance } from 'antd/es/message/interface';
|
||||
import type { NotificationInstance } from 'antd/es/notification/interface';
|
||||
import type { ModalStaticFunctions } from 'antd/es/modal/confirm';
|
||||
import type { NotificationInstance } from 'antd/es/notification/interface';
|
||||
|
||||
let message: MessageInstance;
|
||||
let notification: NotificationInstance;
|
||||
@ -101,9 +100,9 @@ export { message, notification, modal };
|
||||
|
||||
```tsx
|
||||
// sub page
|
||||
import React from 'react';
|
||||
import { Button, Space } from 'antd';
|
||||
import { message, modal, notification } from './store';
|
||||
import React from 'react';
|
||||
import { message } from './store';
|
||||
|
||||
export default () => {
|
||||
const showMessage = () => {
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('avatar');
|
17
components/avatar/__tests__/demo.test.tsx
Normal file
17
components/avatar/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('avatar');
|
||||
|
||||
rootPropsTest(
|
||||
'avatar',
|
||||
(Avatar, props) => (
|
||||
<Avatar.Group {...props} maxCount={1}>
|
||||
<Avatar>Bamboo</Avatar>
|
||||
<Avatar>Light</Avatar>
|
||||
</Avatar.Group>
|
||||
),
|
||||
{
|
||||
name: 'Avatar.Group',
|
||||
},
|
||||
);
|
@ -30,6 +30,7 @@ export interface AvatarProps {
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
alt?: string;
|
||||
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
||||
@ -99,6 +100,7 @@ const InternalAvatar: React.ForwardRefRenderFunction<HTMLSpanElement, AvatarProp
|
||||
srcSet,
|
||||
icon,
|
||||
className,
|
||||
rootClassName,
|
||||
alt,
|
||||
draggable,
|
||||
children,
|
||||
@ -155,6 +157,7 @@ const InternalAvatar: React.ForwardRefRenderFunction<HTMLSpanElement, AvatarProp
|
||||
[`${prefixCls}-icon`]: !!icon,
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
|
@ -11,6 +11,7 @@ import useStyle from './style';
|
||||
|
||||
export interface GroupProps {
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
@ -27,7 +28,14 @@ export interface GroupProps {
|
||||
|
||||
const Group: React.FC<GroupProps> = (props) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const { prefixCls: customizePrefixCls, className = '', maxCount, maxStyle, size } = props;
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
maxCount,
|
||||
maxStyle,
|
||||
size,
|
||||
} = props;
|
||||
|
||||
const prefixCls = getPrefixCls('avatar', customizePrefixCls);
|
||||
const groupPrefixCls = `${prefixCls}-group`;
|
||||
@ -39,6 +47,7 @@ const Group: React.FC<GroupProps> = (props) => {
|
||||
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
|
@ -19,6 +19,7 @@ export interface BackTopProps {
|
||||
prefixCls?: string;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
duration?: number;
|
||||
}
|
||||
@ -26,7 +27,8 @@ export interface BackTopProps {
|
||||
const BackTop: React.FC<BackTopProps> = (props) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
className,
|
||||
rootClassName,
|
||||
visibilityHeight = 400,
|
||||
target,
|
||||
onClick,
|
||||
@ -79,12 +81,14 @@ const BackTop: React.FC<BackTopProps> = (props) => {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
// fix https://fb.me/react-unknown-prop
|
||||
const divProps = omit(props, [
|
||||
'prefixCls',
|
||||
'className',
|
||||
'rootClassName',
|
||||
'children',
|
||||
'visibilityHeight',
|
||||
'target',
|
||||
|
@ -3,14 +3,14 @@ import CSSMotion from 'rc-motion';
|
||||
import * as React from 'react';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { PresetColorKey } from '../theme/internal';
|
||||
import type { PresetStatusColorType } from '../_util/colors';
|
||||
import { isPresetColor } from '../_util/colors';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
import Ribbon from './Ribbon';
|
||||
import ScrollNumber from './ScrollNumber';
|
||||
import useStyle from './style';
|
||||
import { isPresetColor } from '../_util/colors';
|
||||
import type { PresetColorKey } from '../theme/internal';
|
||||
|
||||
export type { ScrollNumberProps } from './ScrollNumber';
|
||||
|
||||
@ -30,6 +30,7 @@ export interface BadgeProps {
|
||||
prefixCls?: string;
|
||||
scrollNumberPrefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
status?: PresetStatusColorType;
|
||||
color?: LiteralUnion<PresetColorKey>;
|
||||
text?: React.ReactNode;
|
||||
@ -54,6 +55,7 @@ const Badge: CompoundedComponent = ({
|
||||
offset,
|
||||
style,
|
||||
className,
|
||||
rootClassName,
|
||||
showZero = false,
|
||||
...restProps
|
||||
}) => {
|
||||
@ -169,13 +171,15 @@ const Badge: CompoundedComponent = ({
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
// <Badge status="success" />
|
||||
if (!children && hasStatus) {
|
||||
const statusTextColor = mergedStyle.color;
|
||||
return wrapSSR(
|
||||
<span {...restProps} className={classNames(badgeClassName, hashId)} style={mergedStyle}>
|
||||
<span {...restProps} className={badgeClassName} style={mergedStyle}>
|
||||
<span className={statusCls} style={statusStyle} />
|
||||
{text && (
|
||||
<span style={{ color: statusTextColor }} className={`${prefixCls}-status-text`}>
|
||||
@ -186,9 +190,8 @@ const Badge: CompoundedComponent = ({
|
||||
);
|
||||
}
|
||||
|
||||
// <Badge status="success" count={<Icon type="xxx" />}></Badge>
|
||||
return wrapSSR(
|
||||
<span {...restProps} className={classNames(badgeClassName, hashId)}>
|
||||
<span {...restProps} className={badgeClassName}>
|
||||
{children}
|
||||
<CSSMotion
|
||||
visible={!isHidden}
|
||||
|
@ -31,6 +31,7 @@ export interface BreadcrumbProps {
|
||||
) => React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
@ -79,6 +80,7 @@ const Breadcrumb: CompoundedComponent = ({
|
||||
separator = '/',
|
||||
style,
|
||||
className,
|
||||
rootClassName,
|
||||
routes,
|
||||
children,
|
||||
itemRender = defaultItemRender,
|
||||
@ -152,6 +154,7 @@ const Breadcrumb: CompoundedComponent = ({
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
|
@ -34,6 +34,7 @@ export interface BaseButtonProps {
|
||||
loading?: boolean | { delay?: number };
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
ghost?: boolean;
|
||||
danger?: boolean;
|
||||
block?: boolean;
|
||||
@ -78,6 +79,7 @@ const InternalButton: React.ForwardRefRenderFunction<
|
||||
size: customizeSize,
|
||||
disabled: customDisabled,
|
||||
className,
|
||||
rootClassName,
|
||||
children,
|
||||
icon,
|
||||
ghost = false,
|
||||
@ -198,6 +200,7 @@ const InternalButton: React.ForwardRefRenderFunction<
|
||||
},
|
||||
compactItemClassnames,
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
const iconNode =
|
||||
|
@ -1,3 +1,8 @@
|
||||
import dayjs from 'dayjs';
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('calendar');
|
||||
demoTest('calendar', {
|
||||
testRootProps: {
|
||||
value: dayjs(),
|
||||
},
|
||||
});
|
||||
|
@ -45,6 +45,7 @@ export type HeaderRender<DateType> = (config: {
|
||||
export interface CalendarProps<DateType> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
locale?: typeof enUS;
|
||||
validRange?: [DateType, DateType];
|
||||
@ -84,6 +85,7 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
style,
|
||||
dateFullCellRender,
|
||||
dateCellRender,
|
||||
@ -246,6 +248,7 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
[`${calendarPrefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
)}
|
||||
style={style}
|
||||
|
@ -32,6 +32,7 @@ export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 't
|
||||
children?: React.ReactNode;
|
||||
id?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
size?: CardSize;
|
||||
type?: CardType;
|
||||
cover?: React.ReactNode;
|
||||
@ -75,6 +76,7 @@ const Card = React.forwardRef((props: CardProps, ref: React.Ref<HTMLDivElement>)
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
extra,
|
||||
headStyle = {},
|
||||
bodyStyle = {},
|
||||
@ -164,6 +166,7 @@ const Card = React.forwardRef((props: CardProps, ref: React.Ref<HTMLDivElement>)
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
|
@ -228,7 +228,7 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
|
||||
cardShadow,
|
||||
cardHeadPadding,
|
||||
colorBorderSecondary,
|
||||
boxShadow,
|
||||
boxShadowTertiary,
|
||||
cardPaddingBase,
|
||||
} = token;
|
||||
|
||||
@ -241,7 +241,7 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
|
||||
borderRadius: token.borderRadiusLG,
|
||||
|
||||
[`&:not(${componentCls}-bordered)`]: {
|
||||
boxShadow,
|
||||
boxShadow: boxShadowTertiary,
|
||||
},
|
||||
|
||||
[`${componentCls}-head`]: genCardHeadStyle(token),
|
||||
|
@ -13,6 +13,7 @@ export interface CarouselProps extends Omit<Settings, 'dots' | 'dotsClass'> {
|
||||
effect?: CarouselEffect;
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
rootClassName?: string;
|
||||
slickGoTo?: number;
|
||||
dotPosition?: DotPosition;
|
||||
children?: React.ReactNode;
|
||||
@ -35,6 +36,7 @@ const Carousel = React.forwardRef<CarouselRef, CarouselProps>(
|
||||
draggable = false,
|
||||
dotPosition = 'bottom',
|
||||
vertical = dotPosition === 'left' || dotPosition === 'right',
|
||||
rootClassName,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@ -95,6 +97,7 @@ const Carousel = React.forwardRef<CarouselRef, CarouselProps>(
|
||||
[`${prefixCls}-vertical`]: newProps.vertical,
|
||||
},
|
||||
hashId,
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
return wrapSSR(
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('cascader');
|
11
components/cascader/__tests__/demo.test.tsx
Normal file
11
components/cascader/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('cascader', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest('cascader', (Cascader, props) => <Cascader {...props} />, {
|
||||
findRootElements: () => document.querySelectorAll('.ant-cascader, .ant-cascader-dropdown'),
|
||||
expectCount: 2,
|
||||
});
|
@ -114,6 +114,7 @@ export type CascaderProps<DataNodeType> = UnionCascaderProps & {
|
||||
options?: DataNodeType[];
|
||||
status?: InputStatus;
|
||||
|
||||
rootClassName?: string;
|
||||
popupClassName?: string;
|
||||
/** @deprecated Please use `popupClassName` instead */
|
||||
dropdownClassName?: string;
|
||||
@ -130,6 +131,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
size: customizeSize,
|
||||
disabled: customDisabled,
|
||||
className,
|
||||
rootClassName,
|
||||
multiple,
|
||||
bordered = true,
|
||||
transitionName,
|
||||
@ -207,6 +209,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
{
|
||||
[`${cascaderPrefixCls}-dropdown-rtl`]: mergedDirection === 'rtl',
|
||||
},
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
@ -291,6 +294,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
getStatusClassNames(prefixCls, mergedStatus, hasFeedback),
|
||||
compactItemClassnames,
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
)}
|
||||
disabled={mergedDisabled}
|
||||
|
@ -12,6 +12,7 @@ import useStyle from './style';
|
||||
export interface AbstractCheckboxProps<T> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
defaultChecked?: boolean;
|
||||
checked?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
@ -51,6 +52,7 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
|
||||
{
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
children,
|
||||
indeterminate = false,
|
||||
style,
|
||||
@ -116,6 +118,7 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
|
||||
[`${prefixCls}-wrapper-in-form-item`]: isFormItemInput,
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
const checkboxClass = classNames(
|
||||
|
@ -20,6 +20,7 @@ export interface CheckboxOptionType {
|
||||
export interface AbstractCheckboxGroupProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
options?: Array<CheckboxOptionType | string | number>;
|
||||
disabled?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
@ -51,6 +52,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
|
||||
options = [],
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
style,
|
||||
onChange,
|
||||
...restProps
|
||||
@ -152,6 +154,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
|
||||
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
return wrapSSR(
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('checkbox');
|
12
components/checkbox/__tests__/demo.test.tsx
Normal file
12
components/checkbox/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('checkbox');
|
||||
|
||||
rootPropsTest(
|
||||
'checkbox',
|
||||
(Checkbox, props) => <Checkbox.Group {...props} value={[]} options={['Bamboo']} />,
|
||||
{
|
||||
name: 'Checkbox.Group',
|
||||
},
|
||||
);
|
@ -11,6 +11,8 @@ import initCollapseMotion from '../_util/motion';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import warning from '../_util/warning';
|
||||
import type { CollapsibleType } from './CollapsePanel';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import CollapsePanel from './CollapsePanel';
|
||||
|
||||
import useStyle from './style';
|
||||
@ -28,11 +30,13 @@ export interface CollapseProps {
|
||||
onChange?: (key: string | string[]) => void;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
bordered?: boolean;
|
||||
prefixCls?: string;
|
||||
expandIcon?: (panelProps: PanelProps) => React.ReactNode;
|
||||
expandIconPosition?: ExpandIconPosition;
|
||||
ghost?: boolean;
|
||||
size?: SizeType;
|
||||
collapsible?: CollapsibleType;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
@ -52,13 +56,19 @@ interface PanelProps {
|
||||
|
||||
const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const size = React.useContext(SizeContext);
|
||||
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
className,
|
||||
rootClassName,
|
||||
bordered = true,
|
||||
ghost,
|
||||
size: customizeSize,
|
||||
expandIconPosition = 'start',
|
||||
} = props;
|
||||
|
||||
const mergedSize = customizeSize || size || 'middle';
|
||||
const prefixCls = getPrefixCls('collapse', customizePrefixCls);
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
@ -99,8 +109,10 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-ghost`]: !!ghost,
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize !== 'middle',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
);
|
||||
const openMotion: CSSMotionProps = {
|
||||
@ -130,7 +142,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
|
||||
<RcCollapse
|
||||
ref={ref}
|
||||
openMotion={openMotion}
|
||||
{...props}
|
||||
{...omit(props, ['rootClassName'])}
|
||||
expandIcon={renderExpandIcon}
|
||||
prefixCls={prefixCls}
|
||||
className={collapseClassName}
|
||||
|
@ -1438,3 +1438,170 @@ exports[`renders ./components/collapse/demo/noarrow.tsx extend context correctly
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/collapse/demo/size.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Default Size
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-start"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="ant-collapse-header-text"
|
||||
>
|
||||
This is default size panel header
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Small Size
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-start ant-collapse-small"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="ant-collapse-header-text"
|
||||
>
|
||||
This is small size panel header
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Large Size
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-start ant-collapse-large"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="ant-collapse-header-text"
|
||||
>
|
||||
This is large size panel header
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
@ -1356,3 +1356,170 @@ exports[`renders ./components/collapse/demo/noarrow.tsx correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/collapse/demo/size.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Default Size
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-start"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="ant-collapse-header-text"
|
||||
>
|
||||
This is default size panel header
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Small Size
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-start ant-collapse-small"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="ant-collapse-header-text"
|
||||
>
|
||||
This is small size panel header
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Large Size
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-start ant-collapse-large"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="ant-collapse-header-text"
|
||||
>
|
||||
This is large size panel header
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
@ -39,6 +39,14 @@ describe('Collapse', () => {
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should be able to config size', () => {
|
||||
const { container: small } = render(<Collapse size="small" />);
|
||||
const { container: large } = render(<Collapse size="large" />);
|
||||
|
||||
expect(small.querySelector('.ant-collapse')).toHaveClass('ant-collapse-small');
|
||||
expect(large.querySelector('.ant-collapse')).toHaveClass('ant-collapse-large');
|
||||
});
|
||||
|
||||
it('should keep the className of the expandIcon', () => {
|
||||
const { container } = render(
|
||||
<Collapse
|
||||
|
11
components/collapse/demo/size.md
Normal file
11
components/collapse/demo/size.md
Normal file
@ -0,0 +1,11 @@
|
||||
## zh-CN
|
||||
|
||||
折叠面板有大、中、小三种尺寸。
|
||||
|
||||
通过设置 `size` 为 `large` `small` 分别把折叠面板设为大、小尺寸。若不设置 `size`,则尺寸为中。
|
||||
|
||||
## en-US
|
||||
|
||||
Ant Design supports a default collapse size as well as a large and small size.
|
||||
|
||||
If a large or small collapse is desired, set the `size` property to either `large` or `small` respectively. Omit the `size` property for a collapse with the default size.
|
35
components/collapse/demo/size.tsx
Normal file
35
components/collapse/demo/size.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Collapse, Divider } from 'antd';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
||||
const text = `
|
||||
A dog is a type of domesticated animal.
|
||||
Known for its loyalty and faithfulness,
|
||||
it can be found as a welcome guest in many households across the world.
|
||||
`;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<Divider orientation="left">Default Size</Divider>
|
||||
<Collapse>
|
||||
<Panel header="This is default size panel header" key="1">
|
||||
<p>{text}</p>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
<Divider orientation="left">Small Size</Divider>
|
||||
<Collapse size="small">
|
||||
<Panel header="This is small size panel header" key="1">
|
||||
<p>{text}</p>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
<Divider orientation="left">Large Size</Divider>
|
||||
<Collapse size="large">
|
||||
<Panel header="This is large size panel header" key="1">
|
||||
<p>{text}</p>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
@ -16,6 +16,7 @@ A content area which can be collapsed and expanded.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx">Collapse</code>
|
||||
<code src="./demo/size.tsx">Size</code>
|
||||
<code src="./demo/accordion.tsx">Accordion</code>
|
||||
<code src="./demo/mix.tsx">Nested panel</code>
|
||||
<code src="./demo/borderless.tsx">Borderless</code>
|
||||
@ -40,6 +41,7 @@ A content area which can be collapsed and expanded.
|
||||
| expandIcon | Allow to customize collapse icon | (panelProps) => ReactNode | - | |
|
||||
| expandIconPosition | Set expand icon position | `start` \| `end` | - | 4.21.0 |
|
||||
| ghost | Make the collapse borderless and its background transparent | boolean | false | 4.4.0 |
|
||||
| size | Set the size of collapse | `large` \| `middle` \| `small` | `middle` | 5.2.0 |
|
||||
| onChange | Callback function executed when active panel is changed | function | - | |
|
||||
|
||||
### Collapse.Panel
|
||||
|
@ -17,6 +17,7 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*B7HKR5OBe8gAAAAAAA
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx">折叠面板</code>
|
||||
<code src="./demo/size.tsx">面板尺寸</code>
|
||||
<code src="./demo/accordion.tsx">手风琴</code>
|
||||
<code src="./demo/mix.tsx">面板嵌套</code>
|
||||
<code src="./demo/borderless.tsx">简洁风格</code>
|
||||
@ -41,6 +42,7 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*B7HKR5OBe8gAAAAAAA
|
||||
| expandIcon | 自定义切换图标 | (panelProps) => ReactNode | - | |
|
||||
| expandIconPosition | 设置图标位置 | `start` \| `end` | - | 4.21.0 |
|
||||
| ghost | 使折叠面板透明且无边框 | boolean | false | 4.4.0 |
|
||||
| size | 设置折叠面板大小 | `large` \| `middle` \| `small` | `middle` | 5.2.0 |
|
||||
| onChange | 切换面板的回调 | function | - | |
|
||||
|
||||
### Collapse.Panel
|
||||
|
@ -9,6 +9,8 @@ type CollapseToken = FullToken<'Collapse'> & {
|
||||
collapseContentBg: string;
|
||||
collapseHeaderBg: string;
|
||||
collapseHeaderPadding: string;
|
||||
collapseHeaderPaddingSM: string;
|
||||
collapseHeaderPaddingLG: string;
|
||||
collapsePanelBorderRadius: number;
|
||||
collapseContentPaddingHorizontal: number;
|
||||
};
|
||||
@ -21,6 +23,8 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
|
||||
collapseContentPaddingHorizontal,
|
||||
collapseHeaderBg,
|
||||
collapseHeaderPadding,
|
||||
collapseHeaderPaddingSM,
|
||||
collapseHeaderPaddingLG,
|
||||
collapsePanelBorderRadius,
|
||||
|
||||
lineWidth,
|
||||
@ -30,9 +34,11 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
|
||||
colorTextHeading,
|
||||
colorTextDisabled,
|
||||
fontSize,
|
||||
fontSizeLG,
|
||||
lineHeight,
|
||||
marginSM,
|
||||
paddingSM,
|
||||
paddingLG,
|
||||
motionDurationSlow,
|
||||
fontSizeIcon,
|
||||
} = token;
|
||||
@ -141,6 +147,34 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
|
||||
},
|
||||
},
|
||||
|
||||
[`&-small`]: {
|
||||
[`> ${componentCls}-item`]: {
|
||||
[`> ${componentCls}-header`]: {
|
||||
padding: collapseHeaderPaddingSM,
|
||||
},
|
||||
[`> ${componentCls}-content > ${componentCls}-content-box`]: {
|
||||
padding: paddingSM,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`&-large`]: {
|
||||
[`> ${componentCls}-item`]: {
|
||||
fontSize: fontSizeLG,
|
||||
|
||||
[`> ${componentCls}-header`]: {
|
||||
padding: collapseHeaderPaddingLG,
|
||||
|
||||
[`> ${componentCls}-expand-icon`]: {
|
||||
height: fontSizeLG * lineHeight,
|
||||
},
|
||||
},
|
||||
[`> ${componentCls}-content > ${componentCls}-content-box`]: {
|
||||
padding: paddingLG,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-item:last-child`]: {
|
||||
[`> ${componentCls}-content`]: {
|
||||
borderRadius: `0 0 ${collapsePanelBorderRadius}px ${collapsePanelBorderRadius}px`,
|
||||
@ -254,6 +288,8 @@ export default genComponentStyleHook('Collapse', (token) => {
|
||||
collapseContentBg: token.colorBgContainer,
|
||||
collapseHeaderBg: token.colorFillAlter,
|
||||
collapseHeaderPadding: `${token.paddingSM}px ${token.padding}px`,
|
||||
collapseHeaderPaddingSM: `${token.paddingXS}px ${token.paddingSM}px`,
|
||||
collapseHeaderPaddingLG: `${token.padding}px ${token.paddingLG}px`,
|
||||
collapsePanelBorderRadius: token.borderRadiusLG,
|
||||
collapseContentPaddingHorizontal: 16, // Fixed value
|
||||
});
|
||||
|
@ -138,13 +138,9 @@ exports[`ConfigProvider components Anchor configProvider 1`] = `
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -174,13 +170,9 @@ exports[`ConfigProvider components Anchor configProvider componentDisabled 1`] =
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -210,13 +202,9 @@ exports[`ConfigProvider components Anchor configProvider componentSize large 1`]
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -246,13 +234,9 @@ exports[`ConfigProvider components Anchor configProvider componentSize middle 1`
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -282,13 +266,9 @@ exports[`ConfigProvider components Anchor configProvider virtual and dropdownMat
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -318,13 +298,9 @@ exports[`ConfigProvider components Anchor normal 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -354,13 +330,9 @@ exports[`ConfigProvider components Anchor prefixCls 1`] = `
|
||||
<div
|
||||
class="prefix-Anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="prefix-Anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="prefix-Anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="prefix-Anchor-link"
|
||||
>
|
||||
@ -12357,7 +12329,7 @@ exports[`ConfigProvider components Collapse configProvider componentDisabled 1`]
|
||||
|
||||
exports[`ConfigProvider components Collapse configProvider componentSize large 1`] = `
|
||||
<div
|
||||
class="config-collapse config-collapse-icon-position-start"
|
||||
class="config-collapse config-collapse-icon-position-start config-collapse-large"
|
||||
>
|
||||
<div
|
||||
class="config-collapse-item"
|
||||
|
@ -1,19 +1,30 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import Button from '../../button';
|
||||
import ConfigProvider from '..';
|
||||
import { render } from '../../../tests/utils';
|
||||
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
import type { FormInstance } from '../../form';
|
||||
import Form from '../../form';
|
||||
import Input from '../../input';
|
||||
import zhCN from '../../locale/zh_CN';
|
||||
|
||||
jest.mock('scroll-into-view-if-needed');
|
||||
|
||||
describe('ConfigProvider.Form', () => {
|
||||
(scrollIntoView as any).mockImplementation(() => {});
|
||||
|
||||
beforeEach(() => {
|
||||
(scrollIntoView as any).mockReset();
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
(scrollIntoView as any).mockRestore();
|
||||
});
|
||||
|
||||
describe('form validateMessages', () => {
|
||||
@ -146,4 +157,89 @@ describe('ConfigProvider.Form', () => {
|
||||
expect(container.querySelector('#input[disabled]')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('form scrollToFirstError', () => {
|
||||
it('set object, form not set', async () => {
|
||||
(scrollIntoView as any).mockImplementation(() => {});
|
||||
const onFinishFailed = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ConfigProvider form={{ scrollToFirstError: { block: 'center' } }}>
|
||||
<Form onFinishFailed={onFinishFailed}>
|
||||
<Form.Item name="test" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
expect(scrollIntoView).not.toHaveBeenCalled();
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
const inputNode = document.getElementById('test');
|
||||
expect(scrollIntoView).toHaveBeenCalledWith(inputNode, {
|
||||
block: 'center',
|
||||
scrollMode: 'if-needed',
|
||||
});
|
||||
expect(onFinishFailed).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('not set, form set object', async () => {
|
||||
(scrollIntoView as any).mockImplementation(() => {});
|
||||
const onFinishFailed = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ConfigProvider>
|
||||
<Form scrollToFirstError={{ block: 'center' }} onFinishFailed={onFinishFailed}>
|
||||
<Form.Item name="test" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
expect(scrollIntoView).not.toHaveBeenCalled();
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
const inputNode = document.getElementById('test');
|
||||
expect(scrollIntoView).toHaveBeenCalledWith(inputNode, {
|
||||
block: 'center',
|
||||
scrollMode: 'if-needed',
|
||||
});
|
||||
expect(onFinishFailed).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('set object, form set false', async () => {
|
||||
(scrollIntoView as any).mockImplementation(() => {});
|
||||
const onFinishFailed = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ConfigProvider form={{ scrollToFirstError: { block: 'center' } }}>
|
||||
<Form scrollToFirstError={false} onFinishFailed={onFinishFailed}>
|
||||
<Form.Item name="test" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
expect(scrollIntoView).not.toHaveBeenCalled();
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(scrollIntoView).not.toHaveBeenCalled();
|
||||
expect(onFinishFailed).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import type { DerivativeFunc } from '@ant-design/cssinjs';
|
||||
import type { Options } from 'scroll-into-view-if-needed';
|
||||
import type { RequiredMark } from '../form/Form';
|
||||
import type { Locale } from '../locale';
|
||||
import type { AliasToken, MapToken, OverrideToken, SeedToken } from '../theme/interface';
|
||||
@ -61,6 +62,7 @@ export interface ConfigConsumerProps {
|
||||
form?: {
|
||||
requiredMark?: RequiredMark;
|
||||
colon?: boolean;
|
||||
scrollToFirstError?: Options | boolean;
|
||||
};
|
||||
theme?: ThemeConfig;
|
||||
select?: {
|
||||
|
@ -54,7 +54,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p
|
||||
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - | |
|
||||
| direction | Set direction of layout. See [demo](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
|
||||
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 4.3.0 |
|
||||
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional` } | - | requiredMark: 4.8.0 |
|
||||
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0 |
|
||||
| getPopupContainer | To set the container of the popup element. The default is to create a `div` element in `body` | function(triggerNode) | () => document.body | |
|
||||
| getTargetContainer | Config Affix, Anchor scroll target container | () => HTMLElement | () => window | 4.2.0 |
|
||||
| iconPrefixCls | Set icon prefix className | string | `anticon` | 4.11.0 |
|
||||
|
@ -5,6 +5,7 @@ import type { ValidateMessages } from 'rc-field-form/lib/interface';
|
||||
import useMemo from 'rc-util/lib/hooks/useMemo';
|
||||
import * as React from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { Options } from 'scroll-into-view-if-needed';
|
||||
import type { RequiredMark } from '../form/Form';
|
||||
import type { Locale } from '../locale';
|
||||
import LocaleProvider, { ANT_MARK } from '../locale';
|
||||
@ -70,6 +71,7 @@ export interface ConfigProviderProps {
|
||||
validateMessages?: ValidateMessages;
|
||||
requiredMark?: RequiredMark;
|
||||
colon?: boolean;
|
||||
scrollToFirstError?: Options | boolean;
|
||||
};
|
||||
input?: {
|
||||
autoComplete?: string;
|
||||
|
@ -55,7 +55,7 @@ export default Demo;
|
||||
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - | |
|
||||
| direction | 设置文本展示方向。 [示例](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
|
||||
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | 4.3.0 |
|
||||
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean} | - | requiredMark: 4.8.0; colon: 4.18.0 |
|
||||
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0 |
|
||||
| getPopupContainer | 弹出框(Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | function(triggerNode) | () => document.body | |
|
||||
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | () => HTMLElement | () => window | 4.2.0 |
|
||||
| iconPrefixCls | 设置图标统一样式前缀 | string | `anticon` | 4.11.0 |
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('date-picker', { skip: ['locale.tsx'] });
|
10
components/date-picker/__tests__/demo.test.tsx
Normal file
10
components/date-picker/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import dayjs from 'dayjs';
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('date-picker', { skip: ['locale.tsx'], testRootProps: false });
|
||||
|
||||
rootPropsTest('time-picker', (DatePicker, props) => <DatePicker {...props} value={dayjs()} />, {
|
||||
findRootElements: () => document.querySelectorAll('.ant-picker, .ant-picker-dropdown'),
|
||||
expectCount: 2,
|
||||
});
|
@ -7,7 +7,6 @@ import type { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import type { PickerMode } from 'rc-picker/lib/interface';
|
||||
import * as React from 'react';
|
||||
import { forwardRef, useContext, useImperativeHandle } from 'react';
|
||||
import { useCompactItemContext } from '../../space/Compact';
|
||||
import type { PickerProps, PickerTimeProps } from '.';
|
||||
import { Components, getTimeProps } from '.';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
@ -15,6 +14,7 @@ import DisabledContext from '../../config-provider/DisabledContext';
|
||||
import SizeContext from '../../config-provider/SizeContext';
|
||||
import { FormItemInputContext } from '../../form/context';
|
||||
import LocaleReceiver from '../../locale/LocaleReceiver';
|
||||
import { useCompactItemContext } from '../../space/Compact';
|
||||
import type { InputStatus } from '../../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
|
||||
import warning from '../../_util/warning';
|
||||
@ -29,6 +29,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
status?: InputStatus;
|
||||
hashId?: string;
|
||||
popupClassName?: string;
|
||||
rootClassName?: string;
|
||||
};
|
||||
type DatePickerProps = PickerProps<DateType> & CustomPickerProps;
|
||||
type TimePickerProps = PickerTimeProps<DateType> & CustomPickerProps;
|
||||
@ -43,6 +44,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
prefixCls: customizePrefixCls,
|
||||
getPopupContainer: customizeGetPopupContainer,
|
||||
className,
|
||||
rootClassName,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
placement,
|
||||
@ -155,6 +157,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
hashId,
|
||||
compactItemClassnames,
|
||||
className,
|
||||
rootClassName,
|
||||
)}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
|
||||
@ -162,7 +165,11 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
components={Components}
|
||||
direction={direction}
|
||||
disabled={mergedDisabled}
|
||||
dropdownClassName={classNames(hashId, popupClassName || dropdownClassName)}
|
||||
dropdownClassName={classNames(
|
||||
hashId,
|
||||
rootClassName,
|
||||
popupClassName || dropdownClassName,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -101,6 +101,7 @@ function getRows(children: React.ReactNode, column: number) {
|
||||
export interface DescriptionsProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
bordered?: boolean;
|
||||
size?: 'middle' | 'small' | 'default';
|
||||
@ -124,6 +125,7 @@ function Descriptions({
|
||||
layout,
|
||||
children,
|
||||
className,
|
||||
rootClassName,
|
||||
style,
|
||||
size,
|
||||
labelStyle,
|
||||
@ -169,6 +171,7 @@ function Descriptions({
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
hashId,
|
||||
)}
|
||||
style={style}
|
||||
|
@ -40,6 +40,7 @@ const genBorderedStyle = (token: DescriptionsToken): CSSObject => {
|
||||
},
|
||||
},
|
||||
[`${componentCls}-item-label`]: {
|
||||
color: token.colorTextSecondary,
|
||||
backgroundColor: descriptionsBg,
|
||||
'&::after': {
|
||||
display: 'none',
|
||||
@ -116,7 +117,7 @@ const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: Descripti
|
||||
},
|
||||
},
|
||||
[`${componentCls}-item-label`]: {
|
||||
color: token.colorText,
|
||||
color: token.colorTextTertiary,
|
||||
fontWeight: 'normal',
|
||||
fontSize: token.fontSize,
|
||||
lineHeight: token.lineHeight,
|
||||
|
@ -11,6 +11,7 @@ export interface DividerProps {
|
||||
orientation?: 'left' | 'right' | 'center';
|
||||
orientationMargin?: string | number;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
dashed?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
@ -26,6 +27,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
orientation = 'center',
|
||||
orientationMargin,
|
||||
className,
|
||||
rootClassName,
|
||||
children,
|
||||
dashed,
|
||||
plain,
|
||||
@ -52,6 +54,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
[`${prefixCls}-no-default-orientation-margin-right`]: hasCustomMarginRight,
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
const innerStyle = {
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('dropdown');
|
35
components/dropdown/__tests__/demo.test.tsx
Normal file
35
components/dropdown/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('dropdown', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest(
|
||||
'dropdown',
|
||||
(Dropdown, props) => (
|
||||
<Dropdown
|
||||
{...props}
|
||||
menu={{
|
||||
openKeys: ['1'],
|
||||
items: [
|
||||
{
|
||||
key: '1',
|
||||
label: 'parent',
|
||||
children: [
|
||||
{
|
||||
key: '2',
|
||||
label: 'child',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<a />
|
||||
</Dropdown>
|
||||
),
|
||||
{
|
||||
findRootElements: () => document.querySelector('.ant-dropdown')!,
|
||||
},
|
||||
);
|
@ -3,18 +3,21 @@ import classNames from 'classnames';
|
||||
import RcDropdown from 'rc-dropdown';
|
||||
import useEvent from 'rc-util/lib/hooks/useEvent';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
import Menu from '../menu';
|
||||
import type { MenuProps } from '../menu';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { MenuProps } from '../menu';
|
||||
import Menu from '../menu';
|
||||
import { OverrideProvider } from '../menu/OverrideContext';
|
||||
import genPurePanel from '../_util/PurePanel';
|
||||
import { NoCompactStyle } from '../space/Compact';
|
||||
import type { AdjustOverflow } from '../_util/placements';
|
||||
import getPlacements from '../_util/placements';
|
||||
import genPurePanel from '../_util/PurePanel';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import warning from '../_util/warning';
|
||||
import { NoCompactStyle } from '../space/Compact';
|
||||
import DropdownButton from './dropdown-button';
|
||||
import useStyle from './style';
|
||||
import theme from '../theme';
|
||||
|
||||
const Placements = [
|
||||
'topLeft',
|
||||
@ -62,6 +65,7 @@ export interface DropdownProps {
|
||||
getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
transitionName?: string;
|
||||
placement?: Placement;
|
||||
overlayClassName?: string;
|
||||
@ -71,6 +75,7 @@ export interface DropdownProps {
|
||||
mouseLeaveDelay?: number;
|
||||
openClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
autoAdjustOverflow?: boolean | AdjustOverflow;
|
||||
|
||||
// Deprecated
|
||||
/** @deprecated Please use `menu` instead */
|
||||
@ -154,6 +159,7 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
dropdownRender,
|
||||
getPopupContainer,
|
||||
overlayClassName,
|
||||
rootClassName,
|
||||
open,
|
||||
onOpenChange,
|
||||
|
||||
@ -162,6 +168,7 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
onVisibleChange,
|
||||
mouseEnterDelay = 0.15,
|
||||
mouseLeaveDelay = 0.1,
|
||||
autoAdjustOverflow = true,
|
||||
} = props;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@ -180,6 +187,8 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const { token } = theme.useToken();
|
||||
|
||||
const child = React.Children.only(children) as React.ReactElement<any>;
|
||||
|
||||
const dropdownTrigger = cloneElement(child, {
|
||||
@ -211,13 +220,15 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
});
|
||||
|
||||
// =========================== Overlay ============================
|
||||
const overlayClassNameCustomized = classNames(overlayClassName, hashId, {
|
||||
const overlayClassNameCustomized = classNames(overlayClassName, rootClassName, hashId, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
const builtinPlacements = getPlacements({
|
||||
arrowPointAtCenter: typeof arrow === 'object' && arrow.pointAtCenter,
|
||||
autoAdjustOverflow: true,
|
||||
autoAdjustOverflow,
|
||||
offset: token.marginXXS,
|
||||
arrowWidth: arrow ? token.sizePopupArrow : 0,
|
||||
});
|
||||
|
||||
const onMenuClick = React.useCallback(() => {
|
||||
@ -273,7 +284,7 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
return wrapSSR(
|
||||
<RcDropdown
|
||||
alignPoint={alignPoint!}
|
||||
{...props}
|
||||
{...omit(props, ['rootClassName'])}
|
||||
mouseEnterDelay={mouseEnterDelay}
|
||||
mouseLeaveDelay={mouseLeaveDelay}
|
||||
visible={mergedOpen}
|
||||
|
@ -40,6 +40,7 @@ When there are more than a few options to choose from, you can wrap them in a `D
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| arrow | Whether the dropdown arrow should be visible | boolean \| { pointAtCenter: boolean } | false | |
|
||||
| autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true | 5.2.0 |
|
||||
| autoFocus | Focus element in `overlay` when opened | boolean | false | 4.21.0 |
|
||||
| disabled | Whether the dropdown menu is disabled | boolean | - | |
|
||||
| destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false | |
|
||||
|
@ -44,6 +44,7 @@ demo:
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | |
|
||||
| autoAdjustOverflow | 下拉框被遮挡时自动调整位置 | boolean | true | 5.2.0 |
|
||||
| autoFocus | 打开后自动聚焦下拉框 | boolean | false | 4.21.0 |
|
||||
| disabled | 菜单是否禁用 | boolean | - | |
|
||||
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | |
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getArrowOffset } from '../../style/placementArrow';
|
||||
import { genFocusStyle, resetComponent } from '../../style';
|
||||
import {
|
||||
initMoveMotion,
|
||||
initSlideMotion,
|
||||
@ -8,11 +8,11 @@ import {
|
||||
slideUpIn,
|
||||
slideUpOut,
|
||||
} from '../../style/motion';
|
||||
import getArrowStyle, { getArrowOffset } from '../../style/placementArrow';
|
||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||
import genButtonStyle from './button';
|
||||
import genStatusStyle from './status';
|
||||
import { genFocusStyle, resetComponent, roundedArrow } from '../../style';
|
||||
|
||||
export interface ComponentToken {
|
||||
zIndexPopup: number;
|
||||
@ -34,7 +34,6 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
|
||||
menuCls,
|
||||
zIndexPopup,
|
||||
dropdownArrowDistance,
|
||||
dropdownArrowOffset,
|
||||
sizePopupArrow,
|
||||
antCls,
|
||||
iconCls,
|
||||
@ -46,7 +45,6 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
|
||||
fontSizeIcon,
|
||||
controlPaddingHorizontal,
|
||||
colorBgElevated,
|
||||
boxShadowPopoverArrow,
|
||||
} = token;
|
||||
|
||||
return [
|
||||
@ -99,103 +97,6 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
// =============================================================
|
||||
// == Arrow ==
|
||||
// =============================================================
|
||||
// Offset the popover to account for the dropdown arrow
|
||||
[`
|
||||
&-show-arrow${componentCls}-placement-topLeft,
|
||||
&-show-arrow${componentCls}-placement-top,
|
||||
&-show-arrow${componentCls}-placement-topRight
|
||||
`]: {
|
||||
paddingBottom: dropdownArrowDistance,
|
||||
},
|
||||
|
||||
[`
|
||||
&-show-arrow${componentCls}-placement-bottomLeft,
|
||||
&-show-arrow${componentCls}-placement-bottom,
|
||||
&-show-arrow${componentCls}-placement-bottomRight
|
||||
`]: {
|
||||
paddingTop: dropdownArrowDistance,
|
||||
},
|
||||
|
||||
// Note: .popover-arrow is outer, .popover-arrow:after is inner
|
||||
[`${componentCls}-arrow`]: {
|
||||
position: 'absolute',
|
||||
zIndex: 1, // lift it up so the menu wouldn't cask shadow on it
|
||||
display: 'block',
|
||||
|
||||
...roundedArrow(
|
||||
sizePopupArrow,
|
||||
token.borderRadiusXS,
|
||||
token.borderRadiusOuter,
|
||||
colorBgElevated,
|
||||
boxShadowPopoverArrow,
|
||||
),
|
||||
},
|
||||
|
||||
[`
|
||||
&-placement-top > ${componentCls}-arrow,
|
||||
&-placement-topLeft > ${componentCls}-arrow,
|
||||
&-placement-topRight > ${componentCls}-arrow
|
||||
`]: {
|
||||
bottom: dropdownArrowDistance,
|
||||
transform: 'translateY(100%) rotate(180deg)',
|
||||
},
|
||||
|
||||
[`&-placement-top > ${componentCls}-arrow`]: {
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: '50%',
|
||||
},
|
||||
transform: 'translateX(-50%) translateY(100%) rotate(180deg)',
|
||||
},
|
||||
|
||||
[`&-placement-topLeft > ${componentCls}-arrow`]: {
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: dropdownArrowOffset,
|
||||
},
|
||||
},
|
||||
|
||||
[`&-placement-topRight > ${componentCls}-arrow`]: {
|
||||
right: {
|
||||
_skip_check_: true,
|
||||
value: dropdownArrowOffset,
|
||||
},
|
||||
},
|
||||
|
||||
[`
|
||||
&-placement-bottom > ${componentCls}-arrow,
|
||||
&-placement-bottomLeft > ${componentCls}-arrow,
|
||||
&-placement-bottomRight > ${componentCls}-arrow
|
||||
`]: {
|
||||
top: dropdownArrowDistance,
|
||||
transform: `translateY(-100%)`,
|
||||
},
|
||||
|
||||
[`&-placement-bottom > ${componentCls}-arrow`]: {
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: '50%',
|
||||
},
|
||||
transform: `translateY(-100%) translateX(-50%)`,
|
||||
},
|
||||
|
||||
[`&-placement-bottomLeft > ${componentCls}-arrow`]: {
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: dropdownArrowOffset,
|
||||
},
|
||||
},
|
||||
|
||||
[`&-placement-bottomRight > ${componentCls}-arrow`]: {
|
||||
right: {
|
||||
_skip_check_: true,
|
||||
value: dropdownArrowOffset,
|
||||
},
|
||||
},
|
||||
|
||||
// =============================================================
|
||||
// == Motion ==
|
||||
// =============================================================
|
||||
@ -237,6 +138,15 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
|
||||
},
|
||||
},
|
||||
|
||||
// =============================================================
|
||||
// == Arrow style ==
|
||||
// =============================================================
|
||||
getArrowStyle<DropdownToken>(token, {
|
||||
colorBg: colorBgElevated,
|
||||
limitVerticalRadius: true,
|
||||
arrowPlacement: { top: true, bottom: true },
|
||||
}),
|
||||
|
||||
{
|
||||
// =============================================================
|
||||
// == Menu ==
|
||||
|
@ -17,6 +17,7 @@ export interface TransferLocale {
|
||||
export interface EmptyProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
/** @since 3.16.0 */
|
||||
imageStyle?: React.CSSProperties;
|
||||
@ -32,6 +33,7 @@ type CompoundedComponent = React.FC<EmptyProps> & {
|
||||
|
||||
const Empty: CompoundedComponent = ({
|
||||
className,
|
||||
rootClassName,
|
||||
prefixCls: customizePrefixCls,
|
||||
image = defaultEmptyImg,
|
||||
description,
|
||||
@ -68,6 +70,7 @@ const Empty: CompoundedComponent = ({
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
|
@ -15,7 +15,7 @@ import useStyle from './style';
|
||||
const BackTop: React.FC<BackTopProps> = (props) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
className,
|
||||
type = 'default',
|
||||
shape = 'circle',
|
||||
visibilityHeight = 400,
|
||||
|
@ -23,6 +23,7 @@ const FloatButton: React.ForwardRefRenderFunction<
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
type = 'default',
|
||||
shape = 'circle',
|
||||
icon,
|
||||
@ -41,6 +42,7 @@ const FloatButton: React.ForwardRefRenderFunction<
|
||||
hashId,
|
||||
prefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
`${prefixCls}-${type}`,
|
||||
`${prefixCls}-${mergeShape}`,
|
||||
{
|
||||
|
@ -13,6 +13,7 @@ export type FloatButtonGroupTrigger = 'click' | 'hover';
|
||||
export interface FloatButtonProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
icon?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
@ -51,6 +52,7 @@ export interface BackTopProps extends Omit<FloatButtonProps, 'target'> {
|
||||
prefixCls?: string;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
duration?: number;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames';
|
||||
import FieldForm, { List, useWatch } from 'rc-field-form';
|
||||
import type { FormProps as RcFormProps } from 'rc-field-form/lib/Form';
|
||||
import type { ValidateErrorEntity } from 'rc-field-form/lib/interface';
|
||||
import type { InternalNamePath, ValidateErrorEntity } from 'rc-field-form/lib/interface';
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import type { Options } from 'scroll-into-view-if-needed';
|
||||
@ -36,6 +36,7 @@ export interface FormProps<Values = any> extends Omit<RcFormProps<Values>, 'form
|
||||
requiredMark?: RequiredMark;
|
||||
/** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
|
||||
hideRequiredMark?: boolean;
|
||||
rootClassName?: string;
|
||||
}
|
||||
|
||||
const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (props, ref) => {
|
||||
@ -45,7 +46,8 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
|
||||
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
className,
|
||||
rootClassName,
|
||||
size = contextSize,
|
||||
disabled = contextDisabled,
|
||||
form,
|
||||
@ -96,6 +98,7 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
|
||||
},
|
||||
hashId,
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
const [wrapForm] = useForm(form);
|
||||
@ -120,16 +123,28 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
|
||||
|
||||
React.useImperativeHandle(ref, () => wrapForm);
|
||||
|
||||
const scrollToField = (options: boolean | Options, fieldName: InternalNamePath) => {
|
||||
if (options) {
|
||||
let defaultScrollToFirstError: Options = { block: 'nearest' };
|
||||
if (typeof options === 'object') {
|
||||
defaultScrollToFirstError = options;
|
||||
}
|
||||
wrapForm.scrollToField(fieldName, defaultScrollToFirstError);
|
||||
}
|
||||
};
|
||||
|
||||
const onInternalFinishFailed = (errorInfo: ValidateErrorEntity) => {
|
||||
onFinishFailed?.(errorInfo);
|
||||
|
||||
let defaultScrollToFirstError: Options = { block: 'nearest' };
|
||||
|
||||
if (scrollToFirstError && errorInfo.errorFields.length) {
|
||||
if (typeof scrollToFirstError === 'object') {
|
||||
defaultScrollToFirstError = scrollToFirstError;
|
||||
if (errorInfo.errorFields.length) {
|
||||
const fieldName = errorInfo.errorFields[0].name;
|
||||
if (scrollToFirstError !== undefined) {
|
||||
scrollToField(scrollToFirstError, fieldName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (contextForm && contextForm.scrollToFirstError !== undefined) {
|
||||
scrollToField(contextForm.scrollToFirstError, fieldName);
|
||||
}
|
||||
wrapForm.scrollToField(errorInfo.errorFields[0].name, defaultScrollToFirstError);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,17 +2,17 @@ import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import type { Meta } from 'rc-field-form/lib/interface';
|
||||
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
import type { FormItemProps, ValidateStatus } from '.';
|
||||
import { Row } from '../../grid';
|
||||
import FormItemLabel from '../FormItemLabel';
|
||||
import FormItemInput from '../FormItemInput';
|
||||
import type { FormItemStatusContextProps, ReportMetaChange } from '../context';
|
||||
import { FormContext, FormItemInputContext, NoStyleItemContext } from '../context';
|
||||
import type { FormItemProps, ValidateStatus } from '.';
|
||||
import FormItemInput from '../FormItemInput';
|
||||
import FormItemLabel from '../FormItemLabel';
|
||||
import useDebounce from '../hooks/useDebounce';
|
||||
|
||||
const iconMap = {
|
||||
@ -25,6 +25,7 @@ const iconMap = {
|
||||
export interface ItemHolderProps extends FormItemProps {
|
||||
prefixCls: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
errors: React.ReactNode[];
|
||||
warnings: React.ReactNode[];
|
||||
@ -39,6 +40,7 @@ export default function ItemHolder(props: ItemHolderProps) {
|
||||
const {
|
||||
prefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
style,
|
||||
help,
|
||||
errors,
|
||||
@ -117,10 +119,8 @@ export default function ItemHolder(props: ItemHolderProps) {
|
||||
}, [mergedValidateStatus, hasFeedback]);
|
||||
|
||||
// ======================== Render ========================
|
||||
const itemClassName = {
|
||||
[itemPrefixCls]: true,
|
||||
const itemClassName = classNames(itemPrefixCls, className, rootClassName, {
|
||||
[`${itemPrefixCls}-with-help`]: hasHelp || debounceErrors.length || debounceWarnings.length,
|
||||
[`${className}`]: !!className,
|
||||
|
||||
// Status
|
||||
[`${itemPrefixCls}-has-feedback`]: mergedValidateStatus && hasFeedback,
|
||||
@ -129,10 +129,10 @@ export default function ItemHolder(props: ItemHolderProps) {
|
||||
[`${itemPrefixCls}-has-error`]: mergedValidateStatus === 'error',
|
||||
[`${itemPrefixCls}-is-validating`]: mergedValidateStatus === 'validating',
|
||||
[`${itemPrefixCls}-hidden`]: hidden,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classNames(itemClassName)} style={style} ref={itemRef}>
|
||||
<div className={itemClassName} style={style} ref={itemRef}>
|
||||
<Row
|
||||
className={`${itemPrefixCls}-row`}
|
||||
{...omit(restProps, [
|
||||
|
@ -58,6 +58,7 @@ export interface FormItemProps<Values = any>
|
||||
noStyle?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children?: ChildrenType<Values>;
|
||||
id?: string;
|
||||
hasFeedback?: boolean;
|
||||
|
@ -9023,7 +9023,7 @@ exports[`renders ./components/form/demo/register.tsx extend context correctly 1`
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
>
|
||||
<textarea
|
||||
@ -9031,6 +9031,11 @@ exports[`renders ./components/form/demo/register.tsx extend context correctly 1`
|
||||
class="ant-input"
|
||||
id="register_intro"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26455,8 +26460,8 @@ exports[`renders ./components/form/demo/validate-static.tsx extend context corre
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions-affix-wrapper ant-mentions-affix-wrapper-status-error ant-mentions-affix-wrapper-has-feedback"
|
||||
<span
|
||||
class="ant-mentions-affix-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
@ -26493,7 +26498,7 @@ exports[`renders ./components/form/demo/validate-static.tsx extend context corre
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26525,62 +26530,76 @@ exports[`renders ./components/form/demo/validate-static.tsx extend context corre
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count ant-input-textarea-status-error ant-input-textarea-has-feedback"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-status-error ant-input-affix-wrapper-has-feedback"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input ant-input-status-error"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden ant-input-clear-icon-has-suffix"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-textarea-suffix"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-textarea-suffix"
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
0
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6122,7 +6122,7 @@ exports[`renders ./components/form/demo/register.tsx correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
>
|
||||
<textarea
|
||||
@ -6130,6 +6130,11 @@ exports[`renders ./components/form/demo/register.tsx correctly 1`] = `
|
||||
class="ant-input"
|
||||
id="register_intro"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -11050,8 +11055,8 @@ exports[`renders ./components/form/demo/validate-static.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions-affix-wrapper ant-mentions-affix-wrapper-status-error ant-mentions-affix-wrapper-has-feedback"
|
||||
<span
|
||||
class="ant-mentions-affix-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
@ -11088,7 +11093,7 @@ exports[`renders ./components/form/demo/validate-static.tsx correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -11120,62 +11125,76 @@ exports[`renders ./components/form/demo/validate-static.tsx correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count ant-input-textarea-status-error ant-input-textarea-has-feedback"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-status-error ant-input-affix-wrapper-has-feedback"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input ant-input-status-error"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden ant-input-clear-icon-has-suffix"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-textarea-suffix"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-textarea-suffix"
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
0
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('form', { skip: ['complex-form-control.tsx', 'dep-debug.tsx'] });
|
8
components/form/__tests__/demo.test.tsx
Normal file
8
components/form/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('form', { skip: ['complex-form-control.tsx', 'dep-debug.tsx'] });
|
||||
|
||||
rootPropsTest('form', (Form, props) => <Form.Item {...props} />, {
|
||||
name: 'Form.Item',
|
||||
});
|
@ -1,3 +1,5 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('grid');
|
||||
demoTest('grid', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('icon');
|
||||
demoTest('icon', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('input-number');
|
15
components/input-number/__tests__/demo.test.tsx
Normal file
15
components/input-number/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('input-number');
|
||||
|
||||
rootPropsTest(
|
||||
'input-number',
|
||||
(InputNumber, props) => <InputNumber {...props} addonBefore="Bamboo" />,
|
||||
{
|
||||
name: 'input-number.addonBefore',
|
||||
},
|
||||
);
|
||||
rootPropsTest('input-number', (InputNumber, props) => <InputNumber {...props} prefix="Bamboo" />, {
|
||||
name: 'input-number.prefix',
|
||||
});
|
@ -1,9 +1,9 @@
|
||||
import DownOutlined from '@ant-design/icons/DownOutlined';
|
||||
import UpOutlined from '@ant-design/icons/UpOutlined';
|
||||
import type { ValueType } from '@rc-component/mini-decimal';
|
||||
import classNames from 'classnames';
|
||||
import type { InputNumberProps as RcInputNumberProps } from 'rc-input-number';
|
||||
import RcInputNumber from 'rc-input-number';
|
||||
import type { ValueType } from '@rc-component/mini-decimal';
|
||||
import * as React from 'react';
|
||||
import ConfigProvider, { ConfigContext } from '../config-provider';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
@ -19,6 +19,7 @@ import useStyle from './style';
|
||||
export interface InputNumberProps<T extends ValueType = ValueType>
|
||||
extends Omit<RcInputNumberProps<T>, 'prefix' | 'size' | 'controls'> {
|
||||
prefixCls?: string;
|
||||
rootClassName?: string;
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
prefix?: React.ReactNode;
|
||||
@ -39,6 +40,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
|
||||
const {
|
||||
className,
|
||||
rootClassName,
|
||||
size: customizeSize,
|
||||
disabled: customDisabled,
|
||||
prefixCls: customizePrefixCls,
|
||||
@ -86,6 +88,10 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const mergeSize = compactSize || customizeSize || size;
|
||||
|
||||
const hasPrefix = prefix != null || hasFeedback;
|
||||
const hasAddon = !!(addonBefore || addonAfter);
|
||||
|
||||
// ===================== Disabled =====================
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
const mergedDisabled = customDisabled ?? disabled;
|
||||
@ -102,6 +108,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
compactItemClassnames,
|
||||
hashId,
|
||||
className,
|
||||
!hasPrefix && !hasAddon && rootClassName,
|
||||
);
|
||||
|
||||
let element = (
|
||||
@ -118,7 +125,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
/>
|
||||
);
|
||||
|
||||
if (prefix != null || hasFeedback) {
|
||||
if (hasPrefix) {
|
||||
const affixWrapperCls = classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
@ -130,9 +137,11 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
// className will go to addon wrapper
|
||||
[`${className}`]: !(addonBefore || addonAfter) && className,
|
||||
},
|
||||
|
||||
// className will go to addon wrapper
|
||||
!hasAddon && className,
|
||||
!hasAddon && rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
@ -160,7 +169,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
);
|
||||
}
|
||||
|
||||
if (addonBefore != null || addonAfter != null) {
|
||||
if (hasAddon) {
|
||||
const wrapperClassName = `${prefixCls}-group`;
|
||||
const addonClassName = `${wrapperClassName}-addon`;
|
||||
const addonBeforeNode = addonBefore ? (
|
||||
@ -182,6 +191,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
element = (
|
||||
<div className={mergedGroupClassName} style={props.style}>
|
||||
|
@ -1,137 +0,0 @@
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import type { DirectionType } from '../config-provider';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import type { FormItemStatusContextProps } from '../form/context';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import type { InputProps } from './Input';
|
||||
|
||||
const ClearableInputType = ['text', 'input'] as const;
|
||||
|
||||
function hasAddon(props: InputProps | ClearableInputProps) {
|
||||
return !!(props.addonBefore || props.addonAfter);
|
||||
}
|
||||
|
||||
/** This basic props required for input and textarea. */
|
||||
interface BasicProps {
|
||||
prefixCls: string;
|
||||
inputType: typeof ClearableInputType[number];
|
||||
value?: any;
|
||||
allowClear?: boolean;
|
||||
element: React.ReactElement;
|
||||
handleReset: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
disabled?: boolean;
|
||||
direction?: DirectionType;
|
||||
focused?: boolean;
|
||||
readOnly?: boolean;
|
||||
bordered: boolean;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
/** This props only for input. */
|
||||
export interface ClearableInputProps extends BasicProps {
|
||||
size?: SizeType;
|
||||
suffix?: React.ReactNode;
|
||||
prefix?: React.ReactNode;
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
triggerFocus?: () => void;
|
||||
status?: InputStatus;
|
||||
hashId?: string;
|
||||
}
|
||||
|
||||
class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
renderClearIcon(prefixCls: string) {
|
||||
const { value, disabled, readOnly, handleReset, suffix } = this.props;
|
||||
const needClear = !disabled && !readOnly && value;
|
||||
const className = `${prefixCls}-clear-icon`;
|
||||
return (
|
||||
<CloseCircleFilled
|
||||
onClick={handleReset}
|
||||
// Do not trigger onBlur when clear input
|
||||
// https://github.com/ant-design/ant-design/issues/31200
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
className={classNames(
|
||||
{
|
||||
[`${className}-hidden`]: !needClear,
|
||||
[`${className}-has-suffix`]: !!suffix,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
role="button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderTextAreaWithClearIcon(
|
||||
prefixCls: string,
|
||||
element: React.ReactElement,
|
||||
statusContext: FormItemStatusContextProps,
|
||||
) {
|
||||
const {
|
||||
value,
|
||||
allowClear,
|
||||
className,
|
||||
style,
|
||||
direction,
|
||||
bordered,
|
||||
hidden,
|
||||
status: customStatus,
|
||||
hashId,
|
||||
} = this.props;
|
||||
|
||||
const { status: contextStatus, hasFeedback } = statusContext;
|
||||
|
||||
if (!allowClear) {
|
||||
return cloneElement(element, {
|
||||
value,
|
||||
});
|
||||
}
|
||||
const affixWrapperCls = classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
`${prefixCls}-affix-wrapper-textarea-with-clear-btn`,
|
||||
getStatusClassNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
// className will go to addon wrapper
|
||||
[`${className}`]: !hasAddon(this.props) && className,
|
||||
},
|
||||
hashId,
|
||||
);
|
||||
return (
|
||||
<span className={affixWrapperCls} style={style} hidden={hidden}>
|
||||
{cloneElement(element, {
|
||||
style: null,
|
||||
value,
|
||||
})}
|
||||
{this.renderClearIcon(prefixCls)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FormItemInputContext.Consumer>
|
||||
{(statusContext) => {
|
||||
const { prefixCls, inputType, element } = this.props;
|
||||
if (inputType === ClearableInputType[0]) {
|
||||
return this.renderTextAreaWithClearIcon(prefixCls, element, statusContext);
|
||||
}
|
||||
}}
|
||||
</FormItemInputContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ClearableLabeledInput;
|
@ -26,67 +26,6 @@ export interface InputFocusOptions extends FocusOptions {
|
||||
|
||||
export type { InputRef };
|
||||
|
||||
export function fixControlledValue<T>(value: T) {
|
||||
if (typeof value === 'undefined' || value === null) {
|
||||
return '';
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement>(
|
||||
target: E,
|
||||
e:
|
||||
| React.ChangeEvent<E>
|
||||
| React.MouseEvent<HTMLElement, MouseEvent>
|
||||
| React.CompositionEvent<HTMLElement>,
|
||||
onChange?: (event: React.ChangeEvent<E>) => void,
|
||||
targetValue?: string,
|
||||
) {
|
||||
if (!onChange) {
|
||||
return;
|
||||
}
|
||||
let event = e as React.ChangeEvent<E>;
|
||||
|
||||
if (e.type === 'click') {
|
||||
// Clone a new target for event.
|
||||
// Avoid the following usage, the setQuery method gets the original value.
|
||||
//
|
||||
// const [query, setQuery] = React.useState('');
|
||||
// <Input
|
||||
// allowClear
|
||||
// value={query}
|
||||
// onChange={(e)=> {
|
||||
// setQuery((prevStatus) => e.target.value);
|
||||
// }}
|
||||
// />
|
||||
|
||||
const currentTarget = target.cloneNode(true) as E;
|
||||
|
||||
// click clear icon
|
||||
event = Object.create(e, {
|
||||
target: { value: currentTarget },
|
||||
currentTarget: { value: currentTarget },
|
||||
});
|
||||
|
||||
currentTarget.value = '';
|
||||
onChange(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger by composition event, this means we need force change the input value
|
||||
if (targetValue !== undefined) {
|
||||
event = Object.create(e, {
|
||||
target: { value: target },
|
||||
currentTarget: { value: target },
|
||||
});
|
||||
|
||||
target.value = targetValue;
|
||||
onChange(event);
|
||||
return;
|
||||
}
|
||||
onChange(event);
|
||||
}
|
||||
|
||||
export function triggerFocus(
|
||||
element?: HTMLInputElement | HTMLTextAreaElement,
|
||||
option?: InputFocusOptions,
|
||||
@ -121,6 +60,7 @@ export interface InputProps
|
||||
RcInputProps,
|
||||
'wrapperClassName' | 'groupClassName' | 'inputClassName' | 'affixWrapperClassName'
|
||||
> {
|
||||
rootClassName?: string;
|
||||
size?: SizeType;
|
||||
disabled?: boolean;
|
||||
status?: InputStatus;
|
||||
@ -142,6 +82,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
addonAfter,
|
||||
addonBefore,
|
||||
className,
|
||||
rootClassName,
|
||||
onChange,
|
||||
...rest
|
||||
} = props;
|
||||
@ -226,7 +167,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
onFocus={handleFocus}
|
||||
suffix={suffixNode}
|
||||
allowClear={mergedAllowClear}
|
||||
className={classNames(className, compactItemClassnames)}
|
||||
className={classNames(className, rootClassName, compactItemClassnames)}
|
||||
onChange={handleChange}
|
||||
addonAfter={
|
||||
addonAfter && (
|
||||
@ -246,41 +187,43 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
</NoCompactStyle>
|
||||
)
|
||||
}
|
||||
inputClassName={classNames(
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
!inputHasPrefixSuffix && getStatusClassNames(prefixCls, mergedStatus),
|
||||
hashId,
|
||||
)}
|
||||
affixWrapperClassName={classNames(
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
)}
|
||||
wrapperClassName={classNames(
|
||||
{
|
||||
[`${prefixCls}-group-rtl`]: direction === 'rtl',
|
||||
},
|
||||
hashId,
|
||||
)}
|
||||
groupClassName={classNames(
|
||||
{
|
||||
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
)}
|
||||
classes={{
|
||||
input: classNames(
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
!inputHasPrefixSuffix && getStatusClassNames(prefixCls, mergedStatus),
|
||||
hashId,
|
||||
),
|
||||
affixWrapper: classNames(
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
),
|
||||
wrapper: classNames(
|
||||
{
|
||||
[`${prefixCls}-group-rtl`]: direction === 'rtl',
|
||||
},
|
||||
hashId,
|
||||
),
|
||||
group: classNames(
|
||||
{
|
||||
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
),
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
@ -1,91 +1,58 @@
|
||||
import classNames from 'classnames';
|
||||
import type { TextAreaProps as RcTextAreaProps } from 'rc-textarea';
|
||||
import RcTextArea from 'rc-textarea';
|
||||
import type { ResizableTextAreaRef } from 'rc-textarea/lib/ResizableTextArea';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import type { TextAreaProps as RcTextAreaProps } from 'rc-textarea/lib/interface';
|
||||
import type { TextAreaRef as RcTextAreaRef } from 'rc-textarea';
|
||||
import { forwardRef } from 'react';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import RcTextArea from 'rc-textarea';
|
||||
import classNames from 'classnames';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import type { BaseInputProps } from 'rc-input/lib/interface';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import useStyle from './style';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
import type { InputFocusOptions } from './Input';
|
||||
import { fixControlledValue, resolveOnChange, triggerFocus } from './Input';
|
||||
import useStyle from './style';
|
||||
import { triggerFocus } from './Input';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
interface ShowCountProps {
|
||||
formatter: (args: { value: string; count: number; maxLength?: number }) => string;
|
||||
}
|
||||
|
||||
function fixEmojiLength(value: string, maxLength: number) {
|
||||
return [...(value || '')].slice(0, maxLength).join('');
|
||||
}
|
||||
|
||||
function setTriggerValue(
|
||||
isCursorInEnd: boolean,
|
||||
preValue: string,
|
||||
triggerValue: string,
|
||||
maxLength: number,
|
||||
) {
|
||||
let newTriggerValue = triggerValue;
|
||||
if (isCursorInEnd) {
|
||||
// 光标在尾部,直接截断
|
||||
newTriggerValue = fixEmojiLength(triggerValue, maxLength!);
|
||||
} else if (
|
||||
[...(preValue || '')].length < triggerValue.length &&
|
||||
[...(triggerValue || '')].length > maxLength!
|
||||
) {
|
||||
// 光标在中间,如果最后的值超过最大值,则采用原先的值
|
||||
newTriggerValue = preValue;
|
||||
}
|
||||
return newTriggerValue;
|
||||
}
|
||||
|
||||
export interface TextAreaProps extends RcTextAreaProps {
|
||||
allowClear?: boolean;
|
||||
export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
|
||||
bordered?: boolean;
|
||||
showCount?: boolean | ShowCountProps;
|
||||
size?: SizeType;
|
||||
disabled?: boolean;
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
export interface TextAreaRef {
|
||||
focus: (options?: InputFocusOptions) => void;
|
||||
blur: () => void;
|
||||
resizableTextArea?: ResizableTextAreaRef;
|
||||
resizableTextArea?: RcTextAreaRef['resizableTextArea'];
|
||||
}
|
||||
|
||||
const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
const TextArea = forwardRef<TextAreaRef, TextAreaProps>(
|
||||
(
|
||||
{
|
||||
prefixCls: customizePrefixCls,
|
||||
bordered = true,
|
||||
showCount = false,
|
||||
maxLength,
|
||||
className,
|
||||
style,
|
||||
size: customizeSize,
|
||||
disabled: customDisabled,
|
||||
onCompositionStart,
|
||||
onCompositionEnd,
|
||||
onChange,
|
||||
status: customStatus,
|
||||
...props
|
||||
allowClear,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
// ===================== Size =====================
|
||||
const size = React.useContext(SizeContext);
|
||||
const mergedSize = customizeSize || size;
|
||||
|
||||
// ===================== Disabled =====================
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
const mergedDisabled = customDisabled ?? disabled;
|
||||
|
||||
// ===================== Status =====================
|
||||
const {
|
||||
status: contextStatus,
|
||||
hasFeedback,
|
||||
@ -93,87 +60,8 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
} = React.useContext(FormItemInputContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const innerRef = React.useRef<RcTextArea>(null);
|
||||
const clearableInputRef = React.useRef<ClearableLabeledInput>(null);
|
||||
|
||||
const [compositing, setCompositing] = React.useState(false);
|
||||
const oldCompositionValueRef = React.useRef<string>();
|
||||
const oldSelectionStartRef = React.useRef<number>(0);
|
||||
|
||||
const [value, setValue] = useMergedState(props.defaultValue, {
|
||||
value: props.value,
|
||||
});
|
||||
const { hidden } = props;
|
||||
|
||||
const handleSetValue = (val: string, callback?: () => void) => {
|
||||
if (props.value === undefined) {
|
||||
setValue(val);
|
||||
callback?.();
|
||||
}
|
||||
};
|
||||
|
||||
// =========================== Value Update ===========================
|
||||
// Max length value
|
||||
const hasMaxLength = Number(maxLength) > 0;
|
||||
|
||||
const onInternalCompositionStart: React.CompositionEventHandler<HTMLTextAreaElement> = (e) => {
|
||||
setCompositing(true);
|
||||
// 拼音输入前保存一份旧值
|
||||
oldCompositionValueRef.current = value as string;
|
||||
// 保存旧的光标位置
|
||||
oldSelectionStartRef.current = e.currentTarget.selectionStart;
|
||||
onCompositionStart?.(e);
|
||||
};
|
||||
|
||||
const onInternalCompositionEnd: React.CompositionEventHandler<HTMLTextAreaElement> = (e) => {
|
||||
setCompositing(false);
|
||||
|
||||
let triggerValue = e.currentTarget.value;
|
||||
if (hasMaxLength) {
|
||||
const isCursorInEnd =
|
||||
oldSelectionStartRef.current >= maxLength! + 1 ||
|
||||
oldSelectionStartRef.current === oldCompositionValueRef.current?.length;
|
||||
triggerValue = setTriggerValue(
|
||||
isCursorInEnd,
|
||||
oldCompositionValueRef.current as string,
|
||||
triggerValue,
|
||||
maxLength!,
|
||||
);
|
||||
}
|
||||
// Patch composition onChange when value changed
|
||||
if (triggerValue !== value) {
|
||||
handleSetValue(triggerValue);
|
||||
resolveOnChange(e.currentTarget, e, onChange, triggerValue);
|
||||
}
|
||||
|
||||
onCompositionEnd?.(e);
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
let triggerValue = e.target.value;
|
||||
if (!compositing && hasMaxLength) {
|
||||
// 1. 复制粘贴超过maxlength的情况 2.未超过maxlength的情况
|
||||
const isCursorInEnd =
|
||||
e.target.selectionStart >= maxLength! + 1 ||
|
||||
e.target.selectionStart === triggerValue.length ||
|
||||
!e.target.selectionStart;
|
||||
triggerValue = setTriggerValue(isCursorInEnd, value as string, triggerValue, maxLength!);
|
||||
}
|
||||
handleSetValue(triggerValue);
|
||||
resolveOnChange(e.currentTarget, e, onChange, triggerValue);
|
||||
};
|
||||
|
||||
// ============================== Reset ===============================
|
||||
const handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
handleSetValue('');
|
||||
innerRef.current?.focus();
|
||||
resolveOnChange(innerRef.current?.resizableTextArea?.textArea!, e, onChange);
|
||||
};
|
||||
|
||||
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
||||
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
// ===================== Ref =====================
|
||||
const innerRef = React.useRef<RcTextAreaRef>(null);
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
resizableTextArea: innerRef.current?.resizableTextArea,
|
||||
@ -183,89 +71,58 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
blur: () => innerRef.current?.blur(),
|
||||
}));
|
||||
|
||||
const textArea = (
|
||||
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
||||
|
||||
// Allow clear
|
||||
let mergedAllowClear: BaseInputProps['allowClear'];
|
||||
if (typeof allowClear === 'object' && allowClear?.clearIcon) {
|
||||
mergedAllowClear = allowClear;
|
||||
} else if (allowClear) {
|
||||
mergedAllowClear = { clearIcon: <CloseCircleFilled /> };
|
||||
}
|
||||
|
||||
// ===================== Style =====================
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
return wrapSSR(
|
||||
<RcTextArea
|
||||
{...omit(props, ['allowClear'])}
|
||||
{...rest}
|
||||
disabled={mergedDisabled}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[className!]: className && !showCount,
|
||||
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
|
||||
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
hashId,
|
||||
)}
|
||||
style={showCount ? { resize: style?.resize } : style}
|
||||
prefixCls={prefixCls}
|
||||
onCompositionStart={onInternalCompositionStart}
|
||||
onChange={handleChange}
|
||||
onCompositionEnd={onInternalCompositionEnd}
|
||||
ref={innerRef}
|
||||
/>
|
||||
);
|
||||
|
||||
let val = fixControlledValue(value) as string;
|
||||
|
||||
if (!compositing && hasMaxLength && (props.value === null || props.value === undefined)) {
|
||||
// fix #27612 将value转为数组进行截取,解决 '😂'.length === 2 等emoji表情导致的截取乱码的问题
|
||||
val = fixEmojiLength(val, maxLength!);
|
||||
}
|
||||
|
||||
// TextArea
|
||||
const textareaNode = (
|
||||
<ClearableLabeledInput
|
||||
disabled={mergedDisabled}
|
||||
{...props}
|
||||
prefixCls={prefixCls}
|
||||
direction={direction}
|
||||
inputType="text"
|
||||
value={val}
|
||||
element={textArea}
|
||||
handleReset={handleReset}
|
||||
ref={clearableInputRef}
|
||||
bordered={bordered}
|
||||
status={customStatus}
|
||||
style={showCount ? undefined : style}
|
||||
hashId={hashId}
|
||||
/>
|
||||
);
|
||||
|
||||
// Only show text area wrapper when needed
|
||||
if (showCount || hasFeedback) {
|
||||
const valueLength = [...val].length;
|
||||
|
||||
let dataCount = '';
|
||||
if (typeof showCount === 'object') {
|
||||
dataCount = showCount.formatter({ value: val, count: valueLength, maxLength });
|
||||
} else {
|
||||
dataCount = `${valueLength}${hasMaxLength ? ` / ${maxLength}` : ''}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
hidden={hidden}
|
||||
className={classNames(
|
||||
`${prefixCls}-textarea`,
|
||||
allowClear={mergedAllowClear}
|
||||
classes={{
|
||||
affixWrapper: classNames(
|
||||
`${prefixCls}-textarea-affix-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-textarea-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-textarea-show-count`]: showCount,
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-textarea`, mergedStatus, hasFeedback),
|
||||
className,
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus),
|
||||
hashId,
|
||||
)}
|
||||
style={style}
|
||||
data-count={dataCount}
|
||||
>
|
||||
{textareaNode}
|
||||
{hasFeedback && <span className={`${prefixCls}-textarea-suffix`}>{feedbackIcon}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return wrapSSR(textareaNode);
|
||||
),
|
||||
countWrapper: classNames(
|
||||
`${prefixCls}-textarea`,
|
||||
`${prefixCls}-textarea-show-count`,
|
||||
hashId,
|
||||
),
|
||||
textarea: classNames(
|
||||
{
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
hashId,
|
||||
),
|
||||
}}
|
||||
prefixCls={prefixCls}
|
||||
suffix={
|
||||
hasFeedback && <span className={`${prefixCls}-textarea-suffix`}>{feedbackIcon}</span>
|
||||
}
|
||||
ref={innerRef}
|
||||
/>,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -4902,31 +4902,40 @@ Array [
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
placeholder="textarea with clear icon"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
]
|
||||
@ -4994,31 +5003,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx extend context cor
|
||||
placeholder="Unbordered"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-borderless"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-borderless"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input ant-input-borderless"
|
||||
placeholder="Unbordered"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
@ -5100,31 +5118,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx extend context cor
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
style="border:2px solid #000"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -9707,12 +9734,17 @@ Array [
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
@ -9970,31 +10002,40 @@ Array [
|
||||
The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows.
|
||||
</textarea>,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
style="width:93px"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
]
|
||||
@ -10003,7 +10044,7 @@ Array [
|
||||
exports[`renders ./components/input/demo/textarea-show-count.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
style="height:120px;margin-bottom:24px"
|
||||
>
|
||||
@ -10011,9 +10052,14 @@ Array [
|
||||
class="ant-input"
|
||||
placeholder="can resize"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
style="height:120px;resize:none"
|
||||
>
|
||||
@ -10022,6 +10068,11 @@ Array [
|
||||
placeholder="disable resize"
|
||||
style="resize:none"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
@ -1103,31 +1103,40 @@ Array [
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
placeholder="textarea with clear icon"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
]
|
||||
@ -1195,31 +1204,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx correctly 1`] = `
|
||||
placeholder="Unbordered"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-borderless"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-borderless"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input ant-input-borderless"
|
||||
placeholder="Unbordered"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
@ -1301,31 +1319,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
style="border:2px solid #000"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -3455,12 +3482,17 @@ Array [
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
@ -3718,31 +3750,40 @@ Array [
|
||||
The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows.
|
||||
</textarea>,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
style="width:93px"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
]
|
||||
@ -3751,7 +3792,7 @@ Array [
|
||||
exports[`renders ./components/input/demo/textarea-show-count.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
style="height:120px;margin-bottom:24px"
|
||||
>
|
||||
@ -3759,9 +3800,14 @@ Array [
|
||||
class="ant-input"
|
||||
placeholder="can resize"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-input-textarea ant-input-textarea-show-count"
|
||||
class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
|
||||
data-count="0 / 100"
|
||||
style="height:120px;resize:none"
|
||||
>
|
||||
@ -3770,6 +3816,11 @@ Array [
|
||||
placeholder="disable resize"
|
||||
style="resize:none"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-data-count"
|
||||
>
|
||||
0 / 100
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
@ -2,7 +2,7 @@
|
||||
|
||||
exports[`TextArea allowClear should change type when click 1`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
@ -10,234 +10,306 @@ exports[`TextArea allowClear should change type when click 1`] = `
|
||||
111
|
||||
</textarea>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should change type when click 2`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 1`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 2`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 3`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 1`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 2`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 3`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
class="ant-input-clear-icon ant-input-clear-icon-hidden"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('input');
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user