Skip to content

Commit

Permalink
Enable custom content description for the bottom sheet title (#2033)
Browse files Browse the repository at this point in the history
* Add support for custom content description to be set for BpkModalBottomSheetHeader title

* Add story for testing bottom sheet with custom content description for title

* Add BpkModalBottomSheetTest

* Add licence header to BpkModalBottomSheetTest

* Update titleContentDescription to default to null

* Update BpkModalBottomSheetTest to check for SemanticsProperties.Heading also

* Update BottomSheet/README.md

* Update BottomSheet/README.md

* Address PR feedback
  • Loading branch information
vivek-as-sky authored Jul 3, 2024
1 parent ac495c2 commit 8793d9a
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,26 @@ fun ModalBottomSheetWithTopBarStory(
)
}

@Composable
@BottomSheetComponent
@ComposeStory(name = "Modal with TopBar and custom title content description")
fun ModalBottomSheetWithTopBarStoryTitleContentDesc(
modifier: Modifier = Modifier,
) {
ModalBottomSheetStory(
modifier = modifier,
action = TextAction(text = stringResource(id = R.string.section_header_button_text)) {},
title = stringResource(id = R.string.modal_bottom_sheet_title),
titleContentDescription = stringResource(id = R.string.modal_bottom_sheet_title_content_desc),
closeButton = BpkModalBottomSheetCloseAction.Close(stringResource(id = R.string.navigation_close)),
)
}

@Composable
internal fun ModalBottomSheetStory(
modifier: Modifier = Modifier,
title: String? = null,
titleContentDescription: String? = null,
action: TextAction? = null,
closeButton: BpkModalBottomSheetCloseAction = BpkModalBottomSheetCloseAction.None,
) {
Expand All @@ -152,6 +168,7 @@ internal fun ModalBottomSheetStory(
if (openBottomSheet) {
BpkModalBottomSheet(
title = title,
titleContentDescription = titleContentDescription,
closeButton = closeButton,
action = action,
state = state,
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<string name="rating_sample_const_title">Constant Title</string>
<string name="rating_sample_const_subtitle">Constant Subtitle</string>
<string name="modal_bottom_sheet_title">Bottom Sheet Title</string>
<string name="modal_bottom_sheet_title_content_desc">Custom title content description</string>

<string-array name="rating_sample_titles">
<item>Low Title</item>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Backpack for Android - Skyscanner's Design System
*
* Copyright 2018 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.skyscanner.backpack.compose.bottomsheet

import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import net.skyscanner.backpack.compose.theme.BpkTheme
import org.junit.Rule
import org.junit.Test

class BpkModalBottomSheetTest {

@get:Rule
val composeTestRule = createComposeRule()

private val title = "Title"
private val customTitleContentDesc = "Custom content desc"

@Test
fun givenTitleContentDescriptionNotNull_whenBpkModalBottomSheet_thenSemanticsSet() {
composeTestRule.setContent {
BpkTheme {
BpkModalBottomSheet(
onDismissRequest = { },
title = title,
titleContentDescription = customTitleContentDesc,
content = {},
)
}
}

composeTestRule
.onNode(SemanticsMatcher.expectValue(key = SemanticsProperties.Heading, expectedValue = Unit))
.assertTextEquals(title)

composeTestRule
.onNodeWithContentDescription(customTitleContentDesc)
.assertExists()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fun BpkModalBottomSheet(
state: BpkModalBottomSheetState = rememberBpkModalBottomSheetState(),
dragHandleStyle: BpkDragHandleStyle = BpkDragHandleStyle.Default,
title: String? = null,
titleContentDescription: String? = null,
action: TextAction? = null,
closeButton: BpkModalBottomSheetCloseAction = BpkModalBottomSheetCloseAction.None,
content: @Composable ColumnScope.() -> Unit,
Expand All @@ -51,6 +52,7 @@ fun BpkModalBottomSheet(
action = action,
dragHandleStyle = dragHandleStyle,
title = title,
titleContentDescription = titleContentDescription,
closeButton = closeButton,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
Expand All @@ -35,6 +36,7 @@ import net.skyscanner.backpack.compose.tokens.BpkBorderRadius
import net.skyscanner.backpack.compose.tokens.BpkElevation
import net.skyscanner.backpack.compose.tokens.BpkSpacing
import net.skyscanner.backpack.compose.tokens.NativeAndroidClose
import net.skyscanner.backpack.compose.utils.applyIf

/**
* Backpack for Android - Skyscanner's Design System
Expand Down Expand Up @@ -64,6 +66,7 @@ internal fun BpkModalBottomSheetImpl(
action: TextAction?,
closeButton: BpkModalBottomSheetCloseAction,
modifier: Modifier = Modifier,
titleContentDescription: String? = null,
content: @Composable ColumnScope.() -> Unit,
) {
ModalBottomSheet(
Expand All @@ -72,6 +75,7 @@ internal fun BpkModalBottomSheetImpl(
ModalBottomSheetContent(
dragHandleStyle = dragHandleStyle,
title = title,
titleContentDescription = titleContentDescription,
closeButton = closeButton,
state = state,
action = action,
Expand Down Expand Up @@ -99,6 +103,7 @@ private fun ModalBottomSheetContent(
state: BpkModalBottomSheetState,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
titleContentDescription: String? = null,
content: @Composable() (ColumnScope.() -> Unit),
) {
if (closeButton is BpkModalBottomSheetCloseAction.Close || !title.isNullOrEmpty() || action != null) {
Expand All @@ -110,6 +115,7 @@ private fun ModalBottomSheetContent(
BpkModalBottomSheetHeader(
modifier = Modifier.height(BpkSpacing.Lg),
title = title,
titleContentDescription = titleContentDescription,
action = action,
state = state,
dragHandleStyle = dragHandleStyle,
Expand Down Expand Up @@ -150,6 +156,7 @@ private fun BpkModalBottomSheetHeader(
onDismissRequest: () -> Unit,
dragHandleStyle: BpkDragHandleStyle,
modifier: Modifier = Modifier,
titleContentDescription: String? = null,
) {
val coroutineScope = rememberCoroutineScope()
val backgroundColor = when (dragHandleStyle) {
Expand All @@ -170,7 +177,15 @@ private fun BpkModalBottomSheetHeader(
text = it,
style = BpkTheme.typography.heading5,
textAlign = TextAlign.Center,
modifier = Modifier.semantics { heading() },
modifier = Modifier
.semantics {
heading()
}
.applyIf(titleContentDescription != null) {
semantics {
contentDescription = titleContentDescription!!
}
},
)
}
},
Expand Down
22 changes: 22 additions & 0 deletions docs/compose/BottomSheet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,28 @@ if (openBottomSheet) {
)
}
```
By default, the Bottom sheet's `title` text serves as its content description for accessibility purposes. However, if you wish to customize the content description of the title, you can do so by passing a `titleContentDescription` argument, as shown below:
```Kotlin
import net.skyscanner.backpack.compose.bottomsheet.BpkModalBottomSheet
import net.skyscanner.backpack.compose.bottomsheet.BpkModalBottomSheetState
import net.skyscanner.backpack.compose.bottomsheet.rememberBpkModalBottomSheetState

var openBottomSheet by rememberSaveable { mutableStateOf(true) }
val state = rememberBpkModalBottomSheetState()

if (openBottomSheet) {
BpkModalBottomSheet(
state = state,
title = stringResource(id = R.string.generic_title),
titleContentDescription = stringResource(id = R.string.generic_title_custom_content_description),
closeButton = BpkModalBottomSheetCloseAction.Default(stringResource(id = R.string.navigation_close)),
action = TextAction(text = stringResource(id = R.string.section_header_button_text), {}),
content = { /* content of the bottom sheet */ },
dragHandleStyle = BpkDragHandleStyle.Default,
onDismissRequest = { openBottomSheet = false },
)
}
```
By default the Bottom sheet content starts below the drag handle. In cases where you need to show an image at the top you can set the `dragHandleStyle` property to `OnImage` to remove the safe area, like this:

```Kotlin
Expand Down

0 comments on commit 8793d9a

Please sign in to comment.