/** * This file is part of the NocoBase (R) project. * Copyright (c) 2020-2024 NocoBase Co., Ltd. * Authors: NocoBase Team. * * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. * For more information, please refer to: https://www.nocobase.com/agreement. */ import { act, render, screen, userEvent, waitFor } from '@nocobase/test/client'; import { ConfigProvider } from 'antd'; import React from 'react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { FlowEngineProvider } from '../../../provider'; import { FlowEngine } from '../../../flowEngine'; import LazyDropdown from '../LazyDropdown'; const setViewportHeight = (height: number) => { Object.defineProperty(window, 'innerHeight', { configurable: true, value: height, }); }; describe('LazyDropdown', () => { const originalInnerHeight = window.innerHeight; afterEach(() => { setViewportHeight(originalInnerHeight); vi.restoreAllMocks(); }); it('uses the current viewport space when opening after the viewport height changes', async () => { setViewportHeight(720); const engine = new FlowEngine(); const user = userEvent.setup(); render( , ); const trigger = screen.getByRole('button', { name: 'Open fields' }); vi.spyOn(trigger, 'getBoundingClientRect').mockReturnValue({ bottom: 196, height: 32, left: 49, right: 141, top: 164, width: 92, x: 49, y: 164, toJSON: () => ({}), }); setViewportHeight(460); await user.click(trigger); const menu = await screen.findByRole('menu'); await waitFor(() => expect(menu).toHaveStyle({ maxHeight: '256px', overflowY: 'auto' })); }); it('updates the available height while the dropdown is open', async () => { setViewportHeight(720); const engine = new FlowEngine(); const user = userEvent.setup(); render( , ); const trigger = screen.getByRole('button', { name: 'Open fields' }); vi.spyOn(trigger, 'getBoundingClientRect').mockReturnValue({ bottom: 196, height: 32, left: 49, right: 141, top: 164, width: 92, x: 49, y: 164, toJSON: () => ({}), }); await user.click(trigger); const menu = await screen.findByRole('menu'); await waitFor(() => expect(menu).toHaveStyle({ maxHeight: '400px', overflowY: 'auto' })); act(() => { setViewportHeight(460); window.dispatchEvent(new Event('resize')); }); await waitFor(() => expect(menu).toHaveStyle({ maxHeight: '256px', overflowY: 'auto' })); act(() => { setViewportHeight(720); window.dispatchEvent(new Event('resize')); }); await waitFor(() => expect(menu).toHaveStyle({ maxHeight: '400px', overflowY: 'auto' })); }); it('reserves the placement offset when the dropdown has an arrow', async () => { setViewportHeight(460); const engine = new FlowEngine(); const user = userEvent.setup(); render( , ); const trigger = screen.getByRole('button', { name: 'Open fields' }); vi.spyOn(trigger, 'getBoundingClientRect').mockReturnValue({ bottom: 196, height: 32, left: 49, right: 141, top: 164, width: 92, x: 49, y: 164, toJSON: () => ({}), }); await user.click(trigger); const menu = await screen.findByRole('menu'); await waitFor(() => expect(menu).toHaveStyle({ maxHeight: '248px', overflowY: 'auto' })); }); it('uses the space above when it is larger than the space below', async () => { setViewportHeight(460); const engine = new FlowEngine(); const user = userEvent.setup(); render( , ); const trigger = screen.getByRole('button', { name: 'Open fields' }); vi.spyOn(trigger, 'getBoundingClientRect').mockReturnValue({ bottom: 332, height: 32, left: 49, right: 141, top: 300, width: 92, x: 49, y: 300, toJSON: () => ({}), }); await user.click(trigger); const menu = await screen.findByRole('menu'); await waitFor(() => expect(menu).toHaveStyle({ maxHeight: '292px', overflowY: 'auto' })); }); });