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

[Calendar] Remove promise based loading in favor of loading prop #1829

Merged
merged 5 commits into from May 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -36,3 +36,5 @@ coverage
# editors
.vs
.DS_Store

.vercel
1 change: 1 addition & 0 deletions docs/.gitignore
@@ -0,0 +1 @@
.vercel
3 changes: 3 additions & 0 deletions docs/babel.config.js
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
@@ -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
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
@@ -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
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} />

#### Components API
Expand Down