Browse Source

refactor: refines types and fix to use prop direction over context direction (#37716)

* refactor: use enum instead of multiple constants and refines types

* fix: siganture and direction prop passing

* Update Editable.tsx

* refine types

* test: update snap

* style: prefer literal constants over enum

* fix: refine types for typography

* fix: restore ellipsis const

* fix: restore ellipsis const

* fix: restore textare importing
pull/38085/head
Zheeeng 2 years ago
committed by GitHub
parent
commit
29a15e2a5d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      components/input/__tests__/textarea.test.tsx
  2. 28
      components/typography/Base/Ellipsis.tsx
  3. 27
      components/typography/Base/index.tsx
  4. 5
      components/typography/Editable.tsx
  5. 16
      components/typography/Link.tsx
  6. 12
      components/typography/Paragraph.tsx
  7. 5
      components/typography/Text.tsx
  8. 23
      components/typography/Title.tsx
  9. 92
      components/typography/Typography.tsx
  10. 4
      components/typography/__tests__/__snapshots__/index.test.tsx.snap
  11. 17
      components/typography/__tests__/ellipsis.test.tsx

9
components/input/__tests__/textarea.test.tsx

@ -3,7 +3,14 @@ import type { ChangeEventHandler, TextareaHTMLAttributes } from 'react';
import React, { useState } from 'react';
import Input from '..';
import focusTest from '../../../tests/shared/focusTest';
import { fireEvent, waitFakeTimer, render, sleep, triggerResize, pureRender } from '../../../tests/utils';
import {
fireEvent,
waitFakeTimer,
render,
sleep,
triggerResize,
pureRender,
} from '../../../tests/utils';
import type { RenderOptions } from '../../../tests/utils';
import type { TextAreaRef } from '../TextArea';

28
components/typography/Base/Ellipsis.tsx

@ -67,6 +67,13 @@ const WALKING = 2;
const DONE_WITH_ELLIPSIS = 3;
const DONE_WITHOUT_ELLIPSIS = 4;
type WalkingState =
| typeof NONE
| typeof PREPARE
| typeof WALKING
| typeof DONE_WITH_ELLIPSIS
| typeof DONE_WITHOUT_ELLIPSIS;
const Ellipsis = ({
enabledMeasure,
children,
@ -76,20 +83,15 @@ const Ellipsis = ({
rows,
onEllipsis,
}: EllipsisProps) => {
const [cutLength, setCutLength] = React.useState<[number, number, number]>([0, 0, 0]);
const [walkingState, setWalkingState] = React.useState<
| typeof NONE
| typeof PREPARE
| typeof WALKING
| typeof DONE_WITH_ELLIPSIS
| typeof DONE_WITHOUT_ELLIPSIS
>(NONE);
const [startLen, midLen, endLen] = cutLength;
const [[startLen, midLen, endLen], setCutLength] = React.useState<
[startLen: number, midLen: number, endLen: number]
>([0, 0, 0]);
const [walkingState, setWalkingState] = React.useState<WalkingState>(NONE);
const [singleRowHeight, setSingleRowHeight] = React.useState(0);
const singleRowRef = React.useRef<HTMLSpanElement>(null);
const midRowRef = React.useRef<HTMLSpanElement>(null);
const singleRowRef = React.useRef<HTMLElement>(null);
const midRowRef = React.useRef<HTMLElement>(null);
const nodeList = React.useMemo(() => toArray(text), [text]);
const totalLen = React.useMemo(() => getNodesLen(nodeList), [nodeList]);
@ -167,7 +169,7 @@ const Ellipsis = ({
const renderMeasure = (
content: React.ReactNode,
ref: React.Ref<HTMLSpanElement>,
ref: React.Ref<HTMLElement>,
style: React.CSSProperties,
) => (
<span
@ -189,7 +191,7 @@ const Ellipsis = ({
</span>
);
const renderMeasureSlice = (len: number, ref: React.Ref<HTMLSpanElement>) => {
const renderMeasureSlice = (len: number, ref: React.Ref<HTMLElement>) => {
const sliceNodeList = sliceNodes(nodeList, len);
return renderMeasure(children(sliceNodeList, true), ref, measureStyle);

27
components/typography/Base/index.tsx

@ -59,7 +59,8 @@ export interface EllipsisConfig {
tooltip?: React.ReactNode | TooltipProps;
}
export interface BlockProps extends TypographyProps {
export interface BlockProps<C extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements>
extends TypographyProps<C> {
title?: string;
editable?: boolean | EditConfig;
copyable?: boolean | CopyConfig;
@ -113,13 +114,9 @@ function toList<T extends any>(val: T | T[]): T[] {
return Array.isArray(val) ? val : [val];
}
interface InternalBlockProps extends BlockProps {
component: string;
}
const ELLIPSIS_STR = '...';
const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
@ -151,7 +148,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
'strong',
'keyboard',
'italic',
]) as any;
]);
// ========================== Editable ==========================
const [enableEdit, editConfig] = useMergedConfig<EditConfig>(editable);
@ -175,7 +172,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
}
}, [editing]);
const onEditClick = (e?: React.MouseEvent<HTMLDivElement>) => {
const onEditClick = (e?: React.MouseEvent<HTMLElement>) => {
e?.preventDefault();
triggerEdit(true);
};
@ -193,7 +190,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
// ========================== Copyable ==========================
const [enableCopy, copyConfig] = useMergedConfig<CopyConfig>(copyable);
const [copied, setCopied] = React.useState(false);
const copyIdRef = React.useRef<NodeJS.Timeout>();
const copyIdRef = React.useRef<number>();
const copyOptions: Pick<CopyConfig, 'format'> = {};
if (copyConfig.format) {
@ -201,7 +198,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
}
const cleanCopyId = () => {
clearTimeout(copyIdRef.current!);
window.clearTimeout(copyIdRef.current!);
};
const onCopyClick = (e?: React.MouseEvent<HTMLDivElement>) => {
@ -214,7 +211,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
// Trigger tips update
cleanCopyId();
copyIdRef.current = setTimeout(() => {
copyIdRef.current = window.setTimeout(() => {
setCopied(false);
}, 3000);
@ -351,7 +348,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
tooltipProps = { title: ellipsisConfig.tooltip };
}
const topAriaLabel = React.useMemo(() => {
const isValid = (val: any) => ['string', 'number'].includes(typeof val);
const isValid = (val: any): val is string | number => ['string', 'number'].includes(typeof val);
if (!enableEllipsis || cssEllipsis) {
return undefined;
@ -490,7 +487,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
return (
<ResizeObserver onResize={onResize} disabled={!mergedEnableEllipsis || cssEllipsis}>
{resizeRef => (
{(resizeRef: React.RefObject<HTMLElement>) => (
<EllipsisTooltip
tooltipProps={tooltipProps}
enabledEllipsis={mergedEnableEllipsis}
@ -515,8 +512,8 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
component={component}
ref={composeRef(resizeRef, typographyRef, ref)}
direction={direction}
onClick={triggerType.includes('text') ? onEditClick : null}
aria-label={topAriaLabel}
onClick={triggerType.includes('text') ? onEditClick : undefined}
aria-label={topAriaLabel?.toString()}
title={title}
{...textProps}
>

5
components/typography/Editable.tsx

@ -5,6 +5,7 @@ import KeyCode from 'rc-util/lib/KeyCode';
import * as React from 'react';
import type { DirectionType } from '../config-provider';
import TextArea from '../input/TextArea';
import type { TextAreaRef } from '../input/TextArea';
import { cloneElement } from '../_util/reactNode';
interface EditableProps {
@ -38,7 +39,7 @@ const Editable: React.FC<EditableProps> = ({
component,
enterIcon = <EnterOutlined />,
}) => {
const ref = React.useRef<any>();
const ref = React.useRef<TextAreaRef>(null);
const inComposition = React.useRef(false);
const lastKeyCode = React.useRef<number>();
@ -125,7 +126,7 @@ const Editable: React.FC<EditableProps> = ({
return (
<div className={textAreaClassName} style={style}>
<TextArea
ref={ref as any}
ref={ref}
maxLength={maxLength}
value={current}
onChange={onChange}

16
components/typography/Link.tsx

@ -4,15 +4,12 @@ import type { BlockProps } from './Base';
import Base from './Base';
export interface LinkProps
extends BlockProps,
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'type'> {
extends BlockProps<'a'>,
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'type' | keyof BlockProps<'a'>> {
ellipsis?: boolean;
}
const Link: React.ForwardRefRenderFunction<HTMLElement, LinkProps> = (
{ ellipsis, rel, ...restProps },
ref,
) => {
const Link = React.forwardRef<HTMLElement, LinkProps>(({ ellipsis, rel, ...restProps }, ref) => {
warning(
typeof ellipsis !== 'object',
'Typography.Link',
@ -28,11 +25,10 @@ const Link: React.ForwardRefRenderFunction<HTMLElement, LinkProps> = (
rel: rel === undefined && restProps.target === '_blank' ? 'noopener noreferrer' : rel,
};
// https://github.com/ant-design/ant-design/issues/26622
// @ts-ignore
// @ts-expect-error: https://github.com/ant-design/ant-design/issues/26622
delete mergedProps.navigate;
return <Base {...mergedProps} ref={baseRef} ellipsis={!!ellipsis} component="a" />;
};
});
export default React.forwardRef(Link);
export default Link;

12
components/typography/Paragraph.tsx

@ -2,12 +2,12 @@ import * as React from 'react';
import type { BlockProps } from './Base';
import Base from './Base';
export interface ParagraphProps extends BlockProps {
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
}
export interface ParagraphProps
extends BlockProps<'div'>,
Omit<React.HTMLAttributes<HTMLDivElement>, 'type' | keyof BlockProps<'div'>> {}
const Paragraph: React.ForwardRefRenderFunction<HTMLDivElement, ParagraphProps> = (props, ref) => (
const Paragraph = React.forwardRef<HTMLElement, ParagraphProps>((props, ref) => (
<Base ref={ref} {...props} component="div" />
);
));
export default React.forwardRef(Paragraph);
export default Paragraph;

5
components/typography/Text.tsx

@ -4,9 +4,10 @@ import warning from '../_util/warning';
import type { BlockProps, EllipsisConfig } from './Base';
import Base from './Base';
export interface TextProps extends BlockProps {
export interface TextProps
extends BlockProps<'span'>,
Omit<React.HTMLAttributes<HTMLSpanElement>, 'type' | keyof BlockProps<'span'>> {
ellipsis?: boolean | Omit<EllipsisConfig, 'expandable' | 'rows' | 'onExpand'>;
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
}
const Text: React.ForwardRefRenderFunction<HTMLSpanElement, TextProps> = (

23
components/typography/Title.tsx

@ -6,17 +6,18 @@ import Base from './Base';
const TITLE_ELE_LIST = tupleNum(1, 2, 3, 4, 5);
export type TitleProps = Omit<
BlockProps & {
level?: typeof TITLE_ELE_LIST[number];
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
},
'strong'
>;
export interface TitleProps
extends Omit<BlockProps<'h1' | 'h2' | 'h3' | 'h4' | 'h5'>, 'strong'>,
Omit<
React.HTMLAttributes<HTMLHeadElement>,
'type' | keyof BlockProps<'h1' | 'h2' | 'h3' | 'h4' | 'h5'>
> {
level?: typeof TITLE_ELE_LIST[number];
}
const Title: React.ForwardRefRenderFunction<HTMLHeadingElement, TitleProps> = (props, ref) => {
const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
const { level = 1, ...restProps } = props;
let component: string;
let component: keyof JSX.IntrinsicElements;
if (TITLE_ELE_LIST.indexOf(level) !== -1) {
component = `h${level}`;
@ -30,6 +31,6 @@ const Title: React.ForwardRefRenderFunction<HTMLHeadingElement, TitleProps> = (p
}
return <Base ref={ref} {...restProps} component={component} />;
};
});
export default React.forwardRef(Title);
export default Title;

92
components/typography/Typography.tsx

@ -1,66 +1,76 @@
import classNames from 'classnames';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import type { DirectionType } from '../config-provider';
import { ConfigContext } from '../config-provider';
import warning from '../_util/warning';
export interface TypographyProps {
export interface TypographyProps<C extends keyof JSX.IntrinsicElements>
extends React.HTMLAttributes<HTMLElement> {
id?: string;
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
/** @internal */
component?: C;
['aria-label']?: string;
direction?: DirectionType;
}
interface InternalTypographyProps extends TypographyProps {
component?: string;
interface InternalTypographyProps<C extends keyof JSX.IntrinsicElements>
extends TypographyProps<C> {
/** @deprecated Use `ref` directly if using React 16 */
setContentRef?: (node: HTMLElement) => void;
}
const Typography: React.ForwardRefRenderFunction<{}, InternalTypographyProps> = (
{
prefixCls: customizePrefixCls,
component = 'article',
className,
'aria-label': ariaLabel,
setContentRef,
children,
...restProps
},
ref,
) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
let mergedRef = ref;
if (setContentRef) {
warning(false, 'Typography', '`setContentRef` is deprecated. Please use `ref` instead.');
mergedRef = composeRef(ref, setContentRef);
}
const Component = component as any;
const prefixCls = getPrefixCls('typography', customizePrefixCls);
const componentClassName = classNames(
prefixCls,
const Typography = React.forwardRef<
HTMLElement,
InternalTypographyProps<keyof JSX.IntrinsicElements>
>(
(
{
[`${prefixCls}-rtl`]: direction === 'rtl',
prefixCls: customizePrefixCls,
component: Component = 'article',
className,
setContentRef,
children,
direction: typographyDirection,
...restProps
},
className,
);
return (
<Component className={componentClassName} aria-label={ariaLabel} ref={mergedRef} {...restProps}>
{children}
</Component>
);
};
ref,
) => {
const { getPrefixCls, direction: contextDirection } = React.useContext(ConfigContext);
const direction = typographyDirection ?? contextDirection;
let mergedRef = ref;
if (setContentRef) {
warning(false, 'Typography', '`setContentRef` is deprecated. Please use `ref` instead.');
mergedRef = composeRef(ref, setContentRef);
}
const prefixCls = getPrefixCls('typography', customizePrefixCls);
const componentClassName = classNames(
prefixCls,
{
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
);
return (
// @ts-expect-error: Expression produces a union type that is too complex to represent.
<Component className={componentClassName} ref={mergedRef} {...restProps}>
{children}
</Component>
);
},
);
const RefTypography = React.forwardRef(Typography);
if (process.env.NODE_ENV !== 'production') {
RefTypography.displayName = 'Typography';
Typography.displayName = 'Typography';
}
// es default export should use const instead of let
const ExportTypography = RefTypography as unknown as React.FC<TypographyProps>;
export default ExportTypography;
export default Typography;

4
components/typography/__tests__/__snapshots__/index.test.tsx.snap

@ -3,27 +3,23 @@
exports[`Typography rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-typography ant-typography-rtl"
direction="rtl"
/>
`;
exports[`Typography rtl render component should be rendered correctly in RTL direction 2`] = `
<article
class="ant-typography ant-typography-rtl"
direction="rtl"
/>
`;
exports[`Typography rtl render component should be rendered correctly in RTL direction 3`] = `
<h1
class="ant-typography ant-typography-rtl"
direction="rtl"
/>
`;
exports[`Typography rtl render component should be rendered correctly in RTL direction 4`] = `
<a
class="ant-typography ant-typography-rtl"
direction="rtl"
/>
`;

17
components/typography/__tests__/ellipsis.test.tsx

@ -382,21 +382,14 @@ describe('Typography.Ellipsis', () => {
it('js ellipsis should show aria-label', () => {
const { container: titleWrapper } = render(
<Base
component={undefined as unknown as string}
title="bamboo"
ellipsis={{ expandable: true }}
/>,
<Base component={undefined} title="bamboo" ellipsis={{ expandable: true }} />,
);
expect(titleWrapper.querySelector('.ant-typography')?.getAttribute('aria-label')).toEqual(
'bamboo',
);
const { container: tooltipWrapper } = render(
<Base
component={undefined as unknown as string}
ellipsis={{ expandable: true, tooltip: 'little' }}
/>,
<Base component={undefined} ellipsis={{ expandable: true, tooltip: 'little' }} />,
);
expect(tooltipWrapper.querySelector('.ant-typography')?.getAttribute('aria-label')).toEqual(
'little',
@ -426,11 +419,7 @@ describe('Typography.Ellipsis', () => {
const ref = React.createRef<any>();
const { container, baseElement } = render(
<Base
component={undefined as unknown as string}
ellipsis={{ tooltip: 'This is tooltip', rows: 2 }}
ref={ref}
>
<Base component={undefined} ellipsis={{ tooltip: 'This is tooltip', rows: 2 }} ref={ref}>
Ant Design, a design language for background applications, is refined by Ant UED Team.
</Base>,
);

Loading…
Cancel
Save