Skip to content
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

2808: Add a filter by date option to events #2898

Open
wants to merge 38 commits into
base: main
Choose a base branch
from

Conversation

bahaaTuffaha
Copy link
Contributor

Short description

Filtering events by date

Proposed changes

For web:

  • Created a component called CustomDatePicker and used Input type='date' as date picker.

For native:

  • Created a component called CustomDatePicker used react-native TextInput and IconButton to show calendar modal.
  • Another component I added is for Calender Range picker called CalendarRangeModal I used react-native-calendars library and tried to mimic the styling like the design in figma.

Shared:

  • Created a hook called useDateFilter that accepts events then will validate and returns filteredEvents.

Side effects

None.

Resolved issues

Fixes: #2808


Copy link
Member

@steffenkleinle steffenkleinle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thank you for your PR, looks pretty nice already! Nice work 🎉

I just reviewed and tested native so far and everything works and looks as expected. I will review web once the comments have been addressed (I think the PR is quite huge, perhaps it would be better to merge it one by one and split up the PR, if possible).

I have a few remarks regarding coding style and complexity. If you feel like I misunderstood something or your solution makes more sense, please tell.

General changes that would be a good addition:

  • Use DateTime to avoid manual parsing and formatting as well as validating
  • Clicking next to the date picker could/should close it for better UX
  • If you delete a slash in the date picker, you can't readd it (since the keyboard only shows numbers). A better choice would perhaps be to implement fields for day, month, and year separately, so you can only edit either one at a time. Not sure if this is an optimal solution though, so if you have any other ideas let me know.

Keep in mind that I did not mention every occurrence of things that could be improved, please apply the suggestions to other parts in the code, too.

web/tsconfig.json Outdated Show resolved Hide resolved
assets/icons/calendar-events.svg Outdated Show resolved Hide resolved
assets/icons/expand.svg Outdated Show resolved Hide resolved
assets/icons/shrink.svg Outdated Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Outdated Show resolved Hide resolved
translations/translations.json Outdated Show resolved Hide resolved
native/src/utils/calendarRangeUtils.ts Outdated Show resolved Hide resolved
native/src/utils/calendarRangeUtils.ts Outdated Show resolved Hide resolved
shared/hooks/useDateFilter.ts Outdated Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Outdated Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Outdated Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Outdated Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Outdated Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Outdated Show resolved Hide resolved
shared/hooks/useDateFilter.ts Outdated Show resolved Hide resolved
shared/hooks/useDateFilter.ts Outdated Show resolved Hide resolved
translations/translations.json Outdated Show resolved Hide resolved
web/src/components/FilterToggle.tsx Show resolved Hide resolved
web/src/components/FilterToggle.tsx Outdated Show resolved Hide resolved
native/src/components/DatePicker.tsx Outdated Show resolved Hide resolved
native/src/components/EventsDateFilter.tsx Outdated Show resolved Hide resolved
native/src/components/EventsDateFilter.tsx Outdated Show resolved Hide resolved
web/src/components/DatePicker.tsx Outdated Show resolved Hide resolved
web/src/components/EventsDateFilter.tsx Outdated Show resolved Hide resolved
web/src/components/EventsDateFilter.tsx Outdated Show resolved Hide resolved
web/src/components/EventsDateFilter.tsx Outdated Show resolved Hide resolved
@steffenkleinle
Copy link
Member

steffenkleinle commented Sep 12, 2024

Some thoughts on the behavior:

  • My personal opinion is that if I never selected a time period, no filtering should be done and all events should be shown. Only if I manually set a start and end date, I want the events to be filtered.
  • Usually, confirming actions, i.e. ok in this case, are placed on the right side while `cancel´ should be placed on the left side of the screen (at least for LTR languages): https://m2.material.io/components/dialogs#actions. This is also the case in the designs.
  • If an invalid date range is selected with the date picker (i.e. no start date, no end date, end date before start date) it should not be possible to press ok (as nothing will happen anyway and the user will be confused about that). Instead the button should be disabled, greyed out.
  • If a date is selected that is before the current fromDate (and if set, the toDate), it should be set as new fromDate and toDate should be cleared. Currently, if only a fromDate is selected, the following happens (see screenshot)

One more general question:
Is there any reason you chose the names fromDate and toDate over e.g. startDate and endDate?

@steffenkleinle
Copy link
Member

Sadly I just realized we have another problem. A lot of our events are recurring, i.e. happening multiple times, for example every week. Our filtering should reflect that and include events that have recurrences in the selected time period. This might not be that easy, so perhaps we should do that in a separate PR. Has to be done before this is good to go though sadly :/

@bahaaTuffaha
Copy link
Contributor Author

Sadly I just realized we have another problem. A lot of our events are recurring, i.e. happening multiple times, for example every week. Our filtering should reflect that and include events that have recurrences in the selected time period. This might not be that easy, so perhaps we should do that in a separate PR. Has to be done before this is good to go though sadly :/

I think it can be done by modifying the filter maybe using rrule.between() event.date has recurrenceRule type RRuleType | null

@bahaaTuffaha bahaaTuffaha marked this pull request as ready for review September 13, 2024 11:11
Copy link
Member

@steffenkleinle steffenkleinle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes and trying to make it work with our recurring events. I still see two problems though (which were also the reasons why I said I'd rather do this in a separate PR):

  • if the filters are set, only recurrences that are within that time period should be shown
  • this only works if one of the first MAX_DATE_RECURRENCES is in the selected time period. If it is a later recurrence, this won't work. You'd therefore need to adjust the recurrences function to accept start/end date values

Furthermore, there are some warnings regarding code complexity still, please try to adjust your code to address those.

shared/hooks/useDateFilter.ts Outdated Show resolved Hide resolved
Copy link
Member

@steffenkleinle steffenkleinle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
The app still shows the "wrong" recurrences if a filter is set.
In this case, the next date shown should be the 23rd.
I think you just have to pass the current filter values there as well.

Great solution for the recurrences though, this was way simpler than I thought. Nice 🎉 🚀
Nearly there :)

shared/api/models/DateModel.ts Outdated Show resolved Hide resolved
shared/api/models/DateModel.ts Outdated Show resolved Hide resolved
shared/hooks/useDateFilter.ts Outdated Show resolved Hide resolved
web/src/routes/EventsPage.tsx Outdated Show resolved Hide resolved
shared/utils/dateFilterUtils.ts Outdated Show resolved Hide resolved
Copy link
Member

@steffenkleinle steffenkleinle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing, great work! Thank you so much for all the work 🎉

Copy link

codeclimate bot commented Oct 16, 2024

Code Climate has analyzed commit ab81a63 and detected 4 issues on this pull request.

Here's the issue category breakdown:

Category Count
Complexity 4

The test coverage on the diff in this pull request is 58.3% (50% is the threshold).

This pull request will bring the total coverage in the repository to 73.8%.

View more on Code Climate.

Copy link
Member

@ztefanie ztefanie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really happy with this solution, because:

  1. When a user only wants to specify a end date, and clicks on the calendar of the "to" input field, a "from" date is selected nevertheless. This is not how this should work intuitively.

  2. When selecting a "from" date from the date picker and entering the "to" date with a text input, it will not work.

  3. When selecting a "from" date for the date picker, i cannot click "ok" in the calendar and if i close the calendar the date is not in the input field.

  4. When using the input field, the and entering the date by keyboard, when i entered two digits for "dd" it should automatically go to the "mm" input, as on a smartphone it is quite annoying to click on the next section, which is very small.

As the library you use, does not really fit our needs of selecting a range, is there any reason to not use e.g. this library? https://www.npmjs.com/package/react-native-ui-datepicker

native/src/routes/Events.tsx Outdated Show resolved Hide resolved
const DatePicker = ({ title, date, setDate, error, modalOpen, setModalOpen }: DatePickerProps): ReactElement => {
const [inputDay, setInputDay] = useState(date?.toFormat('dd'))
const [inputMonth, setInputMonth] = useState(date?.toFormat('MM'))
const [inputYear, setInputYear] = useState(date?.toFormat('yyyy'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is not very good, to have separate state for day, month and year. This leads to

  1. unnecessary re-renders
  2. allows invalid dates to be entered, like 31.02.2024

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is not very good, to have separate state for day, month and year. This leads to

1. unnecessary re-renders

Tbh I don't think there is a difference between separate states or single state for the date here. If one value is changed, there is a rerender. It does not matter if we change a single state multiple times or a multi state multiple times.

2. allows invalid dates to be entered, like 31.02.2024

It is never completely preventable that no invalid date is entered, for example by copy and paste. If it is correctly handled/validated, I don't see a reason why that has to be a problem.

native/src/components/CalendarRangeModal.tsx Show resolved Hide resolved
@steffenkleinle
Copy link
Member

I am not really happy with this solution, because:

1. When a user only wants to specify a end date, and clicks on the calendar of the "to" input field, a "from" date is selected nevertheless. This is not how this should work intuitively.

2. When selecting a "from" date from the date picker and entering the "to" date with a text input, it will not work.

3. When selecting a "from" date for the date picker, i cannot click "ok" in the calendar and if i close the calendar the date is not in the input field.

4. When using the input field, the and entering the date by keyboard, when i entered two digits for "dd" it should automatically go to the "mm" input, as on a smartphone it is quite annoying to click on the next section, which is very small.

As the library you use, does not really fit our needs of selecting a range, is there any reason to not use e.g. this library? https://www.npmjs.com/package/react-native-ui-datepicker

Good find, I think these things should be addressed. I think this is only the case for native, right? I think you should be able to just adjust the day marking handling and some other logic though, I am pretty sure there is no need to switch the library because of this.

@ztefanie
Copy link
Member

I am not really happy with this solution, because:

1. When a user only wants to specify a end date, and clicks on the calendar of the "to" input field, a "from" date is selected nevertheless. This is not how this should work intuitively.

2. When selecting a "from" date from the date picker and entering the "to" date with a text input, it will not work.

3. When selecting a "from" date for the date picker, i cannot click "ok" in the calendar and if i close the calendar the date is not in the input field.

4. When using the input field, the and entering the date by keyboard, when i entered two digits for "dd" it should automatically go to the "mm" input, as on a smartphone it is quite annoying to click on the next section, which is very small.

As the library you use, does not really fit our needs of selecting a range, is there any reason to not use e.g. this library? https://www.npmjs.com/package/react-native-ui-datepicker

Good find, I think these things should be addressed. I think this is only the case for native, right? I think you should be able to just adjust the day marking handling and some other logic though, I am pretty sure there is no need to switch the library because of this.

Why use a library that does not fit our needs, if there are libraries that we can just use out of the box?

@steffenkleinle
Copy link
Member

I am not really happy with this solution, because:

1. When a user only wants to specify a end date, and clicks on the calendar of the "to" input field, a "from" date is selected nevertheless. This is not how this should work intuitively.

2. When selecting a "from" date from the date picker and entering the "to" date with a text input, it will not work.

3. When selecting a "from" date for the date picker, i cannot click "ok" in the calendar and if i close the calendar the date is not in the input field.

4. When using the input field, the and entering the date by keyboard, when i entered two digits for "dd" it should automatically go to the "mm" input, as on a smartphone it is quite annoying to click on the next section, which is very small.

As the library you use, does not really fit our needs of selecting a range, is there any reason to not use e.g. this library? https://www.npmjs.com/package/react-native-ui-datepicker

Good find, I think these things should be addressed. I think this is only the case for native, right? I think you should be able to just adjust the day marking handling and some other logic though, I am pretty sure there is no need to switch the library because of this.

Why use a library that does not fit our needs, if there are libraries that we can just use out of the box?

Why do you think that this library does not fit our need? I think it does pretty much exactly what we need it to do, i.e. being able to select and mark dates. We just have to slightly adjust how when we set start and end dates 🤔

@bahaaTuffaha
Copy link
Contributor Author

bahaaTuffaha commented Oct 21, 2024

I am not really happy with this solution, because:

  1. When a user only wants to specify a end date, and clicks on the calendar of the "to" input field, a "from" date is selected nevertheless. This is not how this should work intuitively.
  2. When selecting a "from" date from the date picker and entering the "to" date with a text input, it will not work.
  3. When selecting a "from" date for the date picker, i cannot click "ok" in the calendar and if i close the calendar the date is not in the input field.
  4. When using the input field, the and entering the date by keyboard, when i entered two digits for "dd" it should automatically go to the "mm" input, as on a smartphone it is quite annoying to click on the next section, which is very small.

As the library you use, does not really fit our needs of selecting a range, is there any reason to not use e.g. this library? https://www.npmjs.com/package/react-native-ui-datepicker

I actually tried to use this library but it has issues with RTL languages the will get switched up like this and we tried to solve it and non worked ...
look at this : 4df2415
image

@ztefanie
Copy link
Member

I will dismiss my review as "changes requested" here, as I am on vacation and do not want to block this.

@bahaaTuffaha Thanks for explaining why the other library was not an option and good work in general with this PR, the UI looks pretty nice and looking forward to have this feature in Integreat 🚀

@ztefanie ztefanie dismissed their stale review October 24, 2024 11:24

Do not want to block this during my vacation

Copy link
Contributor

@LeandraH LeandraH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, thank you for all your work on this already!

I only managed to do a somewhat superficial review, and only of the native parts, but I have found a few more things that could do some work.

  • There seems to be a fade-in animation when showing the filters but not when hiding the filters.

  • image

I put in the dates 1.10.2024 - 24.10.2024 and got dates from 25.10.2024 - 6.11.2024

Edit: I have since realized that those are recurring events. That's cool but we need to show them with the dates that are during the selected period.

  • I find the interactions in the CalendarRangeModal confusing. My expectations from the way other date pickers work would be that the calendar closes when I chose an end date. There are a few other behaviors that I don't expect but I think they would all be covered by closing the calendar when the second date is selected.

  • I get the following warning in my console: WARN (ADVICE) "fit-content" is not a valid dimension. Dimensions must be a number, "auto", or a string suffixed with "%".

  • The inputs for with 'dd', 'mm' and 'yyyy' do absolutely nothing when I tap on them in emulated iOS, don't even give me an error message.

  • If I enter e.g. 01.11.2024 into the start date, then tap somewhere else, then try to edit the day of the start date again, nothing I type is accepted until I first delete the number that is in there already.

I would suggest maybe taking an hour or two and looking at this with fresh eyes and trying to do your own code review. I find it often helps me to improve my own code when I have to understand it after not having looked at it in a while.

Comment on lines 22 to 23
justify-content: ${props => (props.theme.contentDirection === 'rtl' ? 'flex-start' : 'flex-end')};
flex-direction: ${props => (props.theme.contentDirection === 'rtl' ? 'row-reverse' : 'row')};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ This does seem odd, I would expect the combination of these two lines to do nothing since the positioning is turned around twice?

native/src/components/CalendarRangeModal.tsx Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Show resolved Hide resolved
native/src/components/CalendarRangeModal.tsx Show resolved Hide resolved
native/src/components/DatePicker.tsx Outdated Show resolved Hide resolved
native/src/components/DatePicker.tsx Outdated Show resolved Hide resolved
value={inputDay}
/>
<Text>.</Text>
<Input
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 I think the inputs could be pulled into a separate component to improve legibility.

Copy link
Contributor

@LeandraH LeandraH Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think that the inputs could be pulled into a separate component to improve legibility. :)

native/src/components/DatePicker.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 Thank you for always writing tests immediately! I think this is a good place to expand on some tests :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added 3 more but I couldn't get the .focus() or fireEvent.focus() working to test backspace jumping or even jumping forward from day to the month when its entered.

@LeandraH
Copy link
Contributor

I just had a short call with Svenja to show her the current state and she suggested also zero-padding the dates here:
image

@LeandraH
Copy link
Contributor

When this is merged, we should make a note in this thread that it will be deployed to production the Monday after the next

Copy link
Contributor

@LeandraH LeandraH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, we're getting there! I have a few more general comments:

  1. As you already noticed, you haven't dealt with the animation yet, and I think it would also be nice to have one in the web.
  2. While I agree that we should show recurring events in the filtered list, we definitely need to show them with the date that is within that time frame, or show in some other way why these events are shown even though the filter should be hiding them.
  3. In native, I still find the interactions in the CalendarRangeModal confusing. My expectations from the way other date pickers work would be that the calendar closes when I chose an end date. There are a few other behaviors that I don't expect but I think they would all be covered by closing the calendar when the second date is selected.
  4. I like your solution for dealing with someone opening an input field a second time, very nice! But I think we still need a better solution for people putting in invalid formats. I think we should let them enter what they want and validate on blur. I would expect a number that is too high to be turned into the maximum possible number, and otherwise invalid dates to either be blanked or get a red outline and an explaining text. I guess an error would be ideal but it’s also more work to add an error behavior for something that, realistically speaking, won’t be happening all that much.

padding: 5px 14px;
`

const OPACITY_DISABLED = 0.5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙃 OPACITY_DISABLED sounds to me like it should be a boolean of whether the opacity is disabled. Since this is supposed to be the opacity of a disabled button, I would say it should be called DISABLED_OPACITY.


const textButtonStyles = {
container: {
height: 40,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ The height can go directly into the StyledTextButton

Comment on lines +122 to +132
if (tempStartDate && currentInput === 'from' && tempEndDate == null) {
setStartDate(tempStartDate)
} else if (tempStartDate && currentInput === 'to' && tempEndDate == null) {
setEndDate(tempStartDate)
} else if (tempStartDate && tempEndDate) {
setStartDate(tempStartDate)
setEndDate(tempEndDate)
}
setTempStartDate(null)
setTempEndDate(null)
closeModal()
Copy link
Contributor

@LeandraH LeandraH Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 I think this entire block could do with being pulled into a well-named function. Maybe also refactor it a little bit while you're at it, first thing I see: the lines 122 and 124 are almost the same, you could have

  • one if statement with the shared parts and
  • in the then part a ternary operator to decide which date should be set.

type='clear'
disabled={
(tempStartDate === null && tempEndDate === null) ||
Boolean(tempStartDate && tempEndDate && tempStartDate > tempEndDate)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙃 We usually use !! to type cast something into a boolean :)

Comment on lines +111 to +112
setTempStartDate(startDate)
setTempEndDate(endDate)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Why are you setting these temporary values as you're closing the modal? Wouldn't they be filled in correctly anyway with the next opening?


const DateSection = styled.div`
display: flex;
flex-direction: row;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 That's the default direction, we don't need it


const StyledButton = styled(Button)`
display: flex;
flex-direction: row;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 Same here


const ResetFilterText = ({ startDate, endDate }: ResetFilterTextProps) => {
const { t } = useTranslation('events')
const title = `${t('resetFilter')} ${startDate?.toLocal().toFormat('dd.MM.yyyy') ?? '∞'} - ${endDate?.toLocal().toFormat('dd.MM.yyyy') ?? '∞'}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const title = `${t('resetFilter')} ${startDate?.toLocal().toFormat('dd.MM.yyyy') ?? '∞'} - ${endDate?.toLocal().toFormat('dd.MM.yyyy') ?? '∞'}`
const title = `${t('resetFilter')} ${startDate?.toLocaleString({ day: '2-digit', month: '2-digit', year: 'numeric' } ?? '∞'} - ${endDate?.toLocaleString({ day: '2-digit', month: '2-digit', year: 'numeric' } ?? '∞'}`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, that's not a title, that's a text :)


const StyledButton = styled(Button)`
display: flex;
flex-direction: row;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also unnecessary :)

toggle,
setToggleDateFilter,
}: {
toggle: boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙃 As discussed in the native part, I find this naming confusing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a filter by date option to events
4 participants