mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Expand the emoji picker to full height when the keyboard is shown (#7382)
* Expand the emoji picker to full height when the keyboard is shown The picker content is rendered with fillMaxSize, so the sheet measures itself to the full window height. That makes the partially expanded anchor available, and the modal sheet auto-shows at that anchor, half way down the screen. The sheet reserves the IME inset through its content window insets, but applies that padding at its own bottom edge, which is off-screen while it rests half way down. The band that is both on-screen and above the keyboard is then only about a tenth of the screen, which is why the matching emoji end up behind it. Expand the sheet while the keyboard is visible so its bottom edge returns to the screen edge and the inset it already reserves lands on top of the keyboard. Modifier.imePadding on the sheet content is not an option: the sheet applies the insets with windowInsetsPadding, which consumes them, so a nested imePadding reads zero. * Cover the keyboard driven sheet expansion with a test The IME reaction moves into ExpandSheetWhenImeIsVisible so a Robolectric test can drive it: dispatching window insets to the composition flips WindowInsets.isImeVisible, and the sheet state is observable from the test. Both the shown and the hidden case are asserted, which is the patch coverage this change was missing. * Do not leave a blank strip below the expanded emoji search The 10dp of bottom padding on the search bar separates the collapsed field from the category tabs underneath it. It stayed applied once the search bar expanded to fill the picker, so the picker stopped 10dp short of its own bottom edge. That strip used to sit below the visible part of a half height sheet. Now that the sheet expands against the keyboard it reads as a gap between the results and the keyboard, so only pad while collapsed.
This commit is contained in:
@@ -36,5 +36,5 @@ dependencies {
|
||||
implementation(libs.matrix.emojibase.bindings)
|
||||
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testCommonDependencies(libs)
|
||||
testCommonDependencies(libs, includeTestComposeView = true)
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ internal fun EmojiPickerView(
|
||||
|
||||
Column(modifier) {
|
||||
SearchBar(
|
||||
modifier = Modifier.padding(bottom = 10.dp),
|
||||
modifier = Modifier.padding(bottom = if (state.isSearchActive) 0.dp else 10.dp),
|
||||
queryState = state.searchQuery,
|
||||
resultState = state.searchResults,
|
||||
active = state.isSearchActive,
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalTestApi::class)
|
||||
|
||||
package io.element.android.libraries.emoji.impl.picker
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.test.AndroidComposeUiTest
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.assertIsEqualTo
|
||||
import androidx.compose.ui.test.getBoundsInRoot
|
||||
import androidx.compose.ui.test.hasScrollToIndexAction
|
||||
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.emojibasebindings.Emoji
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import org.junit.Test
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
private val PICKER_HEIGHT = 600.dp
|
||||
|
||||
class EmojiPickerViewTest : RobolectricTest() {
|
||||
@Config(qualifiers = "w400dp-h800dp")
|
||||
@Test
|
||||
fun `the search results reach the bottom of the picker`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
setEmojiPickerView()
|
||||
onNode(hasScrollToIndexAction())
|
||||
.getBoundsInRoot()
|
||||
.bottom
|
||||
.assertIsEqualTo(PICKER_HEIGHT, "emoji grid bottom")
|
||||
}
|
||||
}
|
||||
|
||||
private fun AndroidComposeUiTest<ComponentActivity>.setEmojiPickerView() {
|
||||
setContent {
|
||||
Box(Modifier.size(400.dp, PICKER_HEIGHT)) {
|
||||
EmojiPickerView(
|
||||
state = aDefaultEmojiPickerState(
|
||||
isSearchActive = true,
|
||||
searchQuery = "smile",
|
||||
searchResults = SearchBarResultState.Results(
|
||||
persistentListOf(
|
||||
Emoji(
|
||||
hexcode = "0x00",
|
||||
label = "grinning face",
|
||||
tags = persistentListOf("grinning"),
|
||||
shortcodes = persistentListOf("smile"),
|
||||
unicode = "😀",
|
||||
skins = null,
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
onSelectEmoji = {},
|
||||
selectedEmojis = persistentSetOf(),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user