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 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5028027
2808: Creating a component called CustomDatePicker and filtering even…
bahaaTuffaha Aug 20, 2024
b6683da
2808: Creating a hook for useDateFilter and a toggle to hide and show…
bahaaTuffaha Aug 21, 2024
8dd13b4
2808: web changes and adding translation and direction for RTL plus c…
bahaaTuffaha Aug 21, 2024
3fb9deb
2828: added a library called react-native-ui-datepicker and create Ca…
bahaaTuffaha Aug 22, 2024
566da43
2808: Added DateFilterToggle to native and improvements to useDateFil…
bahaaTuffaha Aug 24, 2024
3c0dbe5
2808: Adding translations and using React-native modal
bahaaTuffaha Aug 25, 2024
63d158a
Merge remote-tracking branch 'origin' into 2808-Add-a-filter-by-date-…
bahaaTuffaha Aug 25, 2024
4df2415
2808: Replacing (react-native-ui-datepicker) with (react-native-calen…
bahaaTuffaha Aug 27, 2024
c67fd25
Added some tests and release note
bahaaTuffaha Aug 27, 2024
60f9de7
Merge remote-tracking branch 'origin' into 2808-Add-a-filter-by-date-…
bahaaTuffaha Aug 27, 2024
193909c
2808: Refactoring code + some improvements
bahaaTuffaha Aug 27, 2024
e701023
Merge remote-tracking branch 'origin' into 2808-Add-a-filter-by-date-…
bahaaTuffaha Aug 27, 2024
5d18825
2808: prettier:check fixed for tsconfig
bahaaTuffaha Aug 27, 2024
c1cab6d
2808: Fixing Typescript issues
bahaaTuffaha Aug 27, 2024
25406e8
2808: Requested changes about DateTime
bahaaTuffaha Aug 29, 2024
3321eb4
2808: Fix translations
steffenkleinle Sep 10, 2024
115de27
2808: Requested changes part 2
bahaaTuffaha Sep 11, 2024
e652e78
Merge remote-tracking branch 'origin' into 2808-Add-a-filter-by-date-…
bahaaTuffaha Sep 11, 2024
000a9a4
2808: Requested changes part 3
bahaaTuffaha Sep 12, 2024
d7ca0ff
2808: Replacing names fromDate and toDate to startDate and endDate
bahaaTuffaha Sep 12, 2024
a6176f5
2808: Replacing alt with placeholder at date-input (web) for tests ge…
bahaaTuffaha Sep 13, 2024
5b10e03
2808: Requested changes Part4
bahaaTuffaha Sep 19, 2024
52db677
Merge remote-tracking branch 'origin' into 2808-Add-a-filter-by-date-…
bahaaTuffaha Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions native/src/components/CalendarRangeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ const DatePickerWrapper = styled.View`
position: absolute;
width: 90%;
top: 228px;
justify-self: center;
/* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */
align-self: center;
`
const StyledView = styled.View`
gap: 8px;
flex-direction: row;
justify-content: ${props => (props.theme.contentDirection === 'rtl' ? 'flex-start' : 'flex-end')};
flex-direction: ${props => (props.theme.contentDirection === 'ltr' ? 'row' : 'row-reverse')};
justify-content: flex-start;
padding: 5px 10px;
`
const StyledTextButton = styled(TextButton)`
Expand Down Expand Up @@ -63,14 +61,11 @@ const CalendarRangeModal = ({
const handleDayPress = (day: { dateString: string }) => {
const selectedDate = DateTime.fromISO(day.dateString)

if (!tempFromDate) {
if (!tempFromDate || tempToDate) {
setTempFromDate(selectedDate)
setTempToDate(null)
} else if (!tempToDate) {
setTempToDate(selectedDate)
} else {
setTempFromDate(selectedDate)
setTempToDate(null)
setTempToDate(selectedDate)
}
}

Expand All @@ -81,7 +76,7 @@ const CalendarRangeModal = ({
<Caption title={t('selectRange')} />
<Calendar
markingType='period'
markedDates={getMarkedDates(tempFromDate, tempToDate)}
markedDates={getMarkedDates(tempFromDate, tempToDate, theme)}
onDayPress={handleDayPress}
theme={{
calendarBackground: theme.colors.textDecorationColor,
Expand All @@ -104,13 +99,13 @@ const CalendarRangeModal = ({
/>
<StyledTextButton
onPress={() => {
if (Boolean(tempFromDate) && Boolean(tempToDate)) {
setFromDate(tempFromDate || DateTime.local())
setToDate(tempToDate || DateTime.local())
if (tempFromDate && tempToDate) {
setFromDate(tempFromDate)
setToDate(tempToDate)
}
closeModal()
}}
text={t('ok')}
text={t('common:ok')}
type='clear'
/>
</StyledView>
Expand Down
57 changes: 24 additions & 33 deletions native/src/components/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DateTime } from 'luxon'
import React, { ReactElement, useEffect, useState } from 'react'
import styled from 'styled-components/native'

import { DATE_FORMAT } from 'shared/constants'
import { DATE_FORMAT } from 'shared'

import { CalendarTodayIcon } from '../assets'
import Icon from './base/Icon'
Expand Down Expand Up @@ -57,35 +57,32 @@ const StyledError = styled.Text`
`
export type DatePickerProps = {
title: string
value: DateTime | null
setValue: (fromDate: DateTime | null) => void
date: DateTime | null
setDate: (date: DateTime | null) => void
error?: string
modalState: boolean
setModalState: React.Dispatch<React.SetStateAction<boolean>>
modalOpen: boolean
setModalOpen: (open: boolean) => void
}
const DatePicker = ({ title, value, setValue, error, modalState, setModalState }: DatePickerProps): ReactElement => {
const formatDate = (date: DateTime | null, part: number) => date?.toFormat(DATE_FORMAT).split('/')[part]

const [inputDay, setInputDay] = useState(formatDate(value, 0))
const [inputMonth, setInputMonth] = useState(formatDate(value, 1))
const [inputYear, setInputYear] = useState(formatDate(value, 2))
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'))

useEffect(() => {
try {
setValue(DateTime.fromFormat(`${inputDay}/${inputMonth}/${inputYear}`, DATE_FORMAT).toLocal())
setDate(DateTime.fromFormat(`${inputDay}/${inputMonth}/${inputYear}`, DATE_FORMAT))
steffenkleinle marked this conversation as resolved.
Show resolved Hide resolved
} catch (e) {
setValue(null)
setDate(null)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputDay, inputMonth, inputYear])
}, [inputDay, inputMonth, inputYear, setDate])

useEffect(() => {
if (value) {
setInputDay(formatDate(value, 0))
setInputMonth(formatDate(value, 1))
setInputYear(formatDate(value, 2))
if (date) {
setInputDay(date.toFormat('dd'))
setInputMonth(date.toFormat('MM'))
setInputYear(date.toFormat('yyyy'))
}
}, [value])
}, [date])

return (
<DateContainer>
Expand All @@ -96,38 +93,32 @@ const DatePicker = ({ title, value, setValue, error, modalState, setModalState }
testID='DatePicker-day'
keyboardType='numeric'
maxLength={2}
onChangeText={event => {
setInputDay(event as string)
}}
onChangeText={setInputDay}
value={inputDay}
/>
<Text>/</Text>
<Text>.</Text>
<Input
testID='DatePicker-month'
keyboardType='numeric'
maxLength={2}
onChangeText={event => {
setInputMonth(event)
}}
onChangeText={setInputMonth}
value={inputMonth}
/>
<Text>/</Text>
<Text>.</Text>
<Input
testID='DatePicker-year'
maxLength={4}
keyboardType='numeric'
onChangeText={event => {
setInputYear(event)
}}
onChangeText={setInputYear}
value={inputYear}
/>
</Wrapper>
<StyledIconButton
$isModalOpen={modalState}
$isModalOpen={modalOpen}
icon={<Icon Icon={CalendarTodayIcon} />}
accessibilityLabel='calenderEventsIcon'
onPress={() => {
setModalState(true)
setModalOpen(true)
}}
/>
</StyledInputWrapper>
Expand Down
51 changes: 21 additions & 30 deletions native/src/components/EventsDateFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { TFunction } from 'i18next'
import { DateTime } from 'luxon'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
Expand Down Expand Up @@ -33,13 +32,11 @@ const StyledText = styled(Text)`
type ResetFilterTextProps = {
fromDate: DateTime | null
toDate: DateTime | null
defaultFromDate: DateTime | null
steffenkleinle marked this conversation as resolved.
Show resolved Hide resolved
defaultToDate: DateTime | null
t: TFunction<'events', undefined>
}

const ResetFilterText = ({ fromDate, toDate, defaultFromDate, defaultToDate, t }: ResetFilterTextProps) => {
const title = `${t('resetFilter')} ${fromDate?.toFormat('dd/MM/yy') ?? defaultFromDate?.toFormat('dd/MM/yy')} - ${toDate?.toFormat('dd/MM/yy') ?? defaultToDate?.toFormat('dd/MM/yy')}`
const ResetFilterText = ({ fromDate, toDate }: ResetFilterTextProps) => {
const { t } = useTranslation('events')
const title = `${t('resetFilter')} ${fromDate?.toFormat('dd.MM.yy')} - ${toDate?.toFormat('dd.MM.yy')}`
return <StyledText>{title}</StyledText>
}

Expand All @@ -50,8 +47,8 @@ type EventsDateFilterProps = {
toDate: DateTime | null
setToDate: (fromDate: DateTime | null) => void
toDateError: string | null
modalState: boolean
setModalState: React.Dispatch<React.SetStateAction<boolean>>
modalOpen: boolean
setModalOpen: (modalOpen: boolean) => void
}
const EventsDateFilter = ({
fromDate,
Expand All @@ -60,35 +57,35 @@ const EventsDateFilter = ({
toDate,
setToDate,
toDateError,
modalState,
setModalState,
modalOpen,
setModalOpen,
}: EventsDateFilterProps): JSX.Element => {
const defaultFromDate = DateTime.local().startOf('day')
const defaultToDate = DateTime.local().plus({ day: 10 }).startOf('day')
const defaultFromDate = DateTime.now().startOf('day')
const defaultToDate = DateTime.now().plus({ day: 10 }).startOf('day')
const [toggleDateFilter, setToggleDateFilter] = useState(true)
steffenkleinle marked this conversation as resolved.
Show resolved Hide resolved
const isReset = fromDate?.startOf('day').equals(defaultFromDate) && toDate?.startOf('day').equals(defaultToDate)
const { t } = useTranslation('events')
return (
<>
<DateSection>
<FilterToggle t={t} toggle={toggleDateFilter} setToggleDateFilter={setToggleDateFilter} />
<FilterToggle toggle={toggleDateFilter} setToggleDateFilter={setToggleDateFilter} />
{toggleDateFilter && (
<>
<DatePicker
modalState={modalState}
setModalState={setModalState}
setValue={setFromDate}
modalOpen={modalOpen}
setModalOpen={setModalOpen}
setDate={setFromDate}
title={t('from')}
error={(fromDateError as string) || ''}
value={fromDate}
error={t(fromDateError ?? '')}
date={fromDate}
/>
<DatePicker
modalState={modalState}
setModalState={setModalState}
setValue={setToDate}
modalOpen={modalOpen}
setModalOpen={setModalOpen}
setDate={setToDate}
title={t('to')}
error={(toDateError as string) || ''}
value={toDate}
error={t(toDateError ?? '')}
date={toDate}
/>
</>
)}
Expand All @@ -101,13 +98,7 @@ const EventsDateFilter = ({
setToDate(defaultToDate)
}}>
<Icon Icon={CloseIcon} />
<ResetFilterText
fromDate={fromDate}
toDate={toDate}
t={t}
defaultFromDate={defaultFromDate}
defaultToDate={defaultToDate}
/>
<ResetFilterText fromDate={fromDate ?? defaultFromDate} toDate={toDate ?? defaultToDate} />
</StyledButton>
)}
</>
Expand Down
18 changes: 10 additions & 8 deletions native/src/components/FilterToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TFunction } from 'i18next'
import React, { ReactElement } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components/native'

import { ExpandIcon, ShrinkIcon } from '../assets'
Expand All @@ -21,13 +21,15 @@ const StyledButton = styled.TouchableOpacity`
type DateFilterToggleProps = {
toggle: boolean
setToggleDateFilter: React.Dispatch<React.SetStateAction<boolean>>
t: TFunction<'events', undefined>
}

const FilterToggle = ({ toggle, setToggleDateFilter, t }: DateFilterToggleProps): ReactElement => (
<StyledButton onPress={() => setToggleDateFilter((prev: boolean) => !prev)}>
<Icon Icon={toggle ? ShrinkIcon : ExpandIcon} />
{toggle ? <StyledText>{t('hideFilters')}</StyledText> : <StyledText>{t('showFilters')}</StyledText>}
</StyledButton>
)
const FilterToggle = ({ toggle, setToggleDateFilter }: DateFilterToggleProps): ReactElement => {
const { t } = useTranslation('events')
return (
<StyledButton onPress={() => setToggleDateFilter((prev: boolean) => !prev)}>
<Icon Icon={toggle ? ShrinkIcon : ExpandIcon} />
<StyledText>{t(toggle ? 'hideFilters' : 'showFilters')}</StyledText>
</StyledButton>
)
}
export default FilterToggle
21 changes: 5 additions & 16 deletions native/src/components/__tests__/CalendarRangeModal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,7 @@ import React from 'react'
import renderWithTheme from '../../testing/render'
import CalendarRangeModal from '../CalendarRangeModal'

jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => {
const translations: Record<string, string> = {
'layout:cancel': 'Cancel',
ok: 'OK',
selectRange: 'Select Date Range',
}
return translations[key] || key
},
}),
}))
jest.mock('react-i18next')

describe('CalendarRangeModal', () => {
beforeEach(jest.clearAllMocks)
Expand All @@ -29,25 +18,25 @@ describe('CalendarRangeModal', () => {
renderWithTheme(
<CalendarRangeModal
closeModal={closeModal}
fromDate={DateTime.local()}
fromDate={DateTime.now()}
modalVisible
setFromDate={setFromDate}
setToDate={setToDate}
toDate={DateTime.local().plus({ day: 2 })}
toDate={DateTime.now().plus({ day: 2 })}
/>,
)

it('should close modal on "Cancel" button press', () => {
const { getByText } = renderCalendarRangeModal()

fireEvent.press(getByText('Cancel'))
fireEvent.press(getByText('layout:cancel'))
expect(closeModal).toHaveBeenCalledTimes(1)
})

it('should set dates and close modal on "OK" button press', () => {
const { getByText } = renderCalendarRangeModal()

fireEvent.press(getByText('OK'))
fireEvent.press(getByText('common:ok'))
expect(setFromDate).toHaveBeenCalled()
expect(setToDate).toHaveBeenCalled()
expect(closeModal).toHaveBeenCalledTimes(1)
Expand Down
Loading
Loading