Skip to content

Commit

Permalink
refactor(date-picker): class to function component & write tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Luckened committed Oct 14, 2020
1 parent ede530c commit 5e4ee42
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 180 deletions.
276 changes: 122 additions & 154 deletions src/DatePicker/DatePicker.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { useState } from 'react';
import propTypes from 'prop-types';

import styled from 'styled-components';
Expand Down Expand Up @@ -58,161 +58,129 @@ function dayIndex(year, month, day) {
return new Date(year, month, day).getDay();
}

class DatePicker extends Component {
static propTypes = {
className: propTypes.string,
shadow: propTypes.bool,
onAccept: propTypes.func,
onCancel: propTypes.func,
date: propTypes.instanceOf(Date)
};

static defaultProps = {
shadow: true,
className: '',
onAccept: null,
onCancel: null,
date: null
};

constructor(props) {
super(props);

const initialDate = this.convertDateToState(props.date || new Date());
this.state = initialDate;
}

convertDateToState = date => {
const day = date.getDate();
const month = date.getMonth();
const year = date.getFullYear();

return { day, month, year };
};

handleMonthSelect = e => this.setState({ month: e.target.value });

handleYearSelect = year => this.setState({ year });

handleDaySelect = day => this.setState({ day });

handleAccept = () => {
const { year, month, day } = this.state;
const { onAccept } = this.props;
const date = new Date(year, month, day);

onAccept(date);
};

render() {
let { day } = this.state;
const { month, year } = this.state;
const { shadow, className, onAccept, onCancel } = this.props;

const months = [
{ value: 0, label: 'January' },
{ value: 1, label: 'February' },
{ value: 2, label: 'March' },
{ value: 3, label: 'April' },
{ value: 4, label: 'May' },
{ value: 5, label: 'June' },
{ value: 6, label: 'July' },
{ value: 7, label: 'August' },
{ value: 8, label: 'September' },
{ value: 9, label: 'October' },
{ value: 10, label: 'November' },
{ value: 11, label: 'December' }
];

// eslint-disable-next-line
const dayPickerItems = Array.apply(null, { length: 35 });
const firstDayIndex = dayIndex(year, month, 1);

const daysNumber = daysInMonth(year, month);
day = day < daysNumber ? day : daysNumber;
dayPickerItems.forEach((item, i) => {
if (i >= firstDayIndex && i < daysNumber + firstDayIndex) {
const dayNumber = i - firstDayIndex + 1;

dayPickerItems[i] = (
<DateItem
// eslint-disable-next-line
key={i}
const months = [
{ value: 0, label: 'January' },
{ value: 1, label: 'February' },
{ value: 2, label: 'March' },
{ value: 3, label: 'April' },
{ value: 4, label: 'May' },
{ value: 5, label: 'June' },
{ value: 6, label: 'July' },
{ value: 7, label: 'August' },
{ value: 8, label: 'September' },
{ value: 9, label: 'October' },
{ value: 10, label: 'November' },
{ value: 11, label: 'December' }
];

const DatePicker = props => {
const { className, shadow, onAccept, onCancel, date } = props;

const initialDate = date || new Date();

const [day, setDay] = useState(initialDate.getDate());
const [month, setMonth] = useState(initialDate.getMonth());
const [year, setYear] = useState(initialDate.getFullYear());

// eslint-disable-next-line
const dayPickerItems = Array.apply(null, { length: 35 });
const firstDayIndex = dayIndex(year, month, 1);

const daysNumber = daysInMonth(year, month);

dayPickerItems.forEach((_, i) => {
if (i >= firstDayIndex && i < daysNumber + firstDayIndex) {
const dayNumber = i - firstDayIndex + 1;

dayPickerItems[i] = (
<DateItem key={i} onClick={() => setDay(dayNumber)}>
<DateItemContent active={dayNumber === day}>
{dayNumber}
</DateItemContent>
</DateItem>
);
} else {
dayPickerItems[i] = <DateItem key={i} />;
}
});

return (
<Window style={{ margin: 20 }} className={className} shadow={shadow}>
<WindowHeader>
<span role='img' aria-label='馃搯'>
馃搯
</span>
Date
</WindowHeader>
<WindowContent>
<Toolbar noPadding style={{ justifyContent: 'space-between' }}>
<Select
options={months}
value={month}
onChange={e => setMonth(e.target.value)}
width={128}
menuMaxHeight={200}
/>
<NumberField
value={year}
disableKeyboardInput
onChange={y => setYear(parseInt(y, 10))}
width={100}
/>
</Toolbar>
<Calendar>
<WeekDays>
<DateItem>S</DateItem>
<DateItem>M</DateItem>
<DateItem>T</DateItem>
<DateItem>W</DateItem>
<DateItem>T</DateItem>
<DateItem>F</DateItem>
<DateItem>S</DateItem>
</WeekDays>
<Dates>{dayPickerItems}</Dates>
</Calendar>
<Toolbar noPadding style={{ justifyContent: 'space-between' }}>
<Button
data-testid='cancel'
fullWidth
onClick={() => {
this.handleDaySelect(dayNumber);
onCancel();
}}
disabled={!onCancel}
>
<DateItemContent active={dayNumber === day}>
{dayNumber}
</DateItemContent>
</DateItem>
);
} else {
dayPickerItems[i] = (
<DateItem
// eslint-disable-next-line
key={i}
/>
);
}
});

return (
<Window style={{ margin: 20 }} className={className} shadow={shadow}>
<WindowHeader>
<span role='img' aria-label='馃搯'>
馃搯
</span>
Date
</WindowHeader>
<WindowContent>
<Toolbar noPadding style={{ justifyContent: 'space-between' }}>
<Select
options={months}
value={month}
onChange={this.handleMonthSelect}
width={128}
menuMaxHeight={200}
/>
<NumberField
value={year}
disableKeyboardInput
onChange={this.handleYearSelect}
width={100}
/>
</Toolbar>
<Calendar>
<WeekDays>
<DateItem>S</DateItem>
<DateItem>M</DateItem>
<DateItem>T</DateItem>
<DateItem>W</DateItem>
<DateItem>T</DateItem>
<DateItem>F</DateItem>
<DateItem>S</DateItem>
</WeekDays>
<Dates>{dayPickerItems}</Dates>
</Calendar>
<Toolbar noPadding style={{ justifyContent: 'space-between' }}>
<Button
fullWidth
onClick={onCancel || undefined}
disabled={!onCancel}
>
Cancel
</Button>
<Button
fullWidth
onClick={onAccept ? this.handleAccept : undefined}
disabled={!onAccept}
>
OK
</Button>
</Toolbar>
</WindowContent>
</Window>
);
}
}
Cancel
</Button>
<Button
data-testid='ok'
fullWidth
onClick={() => {
onAccept(new Date(year, month, day));
}}
disabled={!onAccept}
>
OK
</Button>
</Toolbar>
</WindowContent>
</Window>
);
};

DatePicker.defaultProps = {
shadow: true,
className: '',
onAccept: null,
onCancel: null,
date: null
};

DatePicker.propTypes = {
className: propTypes.string,
shadow: propTypes.bool,
onAccept: propTypes.func,
onCancel: propTypes.func,
date: propTypes.instanceOf(Date)
};

export default DatePicker;
30 changes: 30 additions & 0 deletions src/DatePicker/DatePicker.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';

import { renderWithTheme } from '../../test/utils';
import DatePicker from './DatePicker';

describe('<DatePicker />', () => {
it('should call onAccept correctly', () => {
const handleAccept = jest.fn();

const { getByTestId } = renderWithTheme(
<DatePicker onAccept={handleAccept} />
);

const okButton = getByTestId('ok');
okButton.click();
expect(handleAccept).toHaveBeenCalledTimes(1);
});

it('should call onCancel correctly', () => {
const handleCancel = jest.fn();

const { getByTestId } = renderWithTheme(
<DatePicker onCancel={handleCancel} />
);

const cancelButton = getByTestId('cancel');
cancelButton.click();
expect(handleCancel).toHaveBeenCalledTimes(1);
});
});
53 changes: 27 additions & 26 deletions src/DatePicker/DatePicker.stories.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
// 猸曪笍 DON'T SHOW DATEPICKER BEFORE IT'S FINISHED AND TESTED 猸曪笍
import React from 'react';
import DatePicker from './DatePicker';

// import React from 'react';
// import { DatePicker } from 'react95';
export default {
title: 'DatePicker',
component: DatePicker,
decorators: [
story => (
<div
style={{
padding: '5rem',
background: 'teal'
}}
>
{story()}
</div>
)
]
};

// export default {
// title: 'DatePicker',
// component: DatePicker,
// decorators: [
// story => (
// <div
// style={{
// padding: '5rem',
// background: 'teal'
// }}
// >
// {story()}
// </div>
// )
// ]
// };
export const Default = () => (
<DatePicker
onAccept={date => alert(`selected day is: ${date}`)}
onCancel={() => alert('Cancel')}
/>
);

// export const Default = () => (
// <DatePicker onAccept={date => console.log(date)} />
// );

// Default.story = {
// name: 'default'
// };
Default.story = {
name: 'default'
};

0 comments on commit 5e4ee42

Please sign in to comment.