Skip to content

Commit

Permalink
Merge pull request #3425 from CruGlobal/GT-2283-Filter-Language-Searc…
Browse files Browse the repository at this point in the history
…h-Bar

GT-2283 Make the Language Filter SearchBar sticky
  • Loading branch information
frett authored Mar 21, 2024
2 parents bf9b287 + b725ed1 commit 30cc3e6
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
Expand All @@ -20,6 +21,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBar
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -32,6 +34,9 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import org.ccci.gto.android.common.androidx.compose.material3.ui.menu.LazyDropdownMenu
Expand All @@ -43,7 +48,7 @@ import org.cru.godtools.ui.languages.LanguageName
import org.jetbrains.annotations.VisibleForTesting

private val DROPDOWN_MAX_HEIGHT = 700.dp
private val DROPDOWN_MAX_WIDTH = 400.dp
private val DROPDOWN_MAX_WIDTH = 350.dp

internal const val TEST_TAG_FILTER_DROPDOWN = "filter_dropdown"

Expand Down Expand Up @@ -80,7 +85,7 @@ internal fun CategoryFilter(filters: ToolsScreen.Filters, modifier: Modifier = M

ElevatedButton(
onClick = { expanded = !expanded },
modifier = modifier
modifier = modifier.semantics { role = Role.DropdownList }
) {
Text(
selectedCategory?.let { getToolCategoryName(it, LocalContext.current) }
Expand Down Expand Up @@ -134,14 +139,11 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M
val selectedLanguage by rememberUpdatedState(filters.selectedLanguage)
val eventSink by rememberUpdatedState(filters.eventSink)

var expanded by rememberSaveable { mutableStateOf(false) }
val expanded by rememberUpdatedState(filters.showLanguagesMenu)

ElevatedButton(
onClick = {
if (!expanded) eventSink(ToolsScreen.FiltersEvent.UpdateLanguageQuery(""))
expanded = !expanded
},
modifier = modifier
onClick = { eventSink(ToolsScreen.FiltersEvent.ToggleLanguagesMenu) },
modifier = modifier.semantics { role = Role.DropdownList }
) {
Text(
text = selectedLanguage?.getDisplayName(context, LocalAppLanguage.current)
Expand All @@ -154,31 +156,35 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M

LazyDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.sizeIn(maxHeight = DROPDOWN_MAX_HEIGHT, maxWidth = DROPDOWN_MAX_WIDTH)
.testTag(TEST_TAG_FILTER_DROPDOWN)
onDismissRequest = { eventSink(ToolsScreen.FiltersEvent.ToggleLanguagesMenu) },
modifier = Modifier.sizeIn(maxHeight = DROPDOWN_MAX_HEIGHT, maxWidth = DROPDOWN_MAX_WIDTH)
) {
stickyHeader {
Surface(color = MaterialTheme.colorScheme.surface) {
SearchBar(
query,
onQueryChange = { eventSink(ToolsScreen.FiltersEvent.UpdateLanguageQuery(it)) },
onSearch = { eventSink(ToolsScreen.FiltersEvent.UpdateLanguageQuery(it)) },
active = false,
onActiveChange = {},
colors = GodToolsTheme.searchBarColors,
leadingIcon = { Icon(Icons.Filled.Search, null) },
placeholder = {
Text(stringResource(R.string.language_settings_downloadable_languages_search))
},
content = {},
modifier = Modifier
.padding(horizontal = 12.dp)
.fillMaxWidth()
.wrapContentWidth()
)
}
}
item {
SearchBar(
query,
onQueryChange = { eventSink(ToolsScreen.FiltersEvent.UpdateLanguageQuery(it)) },
onSearch = { eventSink(ToolsScreen.FiltersEvent.UpdateLanguageQuery(it)) },
active = false,
onActiveChange = {},
colors = GodToolsTheme.searchBarColors,
leadingIcon = { Icon(Icons.Filled.Search, null) },
placeholder = { Text(stringResource(R.string.language_settings_downloadable_languages_search)) },
content = {},
modifier = Modifier.padding(horizontal = 12.dp)
)
FilterMenuItem(
label = stringResource(R.string.dashboard_tools_section_filter_language_any),
supportingText = stringResource(R.string.dashboard_tools_section_filter_available_tools_all),
onClick = {
eventSink(ToolsScreen.FiltersEvent.SelectLanguage(null))
expanded = false
}
onClick = { eventSink(ToolsScreen.FiltersEvent.SelectLanguage(null)) }
)
}

Expand All @@ -190,10 +196,7 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M
count,
count,
),
onClick = {
eventSink(ToolsScreen.FiltersEvent.SelectLanguage(it.code))
expanded = false
},
onClick = { eventSink(ToolsScreen.FiltersEvent.SelectLanguage(it.code)) },
modifier = Modifier.animateItemPlacement()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.slack.circuit.codegen.annotations.CircuitInject
import com.slack.circuit.runtime.Navigator
Expand Down Expand Up @@ -98,7 +99,8 @@ class ToolsPresenter @AssistedInject constructor(

// selected language
val selectedLocale by remember { settings.getDashboardFilterLocaleFlow() }.collectAsState(null)
var languageQuery by remember { mutableStateOf("") }
var showLanguagesMenu by rememberSaveable { mutableStateOf(false) }
var languageQuery by rememberSaveable { mutableStateOf("") }

val filtersEventSink: (ToolsScreen.FiltersEvent) -> Unit = remember {
{
Expand All @@ -108,15 +110,22 @@ class ToolsPresenter @AssistedInject constructor(
}
is ToolsScreen.FiltersEvent.SelectLanguage -> scope.launch {
settings.updateDashboardFilterLocale(it.locale)
showLanguagesMenu = false
languageQuery = ""
}
is ToolsScreen.FiltersEvent.UpdateLanguageQuery -> languageQuery = it.query
ToolsScreen.FiltersEvent.ToggleLanguagesMenu -> {
showLanguagesMenu = !showLanguagesMenu
languageQuery = ""
}
}
}
}

return ToolsScreen.Filters(
categories = rememberFilterCategories(selectedLocale),
selectedCategory = selectedCategory,
showLanguagesMenu = showLanguagesMenu,
languages = rememberFilterLanguages(selectedCategory, languageQuery),
languageQuery = languageQuery,
selectedLanguage = languagesRepository.rememberLanguage(selectedLocale),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ data object ToolsScreen : Screen {
data class Filters(
val categories: List<Filter<String>> = emptyList(),
val selectedCategory: String? = null,
val showLanguagesMenu: Boolean = false,
val languages: List<Filter<Language>> = emptyList(),
val languageQuery: String = "",
val selectedLanguage: Language? = null,
Expand All @@ -36,6 +37,7 @@ data object ToolsScreen : Screen {
}

sealed interface FiltersEvent : CircuitUiEvent {
data object ToggleLanguagesMenu : FiltersEvent
data class UpdateLanguageQuery(val query: String) : FiltersEvent
data class SelectCategory(val category: String?) : FiltersEvent
data class SelectLanguage(val locale: Locale?) : FiltersEvent
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.cru.godtools.ui.dashboard.tools

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.ui.Modifier
import com.android.resources.NightMode
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import java.util.Locale
import kotlin.test.Ignore
import kotlin.test.Test
import org.cru.godtools.model.Language
import org.cru.godtools.ui.BasePaparazziTest
import org.junit.runner.RunWith

@RunWith(TestParameterInjector::class)
class ToolFiltersPaparazziTest(
@TestParameter nightMode: NightMode,
@TestParameter accessibilityMode: AccessibilityMode,
) : BasePaparazziTest(nightMode = nightMode, accessibilityMode = accessibilityMode) {
@Test
fun `LanguageFilter - Button - No Language Selected`() = renderLanguageFilter(
ToolsScreen.Filters(
selectedLanguage = null,
showLanguagesMenu = false,
)
)

@Test
fun `LanguageFilter - Button - English Selected`() = renderLanguageFilter(
ToolsScreen.Filters(
selectedLanguage = Language(Locale.ENGLISH),
showLanguagesMenu = false,
)
)

// TODO: It appears that LayoutLib does not correctly support Popups/Windows currently
// see: https://issuetracker.google.com/issues/317792376
// see: https://issuetracker.google.com/issues/308808808
// see: https://issuetracker.google.com/issues/321623569
@Test
@Ignore("Ignored for now due to LayoutLib rendering issues")
fun `LanguageFilter - Dropdown Menu`() = renderLanguageFilter(
ToolsScreen.Filters(
selectedLanguage = Language(Locale.ENGLISH),
showLanguagesMenu = true,
languages = listOf(
ToolsScreen.Filters.Filter(Language(Locale.ENGLISH), 12345),
ToolsScreen.Filters.Filter(Language(Locale.FRENCH), 1),
ToolsScreen.Filters.Filter(Language(Locale("es")), 3),
),
)
)

private fun renderLanguageFilter(filters: ToolsScreen.Filters) = centerInSnapshot {
LanguageFilter(filters, modifier = Modifier.fillMaxWidth(0.5f))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cru.godtools.ui.dashboard.tools

import android.app.Application
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.hasClickAction
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
Expand All @@ -14,7 +15,6 @@ import org.cru.godtools.model.Tool
import org.cru.godtools.ui.dashboard.tools.ToolsScreen.Filters.Filter
import org.junit.Rule
import org.junit.Test
import org.junit.experimental.categories.Categories.CategoryFilter
import org.junit.runner.RunWith
import org.robolectric.annotation.Config

Expand Down Expand Up @@ -167,25 +167,27 @@ class ToolFiltersTest {
}

@Test
fun `LanguageFilter() - Dropdown Menu - Show when button is clicked`() {
fun `LanguageFilter() - Button toggles menu`() {
composeTestRule.setContent {
LanguageFilter(ToolsScreen.Filters(eventSink = events))
LanguageFilter(
ToolsScreen.Filters(
selectedLanguage = Language(Locale.ENGLISH),
eventSink = events,
)
)
}

// dropdown menu not shown
composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertDoesNotExist()

// click button to show dropdown
composeTestRule.onNode(hasClickAction()).performClick()
composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertExists()
events.assertEvent(ToolsScreen.FiltersEvent.UpdateLanguageQuery(""))
composeTestRule.onNodeWithText("English").assertHasClickAction().performClick()
events.assertEvent(ToolsScreen.FiltersEvent.ToggleLanguagesMenu)
}

@Test
fun `LanguageFilter() - Dropdown Menu - Show languages`() {
composeTestRule.setContent {
LanguageFilter(
filters = ToolsScreen.Filters(
showLanguagesMenu = true,
languages = listOf(
Filter(Language(Locale.FRENCH), 1),
Filter(Language(Locale.GERMAN), 1),
Expand All @@ -194,12 +196,10 @@ class ToolFiltersTest {
),
)
}
composeTestRule.onNode(hasClickAction()).performClick()

composeTestRule.onNodeWithText("English", substring = true, ignoreCase = true).assertDoesNotExist()
composeTestRule.onNodeWithText("French", substring = true, ignoreCase = true).assertExists()
composeTestRule.onNodeWithText("German", substring = true, ignoreCase = true).assertExists()
events.assertEvent(ToolsScreen.FiltersEvent.UpdateLanguageQuery(""))
}

@Test
Expand All @@ -208,6 +208,7 @@ class ToolFiltersTest {
LanguageFilter(
filters = ToolsScreen.Filters(
selectedLanguage = Language(Locale.FRENCH),
showLanguagesMenu = true,
languages = listOf(
Filter(Language(Locale.FRENCH), 1),
Filter(Language(Locale.GERMAN), 1),
Expand All @@ -216,21 +217,17 @@ class ToolFiltersTest {
),
)
}
composeTestRule.onNode(hasClickAction()).performClick()

composeTestRule.onNodeWithText("Any language", substring = true, ignoreCase = true).performClick()
composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertDoesNotExist()
events.assertEvents(
ToolsScreen.FiltersEvent.UpdateLanguageQuery(""),
ToolsScreen.FiltersEvent.SelectLanguage(null)
)
events.assertEvent(ToolsScreen.FiltersEvent.SelectLanguage(null))
}

@Test
fun `LanguageFilter() - Dropdown Menu - Select a language`() {
composeTestRule.setContent {
LanguageFilter(
filters = ToolsScreen.Filters(
showLanguagesMenu = true,
languages = listOf(
Filter(Language(Locale.FRENCH), 1),
Filter(Language(Locale.GERMAN), 1),
Expand All @@ -239,14 +236,9 @@ class ToolFiltersTest {
),
)
}
composeTestRule.onNode(hasClickAction()).performClick()

composeTestRule.onNodeWithText("French", substring = true, ignoreCase = true).performClick()
composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertDoesNotExist()
events.assertEvents(
ToolsScreen.FiltersEvent.UpdateLanguageQuery(""),
ToolsScreen.FiltersEvent.SelectLanguage(Locale.FRENCH)
)
events.assertEvents(ToolsScreen.FiltersEvent.SelectLanguage(Locale.FRENCH))
}
// endregion LanguageFilter
}
Loading

0 comments on commit 30cc3e6

Please sign in to comment.