diff --git a/src/tinyTag/__tests__/__snapshots__/tinyTag.test.tsx.snap b/src/tinyTag/__tests__/__snapshots__/tinyTag.test.tsx.snap index 7ae0c1fff..b6cbf362a 100644 --- a/src/tinyTag/__tests__/__snapshots__/tinyTag.test.tsx.snap +++ b/src/tinyTag/__tests__/__snapshots__/tinyTag.test.tsx.snap @@ -8,8 +8,8 @@ exports[`test StatusTag should match snapshot 1`] = ` @@ -48,8 +48,8 @@ exports[`test StatusTag should support fill type 1`] = ` diff --git a/src/tinyTag/__tests__/tinyTag.test.tsx b/src/tinyTag/__tests__/tinyTag.test.tsx index f060e418e..6790d8716 100644 --- a/src/tinyTag/__tests__/tinyTag.test.tsx +++ b/src/tinyTag/__tests__/tinyTag.test.tsx @@ -5,7 +5,36 @@ import '@testing-library/jest-dom/extend-expect'; import TinyTag from '..'; describe('test StatusTag', () => { - beforeEach(cleanup); + const svgElementPrototype = SVGElement.prototype as SVGElement & { + getComputedTextLength?: () => number; + }; + const getComputedTextLength = svgElementPrototype.getComputedTextLength; + const requestAnimationFrame = window.requestAnimationFrame; + const cancelAnimationFrame = window.cancelAnimationFrame; + + beforeEach(() => { + cleanup(); + Object.defineProperty(svgElementPrototype, 'getComputedTextLength', { + configurable: true, + value: jest.fn(() => 20), + }); + window.requestAnimationFrame = jest.fn((callback) => { + callback(0); + return 0; + }); + window.cancelAnimationFrame = jest.fn(); + }); + + afterEach(() => { + cleanup(); + Object.defineProperty(svgElementPrototype, 'getComputedTextLength', { + configurable: true, + value: getComputedTextLength, + }); + window.requestAnimationFrame = requestAnimationFrame; + window.cancelAnimationFrame = cancelAnimationFrame; + jest.useRealTimers(); + }); test('should match snapshot', () => { const { asFragment } = render(); @@ -19,6 +48,16 @@ describe('test StatusTag', () => { expect(asFragment()).toMatchSnapshot(); }); + test('should remeasure width after hidden render', () => { + const measureWidth = svgElementPrototype.getComputedTextLength as jest.Mock; + measureWidth.mockReturnValueOnce(0).mockReturnValue(24); + + const { container } = render(); + + expect(container.querySelector('svg')?.getAttribute('width')).toBe('32'); + expect(container.querySelector('rect')?.getAttribute('width')).toBe('31'); + }); + test('should render custom color in default mode', () => { const { container } = render(); const rect = container.querySelector('rect'); diff --git a/src/tinyTag/demos/hidden.tsx b/src/tinyTag/demos/hidden.tsx new file mode 100644 index 000000000..f53c0f652 --- /dev/null +++ b/src/tinyTag/demos/hidden.tsx @@ -0,0 +1,18 @@ +import React, { useState } from 'react'; +import { Button, Space } from 'antd'; +import { TinyTag } from 'dt-react-component'; + +export default () => { + const [visible, setVisible] = useState(false); + + return ( + + +
+ +
+ + ); +}; diff --git a/src/tinyTag/index.md b/src/tinyTag/index.md index 52e3a1a98..c4d6404cb 100644 --- a/src/tinyTag/index.md +++ b/src/tinyTag/index.md @@ -13,6 +13,7 @@ TinyTag 组件通常用于在某些文案后面,用于标识当前行数据的 ## 示例 基础使用 +隐藏渲染 表格使用 ## API diff --git a/src/tinyTag/index.tsx b/src/tinyTag/index.tsx index 8c5fcd746..3364f9dbd 100644 --- a/src/tinyTag/index.tsx +++ b/src/tinyTag/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react'; import classNames from 'classnames'; import './style.scss'; @@ -29,17 +29,37 @@ const useSvgWidth = (): UseSvgWidthResult => { const [rectWidth, setRectWidth] = useState(0); const getWidth = useCallback(() => { - if (!element) return; + if (!element) return false; const paddingWidth = 8; - const textWidth = Math.round(element.getComputedTextLength?.() ?? 0); + const textWidth = Math.round(element.getComputedTextLength()); + if (!textWidth) return false; const svgWidth = textWidth + paddingWidth; setSvgWidth(svgWidth); setRectWidth(Math.max(svgWidth - 1, 0)); + return true; }, [element]); - useEffect(() => { + useLayoutEffect(() => { getWidth(); - }, [element]); + const timer = window.requestAnimationFrame(getWidth); + + return () => { + window.cancelAnimationFrame(timer); + }; + }, [getWidth]); + + useEffect(() => { + if (!element || !window.ResizeObserver) return undefined; + + const resizeObserver = new window.ResizeObserver(() => { + getWidth(); + }); + resizeObserver.observe(element); + + return () => { + resizeObserver.disconnect(); + }; + }, [element, getWidth]); return [ref, svgWidth, rectWidth]; };