-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[LMN-20] SwiftUI Calendar component #1818
Conversation
leading and trailing day cells
unnecessary elements in Calendar components
SingleCalendarContainer_Previews
selection types in tests.
self.selectionType = selectionType | ||
|
||
monthHeaderDateFormatter = DateFormatter() | ||
monthHeaderDateFormatter.dateFormat = "MMMM yyyy" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this date format work for RTL and LTR languages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the same format we used before! So yeah it's the way we expect it to be!
/// is selected as both the first and second date in the range. | ||
/// - returnDatePrompt: The prompt provided to assistive technologies informing a user that they should now | ||
/// select a second date. | ||
public struct RangeAccessibilityConfigurations { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this struct live within the calendar component or be a general Backpack utility? Just asking about which folder this should live in for discoverability purposes. If it's in here, there's less chance of it being re-used elsewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's something that's specific for the Calendar, so living within the Calendar folder is the right place I think. It should not be reused, it's super specific to this!
let calendar = Calendar.current | ||
let date = calendar.date(from: .init(year: 2023, month: 11, day: 8))! | ||
|
||
assertSnapshot( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 very useful to have all the states tested, and visualised through the snapshot 😄
|
||
import SwiftUI | ||
|
||
struct CalendarBadge: View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth having snapshot tests for this as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I missed these...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This already appears in the calendar snapshot, I don't think its worth it to have it's own, given also how simple it is!
|
||
/// CalendarMonthHeader is a view that displays the month name and an optional accessory action. | ||
/// Also exposes a binding to the currently shown month. | ||
struct CalendarMonthHeader: View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this have a snapshot test as well, it looks like it contains some layout logic for different states
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also covered by the other snapshots! I dont think it needs its own!
@ViewBuilder let monthContent: (_ month: Date) -> MonthContent | ||
|
||
var body: some View { | ||
ScrollView { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I can tell, you're not doing any cell recycling of the months, I guess that's because there's a limit of 12 months, so it's not necessary, is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the months themselves are not recycled, but the day cells are. if both the day cells and months are dynamically resolved while scrolling, that produces jumps in the UI while scrolling, because a new month will load but then more cells need to render (the days), pushing content up more.
Later on, when we can support using the Grid component with iOS 16 (only LazyVGrid is available for ios 14 :/ ) we'll be able to switch to recycling months and not the days, which would be preferred!
So we're kinda cornered here by the ios versions we support!
private let daysInAWeek = 7 | ||
|
||
var body: some View { | ||
// Sunday is the first day of the week, so we need to offset the days to make Monday the first day |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you don't need the 1st line of this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh good catch!
LowerAndUpperBoundSelectedCell(calendar: calendar, date: date) | ||
} else if date == selection.lowerBound { | ||
LowerBoundSelectedCell(calendar: calendar, date: date) | ||
} else if date == selection.upperBound { | ||
UpperBoundSelectedCell(calendar: calendar, date: date) | ||
} else { | ||
InbetweenSelectionCell(calendar: calendar, date: date) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to consider the pros / cons of making individual cells like this versus have a view modifier to apply these various states to a common calendar cell.
I guess the way you have it here with distinct View structs gives some simplicity, and clarity when reading this, but it implies more duplication, eg we have to create a BPKText()
view in each cell, and apply mostly the same styling to it each time.
Just wondering if you considered making view modifiers to represent each of these cell types, which could be applied to a common view, or if you think that would end up being too painful / not working?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered it yes. I ended up with different views for each day cell type to make them not depend on each other too much and have more flexibility when adding more cell types (for example, the component needs to support (not in this PR) adding prices to the day cells)
So flexibility here is more worth having than some duplication.
Duplication is not really happening much though here (except for maybe the fact it's using a BPKText with with lineLimit=1) and a modifier would be slightly harder to use for this usecase, in general, conditionally adding modifiers should not happen, as re-composing should be the approach taken... aaaaanyways haha the different structs approach is prefered for this case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that the day headings use a different format compared to the existing calendar. Should we align Mon / Tue / Wed between both UIKit and SwiftUI?
There's also a bit of funkyness when a selection happens, which leads to a double read out of a cell, it feels like it redraws a large portion of the view when the state changes.
About showing the months Mon/Tue/Wed or M/T/W, I followed Figma for this, which shows days using only one letter! on the selection funkyness with VoiceOver, I'm not really sure what's going on? I've debugged every component taking part here and nothing I could find gets re-rendered. |
for selection state
… into swiftui-calendar
RangeCalendarContainer
Initial implementation for the Calendar component purely on SwiftUI
Following: Figma's Calendar
currently supporting:
Remaining features to match UIKit's implementation will be added on a needed-basis, like: