🐛(frontend) fix emojipicker closing
In the tree view, if the emoji picker is opened near the bottom of the viewport, it would trigger an overflow that rerendered the treeview and closed the picker immediately. The root problem is the treeview that rerender because of not stable props. To fix this, we change 2 things: - we use "fixed" position for the emoji picker so it won't affect the document flow - we adjust the position calculation logic, if the picker does not have enough space below, we position it above the icon instead.
This commit is contained in:
@@ -14,6 +14,7 @@ and this project adheres to
|
||||
|
||||
- ✅(e2e) fix e2e test for other browsers #1799
|
||||
- 🐛(frontend) add fallback for unsupported Blocknote languages #1810
|
||||
- 🐛(frontend) fix emojipicker closing in tree #1808
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
createDoc,
|
||||
expectLoginPage,
|
||||
keyCloakSignIn,
|
||||
randomName,
|
||||
updateDocTitle,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
@@ -20,50 +19,6 @@ test.describe('Doc Tree', () => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('create new sub pages', async ({ page, browserName }) => {
|
||||
const [titleParent] = await createDoc(
|
||||
page,
|
||||
'doc-tree-content',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await verifyDocName(page, titleParent);
|
||||
const addButton = page.getByTestId('new-doc-button');
|
||||
const docTree = page.getByTestId('doc-tree');
|
||||
|
||||
await expect(addButton).toBeVisible();
|
||||
|
||||
// Wait for and intercept the POST request to create a new page
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/documents/') &&
|
||||
response.url().includes('/children/') &&
|
||||
response.request().method() === 'POST',
|
||||
);
|
||||
|
||||
await clickOnAddRootSubPage(page);
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
const subPageJson = await response.json();
|
||||
|
||||
await expect(docTree).toBeVisible();
|
||||
const subPageItem = docTree
|
||||
.getByTestId(`doc-sub-page-item-${subPageJson.id}`)
|
||||
.first();
|
||||
|
||||
await expect(subPageItem).toBeVisible();
|
||||
await subPageItem.click();
|
||||
await verifyDocName(page, '');
|
||||
const input = page.getByRole('textbox', { name: 'Document title' });
|
||||
await input.click();
|
||||
const [randomDocName] = randomName('doc-tree-test', browserName, 1);
|
||||
await input.fill(randomDocName);
|
||||
await input.press('Enter');
|
||||
await expect(subPageItem.getByText(randomDocName)).toBeVisible();
|
||||
await page.reload();
|
||||
await expect(subPageItem.getByText(randomDocName)).toBeVisible();
|
||||
});
|
||||
|
||||
test('check the reorder of sub pages', async ({ page, browserName }) => {
|
||||
await createDoc(page, 'doc-tree-content', browserName, 1);
|
||||
const addButton = page.getByTestId('new-doc-button');
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
export const PICKER_HEIGHT = 500;
|
||||
|
||||
interface EmojiPickerProps {
|
||||
emojiData: EmojiMartData;
|
||||
onClickOutside: () => void;
|
||||
@@ -27,12 +29,7 @@ export const EmojiPicker = ({
|
||||
};
|
||||
|
||||
const pickerContent = (
|
||||
<Box
|
||||
$position="absolute"
|
||||
$zIndex={1000}
|
||||
$margin="2rem 0 0 0"
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
>
|
||||
<Box $position="absolute" $zIndex={1000} onKeyDownCapture={handleKeyDown}>
|
||||
<Picker
|
||||
data={emojiData}
|
||||
locale={i18n.resolvedLanguage}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { MouseEvent, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import {
|
||||
Box,
|
||||
BoxButton,
|
||||
BoxButtonType,
|
||||
EmojiPicker,
|
||||
PICKER_HEIGHT,
|
||||
Text,
|
||||
TextType,
|
||||
emojidata,
|
||||
@@ -66,9 +69,24 @@ export const DocIcon = ({
|
||||
|
||||
if (!openEmojiPicker && iconRef.current) {
|
||||
const rect = iconRef.current.getBoundingClientRect();
|
||||
|
||||
const pickerHeight = PICKER_HEIGHT;
|
||||
const spaceBelow = window.innerHeight - rect.bottom;
|
||||
const spaceAbove = rect.top;
|
||||
|
||||
// Position picker above if not enough space below and enough space above
|
||||
const shouldPositionAbove =
|
||||
spaceBelow < pickerHeight && spaceAbove >= pickerHeight;
|
||||
|
||||
// Offset to align the picker properly
|
||||
const ROW_OFFSET_TOP = 55;
|
||||
const ROW_OFFSET_BOTTOM = 10;
|
||||
|
||||
setPickerPosition({
|
||||
top: rect.bottom + window.scrollY + 8,
|
||||
left: rect.left + window.scrollX,
|
||||
top: shouldPositionAbove
|
||||
? rect.top - pickerHeight + ROW_OFFSET_TOP
|
||||
: rect.bottom + ROW_OFFSET_BOTTOM,
|
||||
left: rect.left,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,13 +138,13 @@ export const DocIcon = ({
|
||||
</BoxButton>
|
||||
{openEmojiPicker &&
|
||||
createPortal(
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: pickerPosition.top,
|
||||
left: pickerPosition.left,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
<Box
|
||||
$position="fixed"
|
||||
$css={css`
|
||||
top: ${pickerPosition.top}px;
|
||||
left: ${pickerPosition.left}px;
|
||||
z-index: 1000;
|
||||
`}
|
||||
>
|
||||
<EmojiPicker
|
||||
emojiData={emojidata}
|
||||
@@ -134,7 +152,7 @@ export const DocIcon = ({
|
||||
onClickOutside={handleClickOutside}
|
||||
withOverlay={true}
|
||||
/>
|
||||
</div>,
|
||||
</Box>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user