Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// ---------------------------------------------------------------------------
const MOCK_SCREEN_H = 928;
const MOCK_SV = 1469;
const MOCK_SV_PAGE_Y = 116;

const mockInterpolateFn = (
value: number,
Expand Down Expand Up @@ -89,7 +90,7 @@ jest.mock("../../../utils/findNodeHandle", () => ({

jest.mock("../../../bindings", () => ({
KeyboardControllerNative: {
viewPositionInWindow: jest.fn().mockResolvedValue({ y: 0 }),
viewPositionInWindow: jest.fn().mockResolvedValue({ y: MOCK_SV_PAGE_Y }),
},
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const MOCK_SCREEN_HEIGHT = 928;
export const KEYBOARD_HEIGHT = 312;
export const BOTTOM_OFFSET = 62;
export const MOCK_SV_TARGET = 1469;
export const MOCK_SV_PAGE_Y = 116;
export const INPUT_TARGET_A = 1373;
export const INPUT_TARGET_B = 1395;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import "../__fixtures__/mocks";

import {
INPUT_LAYOUT_B,
INPUT_TARGET_B,
KEYBOARD_HEIGHT,
inputEvent,
kbEvent,
lastScrollToY,
mockInput,
mockKeyboardHandlers,
mockOffset,
mockScrollTo,
mockSelectionHandler,
renderKeyboardAwareScrollView,
reset,
selectionEvent,
} from "../__fixtures__/testUtils";

beforeEach(() => {
reset();
});

const EMOJI_KEYBOARD_HEIGHT = 388;

describe("KeyboardAwareScrollView β€” keyboard size change (emoji toggle)", () => {
// Input B: absoluteY=695.67, selectionHeight=47
// point = 695.67 + 47 = 742.67
//
// With text keyboard (312):
// visibleRect = 928 - 312 = 616
// relativeScrollTo = 312 - (928 - 742.67) + 62 = 188.67
//
// With emoji keyboard (388):
// visibleRect = 928 - 388 = 540
// Corrected absoluteY = 695.67 - 188.67 = 507
// point = 507 + 47 = 554
// relativeScrollTo = 388 - (928 - 554) + 62 = 76
// targetScrollY = 76 + 0 (scrollBeforeKeyboardMovement) = ... but
// interpolation base is scrollPosition=188.67, so:
// targetScrollY = 76 + 188.67 = 264.67

it("should not accumulate scroll when switching emoji ↔ text keyboard", async () => {
await renderKeyboardAwareScrollView();
mockInput.value = inputEvent(INPUT_TARGET_B, INPUT_LAYOUT_B);

// ---- First focus β€” keyboard opens ----
mockSelectionHandler.current(selectionEvent(INPUT_TARGET_B));
mockKeyboardHandlers.current.onStart(
kbEvent(KEYBOARD_HEIGHT, INPUT_TARGET_B),
);
mockKeyboardHandlers.current.onMove(
kbEvent(KEYBOARD_HEIGHT, INPUT_TARGET_B),
);
mockKeyboardHandlers.current.onEnd(
kbEvent(KEYBOARD_HEIGHT, INPUT_TARGET_B),
);
mockOffset.value = 188.67;

// ---- Switch to emoji keyboard (larger) ----
mockScrollTo.mockClear();

mockKeyboardHandlers.current.onStart({
height: EMOJI_KEYBOARD_HEIGHT,
target: INPUT_TARGET_B,
duration: 0,
progress: 1,
});
mockKeyboardHandlers.current.onEnd({
height: EMOJI_KEYBOARD_HEIGHT,
target: INPUT_TARGET_B,
duration: 0,
progress: 1,
});

const scrollAfterEmoji = lastScrollToY();

// Should scroll a bit more (keyboard grew), but NOT double the amount
expect(scrollAfterEmoji).toBeDefined();
expect(scrollAfterEmoji!).toBeLessThan(300);

// Simulate the scroll settling
mockOffset.value = scrollAfterEmoji!;

// ---- Switch back to text keyboard (smaller) ----
mockScrollTo.mockClear();

mockKeyboardHandlers.current.onStart(
kbEvent(KEYBOARD_HEIGHT, INPUT_TARGET_B),
);
mockKeyboardHandlers.current.onEnd(
kbEvent(KEYBOARD_HEIGHT, INPUT_TARGET_B),
);

const scrollAfterText = lastScrollToY();

// Key assertion: position should NOT exceed the emoji keyboard scroll
// (i.e. no accumulation happening)
expect(scrollAfterText ?? mockOffset.value).toBeLessThan(
scrollAfterEmoji! + 10,
);
});
});
27 changes: 22 additions & 5 deletions src/components/KeyboardAwareScrollView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,8 @@ const KeyboardAwareScrollView = forwardRef<
keyboardWillAppear.value = e.height > 0 && keyboardHeight.value === 0;

const keyboardWillHide = e.height === 0;
const focusWasChanged =
(tag.value !== e.target && e.target !== -1) ||
keyboardWillChangeSize;
const actualFocusChanged = tag.value !== e.target && e.target !== -1;
const focusWasChanged = actualFocusChanged || keyboardWillChangeSize;

if (keyboardWillChangeSize) {
initialKeyboardSize.value = keyboardHeight.value;
Expand Down Expand Up @@ -433,8 +432,26 @@ const KeyboardAwareScrollView = forwardRef<
}

// save current scroll position - when keyboard will hide we'll reuse
// this value to achieve smooth hide effect
scrollBeforeKeyboardMovement.value = position.value;
// this value to achieve smooth hide effect (only for actual focus change
// keyboard resize handled below)
if (actualFocusChanged) {
scrollBeforeKeyboardMovement.value = position.value;
}
}

// compute the actual on-screen position from the input's content-relative Y,
// current scroll offset, and ScrollView's position (case for keyboard resize)
if (keyboardWillChangeSize && !actualFocusChanged && layout.value) {
layout.value = {
...layout.value,
layout: {
...layout.value.layout,
absoluteY:
layout.value.layout.y -
position.value +
scrollViewPageY.value,
},
};
}

if (focusWasChanged && !keyboardWillAppear.value) {
Expand Down
Loading