Skip to content

Commit aa1b31e

Browse files
authored
DON-1319 Add scroll callback to BpkCalendar (#2254)
Update readme based on Copilot's advice and with example.
1 parent 43181e8 commit aa1b31e

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

backpack-compose/src/androidTest/kotlin/net/skyscanner/backpack/compose/calendar/BpkCalendarTest.kt

+24
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ import androidx.compose.ui.test.SemanticsMatcher
2525
import androidx.compose.ui.test.SemanticsNodeInteraction
2626
import androidx.compose.ui.test.assert
2727
import androidx.compose.ui.test.assertIsNotSelected
28+
import androidx.compose.ui.test.hasContentDescription
2829
import androidx.compose.ui.test.junit4.createComposeRule
2930
import androidx.compose.ui.test.onAllNodesWithText
3031
import androidx.compose.ui.test.onFirst
3132
import androidx.compose.ui.test.onLast
3233
import androidx.compose.ui.test.onNodeWithTag
3334
import androidx.compose.ui.test.performClick
3435
import androidx.compose.ui.test.performScrollToIndex
36+
import androidx.compose.ui.test.performScrollToNode
3537
import kotlinx.coroutines.test.runTest
3638
import net.skyscanner.backpack.calendar2.CalendarParams
3739
import net.skyscanner.backpack.calendar2.CalendarSelection
@@ -41,6 +43,7 @@ import org.junit.Assert.assertEquals
4143
import org.junit.Rule
4244
import org.junit.Test
4345
import java.time.LocalDate
46+
import java.time.YearMonth
4447
import java.util.Locale
4548

4649
class BpkCalendarTest {
@@ -215,6 +218,27 @@ class BpkCalendarTest {
215218
assertEquals(expected, state.selection)
216219
}
217220

221+
@Test
222+
fun withScrollHandling() = runTest {
223+
val controller = createController(DefaultSingle)
224+
var visitedMonth = YearMonth.of(2019, 1)
225+
226+
composeTestRule.setContent {
227+
BpkTheme {
228+
BpkCalendar(controller, onScrollToMonth = {
229+
visitedMonth = it
230+
})
231+
}
232+
}
233+
234+
composeTestRule.onNodeWithTag(CALENDAR_GRID_TEST_TAG)
235+
.performScrollToNode(hasContentDescription("October 2019", substring = true))
236+
237+
composeTestRule.waitForIdle()
238+
239+
assert(visitedMonth.monthValue > 1)
240+
}
241+
218242
private fun createController(params: CalendarParams): BpkCalendarController =
219243
BpkCalendarController(initialParams = params, onSelectionChanged = {})
220244
}

backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/calendar/BpkCalendar.kt

+9
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,22 @@ import androidx.compose.foundation.layout.fillMaxSize
2424
import androidx.compose.foundation.layout.fillMaxWidth
2525
import androidx.compose.foundation.layout.padding
2626
import androidx.compose.runtime.Composable
27+
import androidx.compose.runtime.LaunchedEffect
2728
import androidx.compose.ui.Alignment
2829
import androidx.compose.ui.Modifier
2930
import net.skyscanner.backpack.calendar2.data.CalendarInteraction
3031
import net.skyscanner.backpack.compose.calendar.internal.BpkCalendarBadge
3132
import net.skyscanner.backpack.compose.calendar.internal.BpkCalendarGrid
3233
import net.skyscanner.backpack.compose.calendar.internal.BpkCalendarHeader
3334
import net.skyscanner.backpack.compose.tokens.BpkSpacing
35+
import java.time.YearMonth
3436

3537
@Composable
3638
fun BpkCalendar(
3739
controller: BpkCalendarController,
3840
modifier: Modifier = Modifier,
3941
customDateHandling: ((CalendarInteraction) -> Unit)? = null,
42+
onScrollToMonth: ((YearMonth) -> Unit)? = null,
4043
) {
4144

4245
val state = controller.state
@@ -67,5 +70,11 @@ fun BpkCalendar(
6770
)
6871
}
6972
}
73+
74+
onScrollToMonth?.let {
75+
LaunchedEffect(onScrollToMonth, controller.firstVisibleItemMonth) {
76+
onScrollToMonth(controller.firstVisibleItemMonth)
77+
}
78+
}
7079
}
7180
}

backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/calendar/BpkCalendarController.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import net.skyscanner.backpack.calendar2.data.dispatchClick
3636
import net.skyscanner.backpack.calendar2.data.dispatchSetSelection
3737
import java.io.Serializable
3838
import java.time.LocalDate
39+
import java.time.YearMonth
3940

4041
class BpkCalendarController(
4142
initialParams: CalendarParams,
@@ -89,11 +90,14 @@ class BpkCalendarController(
8990
}
9091
}
9192

93+
internal val firstVisibleItemMonth: YearMonth
94+
get() = _state.cells[lazyGridState.firstVisibleItemIndex].yearMonth
95+
9296
/**
9397
* Returns the first visible item year.
9498
*/
9599
internal val firstVisibleItemYear: Int
96-
get() = _state.cells[lazyGridState.firstVisibleItemIndex].yearMonth.year
100+
get() = firstVisibleItemMonth.year
97101

98102
/**
99103
* Handles the click events of a calendar.

docs/compose/Calendar2/README.md

+21
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,27 @@ This property is a lambda that will be called when a date is selected by the use
8989
You can use it to handle the selection manually.
9090
You can then set the selection in the controller using the `setSelection` method.
9191

92+
### (Optional) Handling Calendar Scroll Updates
93+
94+
To receive notifications when the calendar is scrolled, you can utilize the `onScrollToMonth` parameter.
95+
This callback is triggered whenever the `lazyGridState.firstVisibleItemIndex` property of the `BpkCalendarController` is
96+
updated.
97+
98+
For example you can show the furthest scrolled month's number (i.e. January = 1, February = 2, etc.) in a `BpkText` label:
99+
100+
```Kotlin
101+
Column {
102+
var month: YearMonth by remember { mutableStateOf(YearMonth.now()) }
103+
BpkText("Month: ${month.monthValue}")
104+
BpkCalendar(
105+
controller = controller,
106+
onScrollToMonth = {
107+
month = it
108+
}
109+
)
110+
}
111+
```
112+
92113
### Advanced Dates Customisation
93114

94115
You can attach some information to each date displayed in the calendar.

0 commit comments

Comments
 (0)