@@ -131,11 +131,70 @@ test.describe('filtering', () => {
131131 } ;
132132 } ) ;
133133
134- expect ( panelLeft ) . toBeCloseTo ( buttonLeft , 0 ) ;
135- expect ( panelTop ) . toBeCloseTo ( buttonBottom , 0 ) ;
134+ expect ( Math . abs ( panelLeft - buttonLeft ) ) . toBeLessThanOrEqual ( 2 ) ;
135+ expect ( Math . abs ( panelTop - buttonBottom ) ) . toBeLessThanOrEqual ( 2 ) ;
136136 expect ( panelBottom ) . toBeGreaterThan ( wrapperBottom ) ;
137137 } ) ;
138138
139+ test ( 'flips filter dialog above the button near the viewport bottom' , async ( { page } ) => {
140+ await page . setViewportSize ( { width : 900 , height : 260 } ) ;
141+
142+ const source : SampleRow [ ] = [
143+ { id : 501 , name : 'Alice' , role : 'Admin' , city : 'Lisbon' } ,
144+ { id : 502 , name : 'Ben' , role : 'Engineer' , city : 'Porto' } ,
145+ ] ;
146+
147+ const columns = buildColumns ( [
148+ { prop : 'id' , name : 'ID' } ,
149+ { prop : 'name' , name : 'Name' } ,
150+ { prop : 'role' , name : 'Role' , filter : true , ...withHeaderTestId ( 'bottom-filter-role' ) } ,
151+ { prop : 'city' , name : 'City' } ,
152+ ] ) ;
153+
154+ await mountGrid ( page , { columns, source, filter : true , height : 160 } ) ;
155+ await page . locator ( 'revo-grid' ) . evaluate ( grid => {
156+ const wrapper = grid . parentElement ;
157+ if ( ! wrapper ) {
158+ throw new Error ( 'Grid wrapper was not found' ) ;
159+ }
160+ wrapper . style . marginTop = '160px' ;
161+ } ) ;
162+
163+ await page
164+ . getByTestId ( 'bottom-filter-role' )
165+ . locator ( SELECTORS . filterButton )
166+ . click ( ) ;
167+
168+ const filterPanel = page . locator ( SELECTORS . filterPanel ) ;
169+ await expect ( filterPanel ) . toBeVisible ( ) ;
170+
171+ const { actionBottom, buttonTop, panelBottom, panelTop, viewportBottom } = await page . evaluate ( ( ) => {
172+ const dialog = document . querySelector ( 'revogr-filter-panel dialog' ) ;
173+ const button = document
174+ . querySelector ( '[data-testid="bottom-filter-role"]' )
175+ ?. querySelector ( '.rv-filter' ) ;
176+ const actions = dialog ?. querySelector ( '.filter-actions' ) ;
177+ if ( ! button || ! dialog || ! actions ) {
178+ throw new Error ( 'Filter button, dialog, or filter actions were not found' ) ;
179+ }
180+ const buttonRect = button . getBoundingClientRect ( ) ;
181+ const panelRect = dialog . getBoundingClientRect ( ) ;
182+ const actionRect = actions . getBoundingClientRect ( ) ;
183+ return {
184+ actionBottom : actionRect . bottom ,
185+ buttonTop : buttonRect . top ,
186+ panelBottom : panelRect . bottom ,
187+ panelTop : panelRect . top ,
188+ viewportBottom : window . innerHeight ,
189+ } ;
190+ } ) ;
191+
192+ expect ( panelTop ) . toBeGreaterThanOrEqual ( 8 ) ;
193+ expect ( panelBottom ) . toBeLessThanOrEqual ( buttonTop + 2 ) ;
194+ expect ( panelBottom ) . toBeLessThanOrEqual ( viewportBottom - 8 ) ;
195+ expect ( Math . abs ( actionBottom - panelBottom ) ) . toBeLessThanOrEqual ( 2 ) ;
196+ } ) ;
197+
139198 test ( 'renders filter condition actions outside select input on the same row' , async ( { page } ) => {
140199 const source : SampleRow [ ] = [
141200 { id : 501 , name : 'Alice' , role : 'Admin' , city : 'Lisbon' } ,
@@ -223,8 +282,8 @@ test.describe('filtering', () => {
223282 await filterPanel . evaluate ( ( panel ) => {
224283 const rows = Array . from ( panel . querySelectorAll ( '.multi-filter-list-row' ) ) ;
225284 const sourceHandle = rows [ 1 ] . querySelector ( '.reorder-button' ) ;
226- const targetHandle = rows [ 0 ] . querySelector ( '.reorder-button' ) ;
227- if ( ! sourceHandle || ! targetHandle ) {
285+ const targetRow = rows [ 0 ] ;
286+ if ( ! sourceHandle || ! targetRow ) {
228287 throw new Error ( 'Filter reorder controls were not found' ) ;
229288 }
230289 const dataTransfer = new DataTransfer ( ) ;
@@ -233,12 +292,25 @@ test.describe('filtering', () => {
233292 cancelable : true ,
234293 dataTransfer,
235294 } ) ) ;
236- targetHandle . dispatchEvent ( new DragEvent ( 'dragover' , {
295+ targetRow . dispatchEvent ( new DragEvent ( 'dragover' , {
237296 bubbles : true ,
238297 cancelable : true ,
239298 dataTransfer,
240299 } ) ) ;
241- targetHandle . dispatchEvent ( new DragEvent ( 'drop' , {
300+ } ) ;
301+
302+ await expect ( filterPanel . locator ( '.multi-filter-list-row' ) . nth ( 0 ) ) . toHaveClass ( / f i l t e r - r o w - d r a g - o v e r / ) ;
303+
304+ await filterPanel . evaluate ( ( panel ) => {
305+ const rows = Array . from ( panel . querySelectorAll ( '.multi-filter-list-row' ) ) ;
306+ const sourceHandle = rows [ 1 ] . querySelector ( '.reorder-button' ) ;
307+ const targetRow = rows [ 0 ] ;
308+ if ( ! sourceHandle || ! targetRow ) {
309+ throw new Error ( 'Filter reorder controls were not found' ) ;
310+ }
311+ const dataTransfer = new DataTransfer ( ) ;
312+ dataTransfer . setData ( 'text/plain' , '2' ) ;
313+ targetRow . dispatchEvent ( new DragEvent ( 'drop' , {
242314 bubbles : true ,
243315 cancelable : true ,
244316 dataTransfer,
@@ -250,6 +322,7 @@ test.describe('filtering', () => {
250322 } ) ) ;
251323 } ) ;
252324
325+ await expect ( filterPanel . locator ( '.filter-row-drag-over' ) ) . toHaveCount ( 0 ) ;
253326 await expect ( filterPanel . locator ( '.select-filter' ) . nth ( 0 ) ) . toHaveValue ( 'eq' ) ;
254327 await expect ( filterPanel . locator ( '.select-filter' ) . nth ( 1 ) ) . toHaveValue ( 'contains' ) ;
255328 await expect ( filterInputs . nth ( 0 ) ) . toHaveValue ( 'Engineer' ) ;
0 commit comments