Skip to content

Commit

Permalink
ExpandableCalendar to ts
Browse files Browse the repository at this point in the history
  • Loading branch information
Inbal-Tish committed Aug 10, 2021
1 parent f7eef22 commit 1354056
Show file tree
Hide file tree
Showing 15 changed files with 491 additions and 330 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import _ from 'lodash';
import React, {Component, useCallback} from 'react';
import {Platform, StyleSheet, Alert, View, Text, TouchableOpacity, Button} from 'react-native';
// @ts-expect-error
import {ExpandableCalendar, AgendaList, CalendarProvider, WeekCalendar} from 'react-native-calendars';


const testIDs = require('../testIDs');

const today = new Date().toISOString().split('T')[0];
Expand Down Expand Up @@ -128,7 +130,11 @@ function getTheme() {
const leftArrowIcon = require('../img/previous.png');
const rightArrowIcon = require('../img/next.png');

export default class ExpandableCalendarScreen extends Component {
interface Props {
weekView?: boolean
}

export default class ExpandableCalendarScreen extends Component<Props> {
marked = getMarkedDates(ITEMS);
theme = getTheme();
todayBtnTheme = {
Expand All @@ -144,7 +150,7 @@ export default class ExpandableCalendarScreen extends Component {
// console.warn('ExpandableCalendarScreen onMonthChange: ', month, updateSource);
};

renderItem = ({item}) => {
renderItem = ({item}: any) => {
return <AgendaItem item={item}/>;
};

Expand Down Expand Up @@ -191,7 +197,11 @@ export default class ExpandableCalendarScreen extends Component {
}
}

const AgendaItem = React.memo(function AgendaItem(props) {
interface ItemProps {
item: any
}

const AgendaItem = React.memo(function AgendaItem(props: ItemProps) {
// console.warn('item rendered', Date.now());
const {item} = props;

Expand Down
26 changes: 10 additions & 16 deletions src/expandableCalendar/Context/Presenter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,18 @@ import XDate from 'xdate';
import {UPDATE_SOURCES} from '../commons';
import {toMarkingFormat} from '../../interface';


describe('Context provider tests', () => {
const makeUUT = () => {
return new Presenter();
};

const pastDate = '2021-04-04';

const futureDate = '2050-04-04';

const today1 = XDate();

const today2 = new Date();

const todayDate = toMarkingFormat(XDate());

const updateSource = UPDATE_SOURCES.CALENDAR_INIT;
const updateSources = UPDATE_SOURCES.CALENDAR_INIT;

describe('isPastDate function tests', () => {
it('Expect to get true while passing a past date', () => {
Expand All @@ -35,7 +31,6 @@ describe('Context provider tests', () => {
describe('Button Icon test', () => {
it('Expect to get down button on past date', () => {
const {getButtonIcon} = makeUUT();

const imageUp = '../../../src/img/up.png';
const imageDown = '../../../src/img/down.png';

Expand All @@ -62,25 +57,25 @@ describe('Context provider tests', () => {
const {setDate} = makeUUT();
const date = '2021-01-01';
const sameMonthDate = '2021-01-20';

const props = {onDateChanged, onMonthChange, showTodayButton: false};
setDate(props, date, sameMonthDate, updateState, updateSource);

setDate(props, date, sameMonthDate, updateState, updateSources);

expect(updateState).toBeCalled();
expect(onDateChanged).toBeCalledWith(date, updateSource);
expect(onDateChanged).toBeCalledWith(date, updateSources);
expect(onMonthChange).not.toBeCalled();
});

it('Expect onMonthChange to be called when different months date passed', () => {
const {setDate} = makeUUT();
const date = '2021-01-01';
const differentMonth = '2021-02-20';

const props = {onDateChanged, onMonthChange, showTodayButton: false};
setDate(props, date, differentMonth, updateState, updateSource);

setDate(props, date, differentMonth, updateState, updateSources);

expect(updateState).toBeCalled();
expect(onDateChanged).toBeCalledWith(date, updateSource);
expect(onDateChanged).toBeCalledWith(date, updateSources);
expect(onMonthChange).toBeCalled();
});
});
Expand Down Expand Up @@ -138,8 +133,8 @@ describe('Context provider tests', () => {
it("Expect animation value to be top position when today's date passed", () => {
const {getPositionAnimation} = makeUUT();
const TOP_POSITION = 65;

const {tension, friction, useNativeDriver} = getPositionAnimation(todayDate, 10);

expect(tension).toEqual(30);
expect(friction).toEqual(8);
expect(useNativeDriver).toBe(true);
Expand All @@ -159,8 +154,8 @@ describe('Context provider tests', () => {
it('Expect opacity animation value', () => {
const {getOpacityAnimation} = makeUUT();
const disabledOpacity = 0.5;

let data = getOpacityAnimation({disabledOpacity}, true);

expect(data.toValue).toBe(0.5);

data = getOpacityAnimation({disabledOpacity}, false);
Expand All @@ -174,7 +169,6 @@ describe('Context provider tests', () => {
describe('onTodayPressed tests', () => {
it("Expect return value to be XDate today's date", () => {
const {getTodayDate} = makeUUT();

expect(getTodayDate()).toEqual(todayDate);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import _ from 'lodash';
import XDate from 'xdate';
import {sameMonth as dateutils_sameMonth} from '../../dateutils';

// @ts-expect-error
import {sameMonth} from '../../dateutils';
// @ts-expect-error
import {xdateToData, toMarkingFormat} from '../../interface';
import {CalendarContextProviderProps} from './Provider';
import {UpdateSource} from '../../types';


const commons = require('../commons');
const TOP_POSITION = 65;

class Presenter {
_isPastDate(date) {
const today = XDate();
const d = XDate(date);
_isPastDate(date: Date) {
const today = new XDate();
const d = new XDate(date);

if (today.getFullYear() > d.getFullYear()) {
return true;
Expand All @@ -35,48 +41,48 @@ class Presenter {
return require('../../img/up.png');
};

getButtonIcon = (date, showTodayButton = true) => {
getButtonIcon = (date: Date, showTodayButton = true) => {
if (!showTodayButton) {
return undefined;
}
const icon = this._isPastDate(date) ? this._getIconDown() : this._getIconUp();
return icon;
};

setDate = (props, date, newDate, updateState, updateSource) => {
const sameMonth = dateutils_sameMonth(XDate(date), XDate(newDate));
setDate = (props: CalendarContextProviderProps, date: Date, newDate: Date, updateState: (buttonIcon: any) => any, updateSource: UpdateSource) => {
const isSameMonth = sameMonth(new XDate(date), new XDate(newDate));
const buttonIcon = this.getButtonIcon(date, props.showTodayButton);

updateState(buttonIcon);

_.invoke(props, 'onDateChanged', date, updateSource);

if (!sameMonth) {
_.invoke(props, 'onMonthChange', xdateToData(XDate(date)), updateSource);
if (!isSameMonth) {
_.invoke(props, 'onMonthChange', xdateToData(new XDate(date)), updateSource);
}
};

setDisabled = (showTodayButton, newDisabledValue, oldDisabledValue, updateState) => {
setDisabled = (showTodayButton: boolean, newDisabledValue: boolean, oldDisabledValue: boolean, updateState: (disabled: boolean) => void) => {
if (!showTodayButton || newDisabledValue === oldDisabledValue) {
return;
}
updateState(newDisabledValue);
};

shouldAnimateTodayButton = props => {
shouldAnimateTodayButton = (props: CalendarContextProviderProps) => {
return props.showTodayButton;
};

_isToday = date => {
const today = toMarkingFormat(XDate());
_isToday = (date: Date) => {
const today = toMarkingFormat(new XDate());
return today === date;
};

getTodayDate = () => {
return toMarkingFormat(XDate());
return toMarkingFormat(new XDate());
};

getPositionAnimation = (date, todayBottomMargin) => {
getPositionAnimation = (date: Date, todayBottomMargin = 0) => {
const toValue = this._isToday(date) ? TOP_POSITION : -todayBottomMargin || -TOP_POSITION;
return {
toValue,
Expand All @@ -86,19 +92,20 @@ class Presenter {
};
};

shouldAnimateOpacity = props => {
shouldAnimateOpacity = (props: CalendarContextProviderProps) => {
return props.disabledOpacity;
};

getOpacityAnimation = (props, disabled) => {
getOpacityAnimation = (props: CalendarContextProviderProps, disabled: boolean) => {
return {
toValue: disabled ? props.disabledOpacity : 1,
toValue: disabled ? props.disabledOpacity || 0 : 1,
duration: 500,
useNativeDriver: true
};
};

getTodayFormatted = () => {
// @ts-expect-error
const todayString = XDate.locales[XDate.defaultLocale].today || commons.todayString;
const today = todayString.charAt(0).toUpperCase() + todayString.slice(1);
return today;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
import React, {Component} from 'react';
import {StyleSheet, Animated, TouchableOpacity, View} from 'react-native';
import PropTypes from 'prop-types';
import XDate from 'xdate';

import React, {Component} from 'react';
import {StyleSheet, Animated, TouchableOpacity, View, StyleProp, ViewStyle} from 'react-native';

// @ts-expect-error
import {toMarkingFormat} from '../../interface';
import {Theme, UpdateSource, DateData} from '../../types';
import styleConstructor from '../style';
import CalendarContext from '.';
import Presenter from './Presenter';
import {toMarkingFormat} from '../../interface';


const commons = require('../commons');
const UPDATE_SOURCES = commons.UPDATE_SOURCES;
const updateSources = commons.UPDATE_SOURCES;
const TOP_POSITION = 65;

interface Props {
/** Initial date in 'yyyy-MM-dd' format. Default = Date() */
date: Date;
/** Callback for date change event */
onDateChanged?: () => Date,
/** Callback for month change event */
onMonthChange?: () => DateData,
/** Whether to show the today button */
showTodayButton?: boolean;
/** Today button's top position */
todayBottomMargin?: number;
/** Today button's style */
todayButtonStyle?: ViewStyle;
/** The opacity for the disabled today button (0-1) */
disabledOpacity?: number;
style?: StyleProp<ViewStyle>;
theme?: Theme;
}
export type CalendarContextProviderProps = Props;

/**
* @description: Calendar context provider component
* @example: https://github.com/wix/react-native-calendars/blob/master/example/src/screens/expandableCalendar.js
*/
class CalendarProvider extends Component {
class CalendarProvider extends Component<Props> {
static displayName = 'CalendarProvider';

static propTypes = {
Expand All @@ -35,26 +60,22 @@ class CalendarProvider extends Component {
disabledOpacity: PropTypes.number
};

constructor(props) {
super(props);
this.style = styleConstructor(props.theme);
this.presenter = new Presenter();
const {showTodayButton} = props;

this.state = {
prevDate: this.props.date || toMarkingFormat(XDate()),
date: this.props.date || toMarkingFormat(XDate()),
updateSource: UPDATE_SOURCES.CALENDAR_INIT,
buttonY: new Animated.Value(-props.todayBottomMargin || -TOP_POSITION),
buttonIcon: this.presenter.getButtonIcon(props.date, showTodayButton),
disabled: false,
opacity: new Animated.Value(1)
};
}
style = styleConstructor(this.props.theme);
presenter = new Presenter();

state = {
prevDate: this.props.date || toMarkingFormat(new XDate()),
date: this.props.date || toMarkingFormat(new XDate()),
updateSource: updateSources.CALENDAR_INIT,
buttonY: new Animated.Value(this.props.todayBottomMargin ? -this.props.todayBottomMargin : -TOP_POSITION),
buttonIcon: this.presenter.getButtonIcon(this.props.date, this.props.showTodayButton),
disabled: false,
opacity: new Animated.Value(1)
};

componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: Props) {
if (prevProps.date !== this.props.date) {
this.setDate(this.props.date, UPDATE_SOURCES.PROP_UPDATE);
this.setDate(this.props.date, updateSources.PROP_UPDATE);
}
}

Expand All @@ -68,10 +89,10 @@ class CalendarProvider extends Component {
};
};

setDate = (date, updateSource) => {
setDate = (date: Date, updateSource: UpdateSource) => {
const {setDate} = this.presenter;

const updateState = buttonIcon => {
const updateState = (buttonIcon: any) => {
this.setState({date, prevDate: this.state.date, updateSource, buttonIcon}, () => {
this.animateTodayButton(date);
});
Expand All @@ -80,19 +101,19 @@ class CalendarProvider extends Component {
setDate(this.props, date, this.state.date, updateState, updateSource);
};

setDisabled = disabled => {
setDisabled = (disabled: boolean) => {
const {setDisabled} = this.presenter;
const {showTodayButton} = this.props;
const {showTodayButton = false} = this.props;

const updateState = disabled => {
const updateState = (disabled: boolean) => {
this.setState({disabled});
this.animateOpacity(disabled);
};

setDisabled(showTodayButton, disabled, this.state.disabled, updateState);
};

animateTodayButton(date) {
animateTodayButton(date: Date) {
const {shouldAnimateTodayButton, getPositionAnimation} = this.presenter;

if (shouldAnimateTodayButton(this.props)) {
Expand All @@ -104,7 +125,7 @@ class CalendarProvider extends Component {
}
}

animateOpacity(disabled) {
animateOpacity(disabled: boolean) {
const {shouldAnimateOpacity, getOpacityAnimation} = this.presenter;

if (shouldAnimateOpacity(this.props)) {
Expand All @@ -118,7 +139,7 @@ class CalendarProvider extends Component {

onTodayPress = () => {
const today = this.presenter.getTodayDate();
this.setDate(today, UPDATE_SOURCES.TODAY_PRESS);
this.setDate(today, updateSources.TODAY_PRESS);
};

renderTodayButton() {
Expand Down
File renamed without changes.
Loading

0 comments on commit 1354056

Please sign in to comment.