Skip to content

Commit

Permalink
[Calendar] Remove promise based loading in favor of loading prop (#…
Browse files Browse the repository at this point in the history
…1829)

* Remove `onMonthChange` promise-based api in favor of `loading` prop

* Update server request example for new API

* Implement fake api for server request example

* Update tests

* Make CalendarSkeleton component and use it in the examples for loading state
  • Loading branch information
dmtrKovalenko committed May 28, 2020
1 parent 9fa389f commit c932737
Show file tree
Hide file tree
Showing 18 changed files with 416 additions and 269 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ coverage
# editors
.vs
.DS_Store

.vercel
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vercel
3 changes: 3 additions & 0 deletions docs/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module.exports = {
'babel-plugin-module-resolver',
{
root: ['./'],
alias: {
'@material-ui/pickers/CalendarSkeleton': '@material-ui/pickers/src/CalendarSkeleton',
},
},
],
],
Expand Down
32 changes: 32 additions & 0 deletions docs/fakeApi/randomDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { getDaysInMonth, isValid } from 'date-fns';
import { NowRequest, NowResponse } from '@now/node';

function getRandomNumber(min: number, max: number) {
return Math.round(Math.random() * (max - min) + min);
}

export default function(req: NowRequest, res: NowResponse) {
const { month } = req.query;

if (!month || typeof month !== 'string') {
res.status(400);
return res.json({
reason: 'month query param is required',
});
}

const date = new Date(month);
if (!isValid(date)) {
res.status(422);
return res.json({
reason: 'cannot parse month value',
});
}

setTimeout(() => {
const daysInMonth = getDaysInMonth(date);
const daysToHighlight = [1, 2, 3].map(_ => getRandomNumber(1, daysInMonth));

res.json({ daysToHighlight });
}, 500); // fake some long work
}
3 changes: 3 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
"@mapbox/rehype-prism": "^0.4.0",
"@material-ui/core": "^4.9.14",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.54",
"@material-ui/pickers": "^4.0.0-alpha.1",
"@now/node": "^1.6.1",
"@types/fuzzy-search": "^2.1.0",
"@types/isomorphic-fetch": "^0.0.35",
"@types/jss": "^10.0.0",
Expand Down Expand Up @@ -57,6 +59,7 @@
"next-images": "^1.4.0",
"next-transpile-modules": "^2.0.0",
"notistack": "^0.9.11",
"now": "^19.0.1",
"prismjs": "^1.20.0",
"raw-loader": "^1.0.0",
"react": "^16.13.0",
Expand Down
56 changes: 38 additions & 18 deletions docs/pages/demo/datepicker/ServerRequest.example.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,61 @@
import React, { useState } from 'react';
import * as React from 'react';
import { Badge } from '@material-ui/core';
import { TextField } from '@material-ui/core';
import { DatePicker, Day } from '@material-ui/pickers';
import { makeJSDateObject } from '../../../utils/helpers';

function getRandomNumber(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
import { CalendarSkeleton } from '@material-ui/pickers/CalendarSkeleton';

function ServerRequest() {
const [selectedDays, setSelectedDays] = useState([1, 2, 15]);
const [selectedDate, handleDateChange] = useState(new Date());

const handleMonthChange = async () => {
// just select random days to simulate server side based data
return new Promise(resolve => {
setTimeout(() => {
setSelectedDays([1, 2, 3].map(() => getRandomNumber(1, 28)));
resolve();
}, 1000);
});
const requestAbortController = React.useRef(null);
const [highlightedDays, setHighlightedDays] = React.useState([1, 2, 15]);
const [selectedDate, handleDateChange] = React.useState(new Date());

React.useEffect(() => {
// abort request on unmount
return () => requestAbortController.current?.abort();
}, []);

const handleMonthChange = date => {
if (requestAbortController.current) {
// make sure that you are aborting useless requests
// because it is possible to switch between months pretty quickly
requestAbortController.current.abort();
}

setHighlightedDays(null);

const controller = new AbortController();
fetch(`/fakeApi/randomDate?month=${date.toString()}`, {
signal: controller.signal,
})
.then(res => res.json())
.then(({ daysToHighlight }) => setHighlightedDays(daysToHighlight))
.catch(() => console.log('Wow, you are switching months too quickly 🐕'));

requestAbortController.current = controller;
};

return (
<>
<DatePicker
value={selectedDate}
loading={highlightedDays === null}
onChange={date => handleDateChange(date)}
onMonthChange={handleMonthChange}
// loading
renderInput={props => <TextField {...props} />}
renderLoading={() => <CalendarSkeleton />}
renderDay={(day, selectedDate, DayComponentProps) => {
const date = makeJSDateObject(day); // skip this step, it is required to support date libs
const isSelected =
DayComponentProps.inCurrentMonth && selectedDays.includes(date.getDate());
DayComponentProps.inCurrentMonth && highlightedDays.includes(date.getDate());

return (
<Badge overlap="circle" badgeContent={isSelected ? '🌚' : undefined}>
<Badge
key={date.toString()}
overlap="circle"
badgeContent={isSelected ? '🌚' : undefined}
>
<Day {...DayComponentProps} />
</Badge>
);
Expand Down
5 changes: 3 additions & 2 deletions docs/pages/demo/datepicker/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ You can leverage our internal [Day](/api/Day) component, and render it in defaul

#### Dynamic data

Sometimes it's required to display additional info right in the calendar.
For this just return `Promise` in `onMonthChange`.
Sometimes it's required to display additional info right in the calendar. Here is an example of prefetching
and displaying server side data using `onMonthChange`, `loading` and `renderDay` props.

<!-- In order to make this example functional, make sure you are running docs using `yarn now dev` from root -->
<Example source={ServerRequest} />

#### API
Expand Down
Loading

1 comment on commit c932737

@vercel
Copy link

@vercel vercel bot commented on c932737 May 28, 2020

Choose a reason for hiding this comment

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

Please sign in to comment.