Skip to content

Commit 877542a

Browse files
byq1213anlyyao
andauthored
更新Image组件示例对齐 (#769)
* fix(image): update Image component examples * fix(image): update unit test * chore: formatting unit test files * chore: update demo --------- Co-authored-by: anlyyao <[email protected]>
1 parent 80a7930 commit 877542a

File tree

10 files changed

+567
-488
lines changed

10 files changed

+567
-488
lines changed

src/image/__tests__/image.test.tsx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import '@testing-library/jest-dom';
2+
import { fireEvent, render, waitFor, afterEach } from '@test/utils';
3+
import React from 'react';
4+
import { describe, expect, it, vi } from 'vitest';
5+
import Image from '../Image';
6+
7+
// 在测试文件中定义局部的 IntersectionObserver mock
8+
const mockIntersectionObserver = () => {
9+
const observe = vi.fn();
10+
const unobserve = vi.fn();
11+
const disconnect = vi.fn();
12+
13+
// 使用 vi.fn() 模拟 IntersectionObserver
14+
const MockIntersectionObserver = vi.fn(() => ({
15+
observe,
16+
unobserve,
17+
disconnect,
18+
}));
19+
20+
// 仅在当前测试文件中替换 IntersectionObserver
21+
vi.stubGlobal('IntersectionObserver', MockIntersectionObserver);
22+
23+
return {
24+
observe,
25+
unobserve,
26+
disconnect,
27+
MockIntersectionObserver,
28+
};
29+
};
30+
31+
describe('Image', () => {
32+
describe(': props', () => {
33+
afterEach(() => {
34+
vi.unstubAllGlobals();
35+
});
36+
37+
it(': src', () => {
38+
const { getByRole } = render(<Image src="test.png" />);
39+
expect(getByRole('img')).toBeInTheDocument();
40+
});
41+
42+
it(': alt', () => {
43+
const { getByAltText } = render(<Image alt="测试图片" src="test.png" />);
44+
expect(getByAltText('测试图片')).toHaveAttribute('src', 'test.png');
45+
});
46+
47+
it(': shape', () => {
48+
const shapes = ['circle', 'round', 'square'] as const;
49+
shapes.forEach((shape) => {
50+
const { container } = render(<Image shape={shape} src="test.png" />);
51+
expect(container.firstChild).toHaveClass(`t-image--${shape}`);
52+
});
53+
});
54+
55+
it(': fit', () => {
56+
const { container } = render(<Image fit="cover" src="test.png" />);
57+
const img = container.querySelector('.t-image__img');
58+
expect(img).toHaveClass('t-image--fit-cover');
59+
});
60+
61+
it(': position', () => {
62+
const { getByRole } = render(<Image position="top center" src="test.png" />);
63+
expect(getByRole('img')).toHaveClass('t-image--position-top center');
64+
});
65+
66+
it(': referrerpolicy', () => {
67+
const { getByRole } = render(<Image referrerpolicy="no-referrer" src="test.png" />);
68+
expect(getByRole('img')).toHaveAttribute('referrerPolicy', 'no-referrer');
69+
});
70+
71+
// 加载状态测试
72+
it(': loading(custom)', () => {
73+
const { container } = render(<Image loading={<span>加载中...</span>} />);
74+
expect(container.textContent).toContain('加载中');
75+
});
76+
77+
it(': loading(default)', () => {
78+
const { container } = render(<Image />);
79+
expect(container.querySelector('.t-loading')).toBeInTheDocument();
80+
});
81+
82+
it(': error', async () => {
83+
const { getByRole, getByText } = render(<Image src="invalid.png" error={<span>加载失败</span>} />);
84+
const img = getByRole('img');
85+
fireEvent.error(img);
86+
87+
await waitFor(() => {
88+
expect(getByText('加载失败')).toBeInTheDocument();
89+
});
90+
});
91+
92+
it(': srcset', () => {
93+
const srcset = {
94+
'image/avif': 'test.avif',
95+
'image/webp': 'test.webp',
96+
};
97+
const { getByRole, container } = render(<Image src="test.jpg" srcset={srcset} />);
98+
99+
const avifSource = container.querySelector('source[type="image/avif"]');
100+
const webpSource = container.querySelector('source[type="image/webp"]');
101+
expect(avifSource).toHaveAttribute('srcset', 'test.avif');
102+
expect(webpSource).toHaveAttribute('srcset', 'test.webp');
103+
expect(getByRole('img')).toHaveAttribute('src', 'test.jpg');
104+
});
105+
106+
it(': lazy', () => {
107+
const { observe, MockIntersectionObserver } = mockIntersectionObserver();
108+
109+
const { container } = render(<Image src="test.png" lazy />);
110+
111+
// 验证 IntersectionObserver 被正确初始化并使用
112+
expect(MockIntersectionObserver).toHaveBeenCalled();
113+
expect(observe).toHaveBeenCalledWith(container.firstElementChild);
114+
});
115+
116+
it(': onLoad', async () => {
117+
const onLoad = vi.fn();
118+
const { getByRole } = render(<Image src="test.png" onLoad={onLoad} />);
119+
const img = getByRole('img');
120+
fireEvent.load(img);
121+
122+
await waitFor(() => {
123+
expect(onLoad).toHaveBeenCalled();
124+
});
125+
});
126+
127+
it(': onError', async () => {
128+
const onError = vi.fn();
129+
const { getByRole } = render(<Image src="invalid.png" fallback="fallback.png" onError={onError} />);
130+
const img = getByRole('img');
131+
fireEvent.error(img);
132+
133+
await waitFor(() => {
134+
expect(onError).toHaveBeenCalled();
135+
});
136+
});
137+
});
138+
});

src/image/_example/base.tsx

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,39 @@
11
import React from 'react';
22
import { Image } from 'tdesign-mobile-react';
3+
import './style/index.less';
34

4-
const imageSrc = 'https://tdesign.gtimg.com/demo/demo-image-1.png';
5+
const imageSrc = 'https://tdesign.gtimg.com/mobile/demos/image.png';
56

67
export default function BaseImage() {
78
return (
89
<div className="image-example">
910
<div className="image-example-title">不同填充模式的图片</div>
1011
<div className="image-example-desc">提供 fill、contain、cover、none、scale-down 5 种填充类型。</div>
11-
<div className="image-group">
12-
<div className="image-demo">
12+
<div className="image-group mt-8">
13+
<div className="image-demo mr-24">
1314
<p className="image-demo-tip">fill</p>
14-
<Image
15-
className="image-container"
16-
style={{ width: '72px', height: '72px' }}
17-
fit="fill"
18-
src={imageSrc}
19-
></Image>
15+
<Image className="image-container" style={{ width: '72px', height: '72px' }} fit="fill" src={imageSrc} />
2016
</div>
21-
<div className="image-demo">
17+
<div className="image-demo mr-24">
2218
<p className="image-demo-tip">contain</p>
23-
<Image
24-
className="image-container"
25-
style={{ width: '72px', height: '72px' }}
26-
fit="contain"
27-
src={imageSrc}
28-
></Image>
19+
<Image className="image-container" style={{ width: '89px', height: '72px' }} fit="contain" src={imageSrc} />
2920
</div>
3021
<div className="image-demo">
3122
<p className="image-demo-tip">cover</p>
32-
<Image
33-
className="image-container"
34-
style={{ width: '72px', height: '72px' }}
35-
fit="cover"
36-
src={imageSrc}
37-
></Image>
23+
<Image className="image-container" style={{ width: '134px', height: '72px' }} fit="cover" src={imageSrc} />
3824
</div>
39-
<div className="image-demo">
25+
<div className="image-demo mt-24 mr-24">
4026
<p className="image-demo-tip">none</p>
41-
<Image
42-
className="image-container"
43-
style={{ width: '72px', height: '72px' }}
44-
fit="none"
45-
src={imageSrc}
46-
></Image>
27+
<Image className="image-container" style={{ width: '72px', height: '72px' }} fit="none" src={imageSrc} />
4728
</div>
48-
<div className="image-demo">
29+
<div className="image-demo mt-24">
4930
<p className="image-demo-tip">scale-down</p>
5031
<Image
5132
className="image-container"
5233
style={{ width: '72px', height: '72px' }}
5334
fit="scale-down"
5435
src={imageSrc}
55-
></Image>
36+
/>
5637
</div>
5738
</div>
5839
</div>

src/image/_example/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import StatusImage from './status';
99
export default function ImageDemo() {
1010
return (
1111
<>
12-
<TDemoHeader title="Image 图片" summary="用于展示图片素材" />
12+
<TDemoHeader title="Image 图片" summary="用于展示效果,主要为上下左右居中裁切、拉伸、平铺等方式。" />
1313
<TDemoBlock title="01 组件类型" padding={true}>
1414
<BaseImage />
1515
<PositionImage />

src/image/_example/position.tsx

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import React from 'react';
22
import { Image } from 'tdesign-mobile-react';
3-
43
import './style/index.less';
54

6-
const imageSrc = 'https://tdesign.gtimg.com/demo/demo-image-1.png';
5+
const imageSrc = 'https://tdesign.gtimg.com/mobile/demos/image.png';
76

87
export default function PositionImage() {
98
return (
10-
<div className="image-example">
9+
<div className="image-example mt-24">
1110
<div className="image-example-title">不同填充位置的图片</div>
1211
<div className="image-example-desc">当图片过大时,提供显示图片的局部左侧对齐、或右侧对齐的不同位置。</div>
13-
<div className="image-group">
14-
<div className="image-demo">
12+
<div className="image-group mt-8">
13+
<div className="image-demo mr-24">
1514
<p className="image-demo-tip">cover center</p>
1615
<Image
1716
className="image-container"
@@ -21,7 +20,7 @@ export default function PositionImage() {
2120
src={imageSrc}
2221
/>
2322
</div>
24-
<div className="image-demo">
23+
<div className="image-demo mr-24">
2524
<p className="image-demo-tip">cover left</p>
2625
<Image
2726
className="image-container"
@@ -41,71 +40,59 @@ export default function PositionImage() {
4140
src={imageSrc}
4241
/>
4342
</div>
44-
<div className="image-demo">
43+
<div className="image-demo mt-24">
4544
<p className="image-demo-tip">cover top</p>
46-
<Image
47-
className="image-container"
48-
style={{ width: '72px', height: '72px' }}
49-
fit="cover"
50-
position="top"
51-
src={imageSrc}
52-
/>
45+
<Image style={{ width: '240px', height: '80px' }} fit="cover" position="top" src={imageSrc} />
5346
</div>
54-
<div className="image-demo">
47+
<div className="image-demo mt-24" style={{ marginRight: '48px' }}>
5548
<p className="image-demo-tip">cover bottom</p>
56-
<Image
57-
className="image-container"
58-
style={{ width: '72px', height: '72px' }}
59-
fit="cover"
60-
position="bottom"
61-
src={imageSrc}
62-
/>
49+
<Image style={{ width: '240px', height: '80px' }} fit="cover" position="bottom" src={imageSrc} />
6350
</div>
64-
<div className="image-demo">
51+
<div className="image-demo mt-24 mr-24">
6552
<p className="image-demo-tip">contain top</p>
6653
<Image
6754
className="image-container"
68-
style={{ width: '72px', height: '72px' }}
55+
style={{ width: '72px', height: '80px' }}
6956
fit="contain"
7057
position="top"
7158
src={imageSrc}
72-
/>
59+
></Image>
7360
</div>
74-
<div className="image-demo">
61+
<div className="image-demo mt-24 mr-24">
7562
<p className="image-demo-tip">contain bottom</p>
7663
<Image
7764
className="image-container"
78-
style={{ width: '72px', height: '72px' }}
65+
style={{ width: '72px', height: '80px' }}
7966
fit="contain"
8067
position="bottom"
8168
src={imageSrc}
82-
/>
69+
></Image>
8370
</div>
84-
<div className="image-demo">
71+
<div className="image-demo mt-24">
8572
<p className="image-demo-tip">contain center</p>
8673
<Image
8774
className="image-container"
88-
style={{ width: '72px', height: '72px' }}
75+
style={{ width: '72px', height: '80px' }}
8976
fit="contain"
9077
position="center"
9178
src={imageSrc}
92-
/>
79+
></Image>
9380
</div>
94-
<div className="image-demo">
81+
<div className="image-demo mt-24">
9582
<p className="image-demo-tip">contain left</p>
9683
<Image
9784
className="image-container"
98-
style={{ width: '72px', height: '72px' }}
85+
style={{ width: '180px', height: '80px' }}
9986
fit="contain"
10087
position="left"
10188
src={imageSrc}
10289
/>
10390
</div>
104-
<div className="image-demo">
91+
<div className="image-demo mt-24">
10592
<p className="image-demo-tip">contain right</p>
10693
<Image
10794
className="image-container"
108-
style={{ width: '72px', height: '72px' }}
95+
style={{ width: '180px', height: '80px' }}
10996
fit="contain"
11097
position="right"
11198
src={imageSrc}

src/image/_example/shape.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
import React from 'react';
22
import { Image } from 'tdesign-mobile-react';
33

4-
const imageSrc = 'https://tdesign.gtimg.com/demo/demo-image-1.png';
4+
const imageSrc = 'https://tdesign.gtimg.com/mobile/demos/image.png';
55

66
export default function RoundDemo() {
77
return (
8-
<div className="image-example">
9-
<div className="image-example-title">不同形状的图片</div>
10-
<div className="image-example-desc">
11-
提供方形、圆角方形、圆角 3 种形状。 当图片长宽不相等时,无法使用 circle 展示一个正圆。
12-
</div>
8+
<div className="image-example mt-24">
139
<div className="image-group">
14-
<div className="image-demo">
10+
<div className="image-demo mr-24">
1511
<p className="image-demo-tip">方形</p>
16-
<Image className="image-container" src={imageSrc} />
12+
<Image className="image-container" src={imageSrc} fit="cover" />
1713
</div>
18-
<div className="image-demo">
14+
<div className="image-demo mr-24">
1915
<p className="image-demo-tip">圆角方形</p>
20-
<Image className="image-container" src={imageSrc} shape="round" />
16+
<Image className="image-container" src={imageSrc} shape="round" fit="cover" />
2117
</div>
2218
<div className="image-demo">
2319
<p className="image-demo-tip">圆形</p>

0 commit comments

Comments
 (0)