Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ImagePreview): 支持taro下缩放效果。 #2939

Open
wants to merge 6 commits into
base: feat_v3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 148 additions & 127 deletions src/packages/imagepreview/__test__/imagepreview.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,148 +1,169 @@
import * as React from 'react'
import { render, waitFor, act } from '@testing-library/react'
import { render, screen, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
import { ImagePreview } from '../imagepreview'
import { triggerDrag } from '@/utils/test/event'

const images = [
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
},
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
},
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
},
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg',
},
]

const videos = [
{
source: {
src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
type: 'video/mp4',
describe('ImagePreview Component', () => {
const images = [
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
},
options: {
muted: true,
controls: true,
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
},
},
{
source: {
src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
type: 'video/mp4',
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
},
options: {
muted: true,
controls: true,
{
src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg',
},
},
]

function sleep(delay = 0): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, delay)
})
}
]

test('basic usage test', () => {
const { container } = render(<ImagePreview images={images} visible />)
const videos = [
{
source: {
src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
type: 'video/mp4',
},
options: {
muted: true,
controls: true,
},
},
{
source: {
src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
type: 'video/mp4',
},
options: {
muted: true,
controls: true,
},
},
]

const mockOnChange = vi.fn()
const mockOnClose = vi.fn()

const setup = (props = {}) => {
render(
<ImagePreview
images={images}
videos={videos}
visible
closeIcon
defaultValue={0}
onChange={mockOnChange}
onClose={mockOnClose}
{...props}
/>
)
}

const element = container.querySelector(
'.nut-imagepreview-pop'
) as HTMLElement
expect(element.style.display).toEqual('')
})
afterEach(() => {
vi.clearAllMocks()
})

test('test autoPlay', async () => {
let _container: any
act(() => {
test('renders correctly when visible', async () => {
const { container } = render(
<ImagePreview images={images} visible autoPlay={1000} />
<ImagePreview
images={images}
videos={videos}
visible
defaultValue={0}
onChange={mockOnChange}
onClose={mockOnClose}
/>
)
expect(screen.getByText('1/6')).toBeInTheDocument() // Assuming pagination is shown
expect((await container).getElementsByTagName('img')[0]).toHaveAttribute(
'src',
'//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg'
)
_container = container
})

const element = _container.querySelector(
'.nut-imagepreview-pop .nut-imagepreview-index'
) as HTMLElement
expect(element).toHaveTextContent('1')

await waitFor(
async () => {
await sleep(1100)
expect(element).toHaveTextContent('2')
},
{
timeout: 2000,
}
)
})

test('init page No.', async () => {
const { container } = render(
<ImagePreview images={images} visible defaultValue={3} />
)

const element = container.querySelector(
'.nut-imagepreview-pop .nut-imagepreview-index'
) as HTMLElement
expect(element).toHaveTextContent('3/4')
})

test('customize indicator and color', async () => {
const { container } = render(
<ImagePreview images={images} visible indicator indicatorColor="red" />
)

const swiperIndicator = container.querySelector('.nut-imagepreview-swiper')
expect(swiperIndicator).toHaveAttribute(
'style',
'--nutui-indicator-color: red;'
)
})

test('video surported in H5 env', async () => {
const { container } = render(
<ImagePreview images={images} videos={videos} visible />
)

const nutVideoPlayer = container.querySelector('.nut-video-player')
expect(nutVideoPlayer).toBeInTheDocument()
})

test('closeIcon = true', async () => {
const { container } = render(
<ImagePreview images={images} videos={videos} visible closeIcon />
)
test('calls onClose when close icon is clicked', async () => {
const { container } = render(
<ImagePreview images={images} visible closeIcon onClose={mockOnClose} />
)
const closeIcon = container.querySelector('.nut-imagepreview-close')
expect(closeIcon).toBeInTheDocument()
expect(closeIcon?.classList).toContain('top-right')
fireEvent.click(closeIcon as Element)
expect(mockOnClose).toHaveBeenCalledTimes(1)
})

const closeIcon = container.querySelector('.nut-imagepreview-close')
expect(closeIcon).toBeInTheDocument()
expect(closeIcon?.classList).toContain('top-right')
})
test('closes on content click if closeOnContentClick is true', async () => {
const { container } = render(
<ImagePreview
images={images}
visible
closeIcon
closeOnContentClick
onClose={mockOnClose}
/>
)
const imageElement = container.querySelector('.nut-image-default')
fireEvent.click(imageElement as Element)
expect(mockOnClose).toHaveBeenCalledTimes(1)
})

test('custom closeIcon', async () => {
const { container } = render(
<ImagePreview images={images} videos={videos} visible closeIcon="close" />
)
test('init page No.', async () => {
const { container } = render(
<ImagePreview images={images} visible defaultValue={3} />
)
const element = container.querySelector(
'.nut-imagepreview-pop .nut-imagepreview-index'
) as HTMLElement
expect(element).toHaveTextContent('3/4')
})

const closeIcon = container.querySelector('.nut-imagepreview-close')
expect(closeIcon?.innerHTML).toContain('close')
})
test('does not close on content click if closeOnContentClick is false', () => {
const { container } = render(
<ImagePreview images={images} visible closeOnContentClick={false} />
)
const imageElement = container.querySelector('.nut-image-default')
fireEvent.click(imageElement as Element)
expect(mockOnClose).toHaveBeenCalledTimes(0)
})

test('closeIconPosition', async () => {
const { container } = render(
<ImagePreview
images={images}
videos={videos}
visible
closeIcon
closeIconPosition="bottom"
/>
)
test('handles zooming in and out on touch events', () => {
const { container } = render(<ImagePreview images={images} visible />)
const swiperIndicator = container.querySelector(
'.nut-imagepreview'
) as Element

// Simulate touch start for zoom in
fireEvent.touchStart(swiperIndicator, {
touches: [
{ pageX: 100, pageY: 100 },
{ pageX: 200, pageY: 200 },
],
})

// Simulate touch move for zooming
fireEvent.touchMove(swiperIndicator, {
touches: [
{ pageX: 100, pageY: 100 },
{ pageX: 300, pageY: 300 },
],
})

// Verify that scale function has been called or scale state has changed
// Since we don't expose the scale, we may need to check the style if set
expect((swiperIndicator as HTMLElement).style.transform).toContain('scale(')
})

const closeIcon = container.querySelector('.nut-imagepreview-close')
expect(closeIcon?.classList).toContain('bottom')
test('autoPlay', async () => {
const { container } = render(
<ImagePreview images={images} videos={videos} visible autoPlay={2000} />
)
const swiper = container.querySelectorAll('.nut-swiper')[0]
const swiperItem = container.querySelector('.nut-swiper-slide')
triggerDrag(swiper, 220, 0)
expect(swiperItem).toHaveStyle({
transform: 'translate3d(100%,0,0)',
})
})
})
3 changes: 2 additions & 1 deletion src/packages/imagepreview/imagepreview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@
}

&-pop {
width: 100%;
height: 100%;
max-width: 100% !important;
background: transparent !important;
display: flex;
align-items: center;
width: 100%;
}

&-swiper {
Expand Down
Loading
Loading