Skip to content

Commit

Permalink
fix(js): fix panel placement after scroll (#593)
Browse files Browse the repository at this point in the history
  • Loading branch information
shortcuts authored May 25, 2021
1 parent 67e7bbf commit ca396ad
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 8 deletions.
157 changes: 151 additions & 6 deletions packages/autocomplete-js/src/__tests__/panelPlacement.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { waitFor } from '@testing-library/dom';
import { fireEvent, waitFor } from '@testing-library/dom';

import { autocomplete } from '../autocomplete';

Expand All @@ -10,6 +10,7 @@ const LEFT = 11;
const RIGHT = 13;
const TOP = 17;
const WIDTH = 19;
const SCROLL = 100;

describe('panelPlacement', () => {
let container: HTMLDivElement;
Expand Down Expand Up @@ -45,6 +46,7 @@ describe('panelPlacement', () => {

afterEach(() => {
document.body.innerHTML = '';
fireEvent.scroll(document.body, { target: { scrollTop: 0 } });
});

afterAll(() => {
Expand Down Expand Up @@ -75,7 +77,37 @@ describe('panelPlacement', () => {

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '24px', // TOP + HEIGHT
top: '24px', // TOP + HEIGHT + SCROLL
left: '11px', // LEFT
right: '1890px', // CLIENT_WIDTH - (LEFT + WIDTH)
width: 'unset',
'max-width': 'unset',
});
});
});

test('keeps the panel positionned after scrolling', async () => {
autocomplete({
container,
panelPlacement: 'input-wrapper-width',
initialState: {
isOpen: true,
},
});

fireEvent.scroll(document.body, { target: { scrollTop: SCROLL } });

// Mock `getBoundingClientRect` for elements used in the panel placement calculation
document.querySelector(
'.aa-Form'
).getBoundingClientRect = mockedGetBoundingClientRect;
document.querySelector(
'.aa-Autocomplete'
).getBoundingClientRect = mockedGetBoundingClientRect;

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '124px', // TOP + HEIGHT + SCROLL
left: '11px', // LEFT
right: '1890px', // CLIENT_WIDTH - (LEFT + WIDTH)
width: 'unset',
Expand Down Expand Up @@ -105,7 +137,34 @@ describe('panelPlacement', () => {

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '24px', // TOP + HEIGHT
top: '24px', // TOP + HEIGHT + SCROLL
left: '11px', // LEFT
});
});
});

test('keeps the panel positionned after scrolling', async () => {
autocomplete({
container,
panelPlacement: 'start',
initialState: {
isOpen: true,
},
});

fireEvent.scroll(document.body, { target: { scrollTop: SCROLL } });

// Mock `getBoundingClientRect` for elements used in the panel placement calculation
document.querySelector(
'.aa-Form'
).getBoundingClientRect = mockedGetBoundingClientRect;
document.querySelector(
'.aa-Autocomplete'
).getBoundingClientRect = mockedGetBoundingClientRect;

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '124px', // TOP + HEIGHT + SCROLL
left: '11px', // LEFT
});
});
Expand All @@ -132,7 +191,34 @@ describe('panelPlacement', () => {

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '24px', // TOP + HEIGHT
top: '24px', // TOP + HEIGHT + SCROLL
right: '1890px', // CLIENT_WIDTH - (LEFT + WIDTH)
});
});
});

test('keeps the panel positionned after scrolling', async () => {
autocomplete({
container,
panelPlacement: 'end',
initialState: {
isOpen: true,
},
});

fireEvent.scroll(document.body, { target: { scrollTop: SCROLL } });

// Mock `getBoundingClientRect` for elements used in the panel placement calculation
document.querySelector(
'.aa-Form'
).getBoundingClientRect = mockedGetBoundingClientRect;
document.querySelector(
'.aa-Autocomplete'
).getBoundingClientRect = mockedGetBoundingClientRect;

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '124px', // TOP + HEIGHT + SCROLL
right: '1890px', // CLIENT_WIDTH - (LEFT + WIDTH)
});
});
Expand All @@ -159,7 +245,37 @@ describe('panelPlacement', () => {

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '24px', // TOP + HEIGHT
top: '24px', // TOP + HEIGHT + SCROLL
left: 0,
right: 0,
width: 'unset',
'max-width': 'unset',
});
});
});

test('keeps the panel positionned after scrolling', async () => {
autocomplete({
container,
panelPlacement: 'full-width',
initialState: {
isOpen: true,
},
});

fireEvent.scroll(document.body, { target: { scrollTop: SCROLL } });

// Mock `getBoundingClientRect` for elements used in the panel placement calculation
document.querySelector(
'.aa-Form'
).getBoundingClientRect = mockedGetBoundingClientRect;
document.querySelector(
'.aa-Autocomplete'
).getBoundingClientRect = mockedGetBoundingClientRect;

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '124px', // TOP + HEIGHT + SCROLL
left: 0,
right: 0,
width: 'unset',
Expand Down Expand Up @@ -187,7 +303,7 @@ describe('panelPlacement', () => {

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '24px', // TOP + HEIGHT
top: '24px', // TOP + HEIGHT + SCROLL
left: '11px', // LEFT
right: '1890px', // CLIENT_WIDTH - (LEFT + WIDTH)
width: 'unset',
Expand Down Expand Up @@ -223,4 +339,33 @@ describe('panelPlacement', () => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({});
});
});

test('default value keeps the panel positionned after scrolling', async () => {
autocomplete({
container,
initialState: {
isOpen: true,
},
});

fireEvent.scroll(document.body, { target: { scrollTop: SCROLL } });

// Mock `getBoundingClientRect` for elements used in the panel placement calculation
document.querySelector(
'.aa-Form'
).getBoundingClientRect = mockedGetBoundingClientRect;
document.querySelector(
'.aa-Autocomplete'
).getBoundingClientRect = mockedGetBoundingClientRect;

await waitFor(() => {
expect(document.querySelector('.aa-Panel')).toHaveStyle({
top: '124px', // TOP + HEIGHT + SCROLL
left: '11px', // LEFT
right: '1890px', // CLIENT_WIDTH - (LEFT + WIDTH)
width: 'unset',
'max-width': 'unset',
});
});
});
});
35 changes: 34 additions & 1 deletion packages/autocomplete-js/src/__tests__/positioning.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AutocompletePlugin } from '@algolia/autocomplete-core';
import { waitFor, getByTestId } from '@testing-library/dom';
import { waitFor, getByTestId, fireEvent } from '@testing-library/dom';
import userEvent from '@testing-library/user-event';

import { autocomplete } from '../';
Expand Down Expand Up @@ -130,6 +130,39 @@ describe('Panel positioning', () => {
});
});

test('keeps the panel positionned after scrolling', async () => {
const container = document.createElement('div');
const panelContainer = document.body;
document.body.appendChild(container);

autocomplete({
id: 'autocomplete-0',
container,
panelContainer,
plugins: [querySuggestionsFixturePlugin],
});

const root = document.querySelector<HTMLDivElement>('.aa-Autocomplete');
root.getBoundingClientRect = jest.fn().mockReturnValue(rootPosition);
const form = document.querySelector<HTMLFormElement>('.aa-Form');
form.getBoundingClientRect = jest.fn().mockReturnValue(formPosition);

const input = document.querySelector<HTMLInputElement>('.aa-Input');
userEvent.type(input, 'a');

fireEvent.scroll(document.body, { target: { scrollTop: 100 } });

await waitFor(() => getByTestId(panelContainer, 'panel'));

expect(getByTestId(panelContainer, 'panel')).toHaveStyle({
top: '140px',
left: '300px',
right: '1020px',
});

fireEvent.scroll(document.body, { target: { scrollTop: 0 } });
});

test('repositions the panel below the root element after a UI change', async () => {
const container = document.createElement('div');
const panelContainer = document.body;
Expand Down
5 changes: 4 additions & 1 deletion packages/autocomplete-js/src/getPanelPlacementStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ export function getPanelPlacementStyle({
environment,
}: GetPanelPlacementStyleParams) {
const containerRect = container.getBoundingClientRect();
const top = containerRect.top + containerRect.height;
const top =
environment.document.body.scrollTop +
containerRect.top +
containerRect.height;

switch (panelPlacement) {
case 'start': {
Expand Down

0 comments on commit ca396ad

Please sign in to comment.