├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .travis.yml
├── CHANGELOG.MD
├── README.MD
├── __tests__
├── Calendar.react-test.js
├── Day.react-test.js
├── Month.react-test.js
├── Timeslot.react-test.js
├── Week.react-test.js
└── __snapshots__
│ ├── Calendar.react-test.js.snap
│ ├── Day.react-test.js.snap
│ ├── Month.react-test.js.snap
│ ├── Timeslot.react-test.js.snap
│ └── Week.react-test.js.snap
├── build
└── build.min.js
├── index.html
├── package.json
├── public
├── atom-one-dark.css
└── build.js
├── src
├── js
│ ├── components
│ │ ├── .gitkeep
│ │ ├── calendar.jsx
│ │ ├── day.jsx
│ │ ├── month.jsx
│ │ ├── timeslot.jsx
│ │ └── week.jsx
│ ├── constants
│ │ ├── day.js
│ │ ├── timeslot.js
│ │ └── week.js
│ ├── demo
│ │ ├── .gitkeep
│ │ ├── app.jsx
│ │ └── snippets
│ │ │ └── custom-timeslot.md
│ ├── react-timeslot-calendar.jsx
│ └── util
│ │ ├── helpers.js
│ │ └── markdown-snippet.jsx
└── styles
│ ├── .gitkeep
│ ├── abstract
│ └── _variables.scss
│ ├── components
│ ├── _calendar.scss
│ ├── _day.scss
│ ├── _month.scss
│ ├── _timeslot.scss
│ └── _week.scss
│ ├── demo
│ └── main.scss
│ └── main.scss
├── webpack.config.js
└── webpack.demo.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 | root = true
5 |
6 |
7 | [*]
8 | # Change these settings to your own preference
9 | indent_style = space
10 | indent_size = 2
11 |
12 | # We recommend you to keep these unchanged
13 | end_of_line = lf
14 | charset = utf-8
15 | trim_trailing_whitespace = true
16 | insert_final_newline = true
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 | indent_size = 4
21 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:jest/recommended"],
3 | "parserOptions": {
4 | "ecmaVersion": 6,
5 | "sourceType": "module",
6 | "ecmaFeatures": {
7 | "jsx": true
8 | }
9 | },
10 | "plugins": ["react", "jest"],
11 | "rules": {
12 | "indent": ["error", 2],
13 | "linebreak-style": ["error", "unix"],
14 | "quotes": ["error", "single"],
15 | "semi": ["error", "always"],
16 | "no-cond-assign": ["error", "always"],
17 | "no-unused-vars": ["error", {"argsIgnorePattern": "state|action|dispatch"}],
18 | "no-console": "off",
19 | "no-extra-boolean-cast": "off",
20 | "camelcase": 2,
21 | "comma-dangle": ["error", "always-multiline"],
22 | "keyword-spacing": ["error", { "after": true, "before": true }],
23 | "react/jsx-curly-spacing": ["error", "always"],
24 | "react/jsx-equals-spacing": ["error", "always"]
25 | },
26 | "globals": {
27 | "document": true,
28 | "console": true,
29 | "__dirname": true,
30 | "module": true,
31 | },
32 | "env": {
33 | "jest/globals": true
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | package-lock.json
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6"
4 |
--------------------------------------------------------------------------------
/CHANGELOG.MD:
--------------------------------------------------------------------------------
1 | ## Version 0.1.3:
2 | * **Bug Fix**: Improve date handling for disabled dates. Before it was only considering startDate, now, it takes into account startDate && endDate.
3 |
4 | ## Version 0.1.2:
5 | * **Bug Fix**: Selecting a Timeslot made calendar return to initial date.
6 |
7 | ## Version 0.1.1:
8 | * Improved build size.
9 | * Input values now return as Moment's Default Format (YYYY-MM-DDThh:mm:ssTZD) instead of a custom format (MMMM Do YYYY, h:mm:ss A).
10 |
11 | ## Version 0.1.0:
12 | * Initial Release.
13 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # React Timeslot Calendar
2 | [](https://travis-ci.org/lrojas94/react-timeslot-calendar)
3 |
4 | A calendar component which allows users to select and reserve avaiblable timeslots so that they can setup a meeting and/or event.
5 |
6 | ## Dependencies
7 | * React 15.4.4 and up
8 | * MomentJS
9 | * CalendarJS
10 |
11 | ## Installation
12 | ```
13 | npm install --save react-timeslot-calendar
14 | ```
15 |
16 | ## Usage:
17 | ```js
18 | import moment from 'moment';
19 | import ReactTimeslotCalendar from 'react-timeslot-calendar';
20 |
21 | ...
22 | render() {
23 | return (
24 |
27 | );
28 | }
29 | ...
30 | ```
31 |
32 | ## Props
33 | ### Required Props:
34 | *Note*: All format strings are read with `moment`, so any format supported by `moment` is available here.
35 |
36 |
37 | * **initialDate** (`String`): An ISO formatted date compatible with `momentJS`. A `moment` instance is also a valid input.
38 |
39 | ### Optional
40 | * **timeslots** (`Object`): An array of available timeslots in the format:
41 | ```js
42 | let timeslots = [
43 | ['1', '2'], // 1:00 AM - 2:00 AM
44 | ['2', '3'], // 2:00 AM - 3:00 AM
45 | ['4', '6'], // 4:00 AM - 6:00 AM
46 | '5', // 5:00 AM
47 | ['4', '6', '7', '8'], // 4:00 AM - 6:00 AM - 7:00AM - 8:00AM
48 | ];
49 | ```
50 | By default, one hour timeslots from 12:00AM to 11:00PM are provided.
51 |
52 | * **timeslotProps** (`Object`): How the timeslots will be interpreted by the calendar. By default, the format goes as follows:
53 | ```js
54 | let timeslotProps = {
55 | format: 'h', // Each element in the timeslot array is an Hour
56 | showFormat: 'h:mm A', // They will be displayed as Hour:Minutes AM/PM
57 | }
58 | ```
59 |
60 | * **disabledTimeslots** (`Object`): Timeslots disabled by default. a `startDate` and `format` should be provided. The resulting date **must** match with one of the timeslots.
61 | ```js
62 | // sample:
63 | let disabledTimeslots = {[
64 | {
65 | startDate: 'April 30th 2017, 12:00:00 AM',
66 | format: 'MMMM Do YYYY, h:mm:ss A',
67 | },
68 | {
69 | startDate: 'May 1st 2017, 3:00:00 PM',
70 | format: 'MMMM Do YYYY, h:mm:ss A',
71 | },
72 | {
73 | startDate: 'May 5th 2017, 6:00:00 PM',
74 | format: 'MMMM Do YYYY, h:mm:ss A',
75 | },
76 | ]}
77 | ```
78 | * **maxTimeslots** (`Number`): The maximum number of timeslots a user can select. Default value is **1**.
79 |
80 | * **renderDays** (`Object`): Days of the week to be rendered in the calendar. As an example, if we wanted to remove weekends, all we have to do is pass:
81 | ```js
82 | let ignoreWeekends = {
83 | 'saturdays': false,
84 | 'sundays': false,
85 | };
86 | ```
87 | By default, all week days are displayed.
88 |
89 | * **startDateInputProps** (`Object`): Few props to modify how the input for `startDate` will behave. Only allows `name` and `class`.
90 | ```js
91 | //sample
92 | let startDateInputProps = {
93 | class: 'some-random-class',
94 | name: 'my-start-date-input-name',
95 | };
96 | ```
97 | * **endDateInputProps** (`Object`): Same idea as **startDateInputProps** but instead takes effect in the `endDate` input.
98 |
99 | * **onSelectTimeslot** (`Function`): A callback which takes as parameters `selectedTimeslots (array)` and `lastSelectedTimeslot (object)`. Every timeslot object contains `startDate` and `endDate`, both of which are MomentJS objects.
100 | ```js
101 | let onSelectTimeslot = (allTimeslots, lastSelectedTimeslot) => {
102 | /**
103 | * All timeslot objects include `startDate` and `endDate`.
104 |
105 | * It is important to note that if timelots provided contain a single
106 | * value (e.g: timeslots = [['8'], ['9', '10']) then only `startDate` is filled up with
107 | * the desired information.
108 | */
109 | console.log(lastSelectedTimeslot.startDate); // MomentJS object.
110 |
111 | }
112 | ```
113 | ### Development
114 | We're open to any help and support, so feel free to open up a PR if you happen to have a great idea! Also, feel free to take up any issues (if available).
115 |
116 | Feel free to download the project and make up some changes! Dev. environment is run with `npm run dev`.
117 |
--------------------------------------------------------------------------------
/__tests__/Calendar.react-test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import sinon from 'sinon';
4 | import moment from 'moment';
5 | import {
6 | mount,
7 | shallow,
8 | } from 'enzyme';
9 | import Calendar from '../src/js/components/calendar';
10 | import Timeslot from '../src/js/components/timeslot';
11 | import { DEFAULT_TIMESLOTS } from '../src/js/constants/day';
12 |
13 | describe('Render tests', () => {
14 | test('Renders Correctly.', () => {
15 | const tree = renderer.create(
16 |
20 | )
21 | .toJSON();
22 |
23 | expect(tree).toMatchSnapshot();
24 | });
25 |
26 | test('Expect timeslot selection to function with min props', () => {
27 | const component = mount(
28 |
32 | );
33 |
34 | const timeslot = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').first();
35 | timeslot.simulate('click');
36 |
37 | expect(component.state().selectedTimeslots).toHaveLength(1);
38 | });
39 |
40 | test('Expects a maximum of 2 selected timeslots', () => {
41 | const component = mount(
42 |
47 | );
48 |
49 | const timeslots = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').slice(0,5);
50 | timeslots.forEach((timeslot) => {
51 | timeslot.simulate('click');
52 | });
53 |
54 | expect(component.state().selectedTimeslots).toHaveLength(2);
55 | });
56 |
57 | test('Expects onSelectTimeslot to be called as many times as timeslots are clicked', () => {
58 | const onSelectTimeslot = sinon.spy();
59 | const component = mount(
60 |
65 | );
66 |
67 | const timeslots = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').slice(0,5);
68 | let clickCount = 0;
69 | timeslots.forEach((timeslot) => {
70 | timeslot.simulate('click');
71 | clickCount++;
72 | });
73 |
74 | expect(onSelectTimeslot).toHaveProperty('callCount', clickCount);
75 | });
76 |
77 | test('Expects 2 input elements after clicking a timeslot with min props.', () => {
78 | const component = mount(
79 |
83 | );
84 |
85 | const timeslot = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').first();
86 | timeslot.simulate('click');
87 |
88 | const inputs = component.findWhere(n => n.prop('name') == 'tsc-startDate' || n.prop('name') == 'tsc-endDate');
89 |
90 | expect(inputs).toHaveLength(2);
91 | });
92 |
93 | test('Expects 2 input elements with 1 custom name after clicking a timeslot', () => {
94 | const component = mount(
95 |
102 | );
103 |
104 | const timeslot = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').first();
105 | timeslot.simulate('click');
106 |
107 | const inputs = component.findWhere(n => n.prop('name') == 'custom-startDate' || n.prop('name') == 'tsc-endDate');
108 |
109 | expect(inputs).toHaveLength(2);
110 | });
111 |
112 | test('Expects 4 input elements after clicking multiple timeslots', () => {
113 | const component = mount(
114 |
119 | );
120 |
121 | const timeslots = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').slice(0,5);
122 | timeslots.forEach((timeslot) => {
123 | timeslot.simulate('click');
124 | });
125 |
126 | const inputs = component.findWhere(n => n.prop('name') == 'tsc-startDate[]' || n.prop('name') == 'tsc-endDate[]');
127 |
128 | expect(inputs).toHaveLength(4);
129 | });
130 |
131 | test('Expects 3 disabled timeslots based on props sent.', () => {
132 | const component = mount(
133 |
154 | );
155 |
156 | const disabledTimeslots = component.findWhere(timeslot => timeslot.prop('status') == 'DISABLED');
157 |
158 | expect(disabledTimeslots).toHaveLength(3);
159 | });
160 | });
161 |
--------------------------------------------------------------------------------
/__tests__/Day.react-test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import sinon from 'sinon';
4 | import moment from 'moment';
5 | import {
6 | shallow,
7 | mount,
8 | } from 'enzyme';
9 | import Day from '../src/js/components/day';
10 | import Timeslot from '../src/js/components/timeslot';
11 | import {
12 | DEFAULT_TIMESLOTS,
13 | DEFAULT_TIMESLOT_FORMAT,
14 | DEFAULT_TIMESLOT_SHOW_FORMAT,
15 | } from '../src/js/constants/day';
16 |
17 | describe('Render tests', () => {
18 | test('Renders Correctly with min props.', () => {
19 | const onClickSpy = sinon.spy();
20 | const tree = renderer.create(
21 |
33 | )
34 | .toJSON();
35 |
36 | expect(tree).toMatchSnapshot();
37 | });
38 |
39 | test('Renders Correctly if timeslots is an array of strings.', () => {
40 | const onClickSpy = sinon.spy();
41 | const timeslots = [
42 | '0',
43 | '1',
44 | '2',
45 | ];
46 |
47 | const tree = renderer.create(
48 |
60 | )
61 | .toJSON();
62 |
63 | expect(tree).toMatchSnapshot();
64 | });
65 |
66 | test('Renders Correctly if timeslots is an array of array.', () => {
67 | const onClickSpy = sinon.spy();
68 | const timeslots = [
69 | ['0', '1'],
70 | ['1', '2'],
71 | '3',
72 | ];
73 |
74 | const tree = renderer.create(
75 |
87 | )
88 | .toJSON();
89 |
90 | expect(tree).toMatchSnapshot();
91 | });
92 | });
93 |
94 | describe('Functionality tests', () => {
95 | test('Uses renderTitle function when provided', () => {
96 | const onClickSpy = sinon.spy();
97 | const renderTitleSpy = sinon.spy();
98 | const component = mount(
99 |
112 | );
113 |
114 | expect(renderTitleSpy).toHaveProperty('callCount', 1);
115 | });
116 |
117 | test('Timeslot click triggers onTimeslotClick', () => {
118 | const onClickSpy = sinon.spy();
119 | const component = mount(
120 |
132 | );
133 |
134 | const timeslot = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').first();
135 | timeslot.simulate('click');
136 |
137 | expect(onClickSpy).toHaveProperty('callCount', 1);
138 | });
139 |
140 | test('Renders 24 timeslots by default', () => {
141 | const onClickSpy = sinon.spy();
142 | const component = shallow(
143 |
155 | );
156 |
157 | const timeslot = component.find(Timeslot);
158 |
159 | expect(timeslot).toHaveLength(24);
160 | });
161 |
162 | test('Renders only the amount of timeslots provided', () => {
163 | const onClickSpy = sinon.spy();
164 | const timeslots = [
165 | ['0', '1'],
166 | ['1', '2'],
167 | ];
168 | const component = shallow(
169 |
181 | );
182 |
183 | const timeslot = component.find(Timeslot);
184 |
185 | expect(timeslot).toHaveLength(2);
186 | });
187 |
188 | test('Expect 12 disabled timeslots', () => {
189 | const onClickSpy = sinon.spy();
190 | const component = mount(
191 |
203 | );
204 |
205 | const timeslot = component.find('.tsc-timeslot--disabled');
206 |
207 | expect(timeslot).toHaveLength(12);
208 | });
209 |
210 | });
211 |
--------------------------------------------------------------------------------
/__tests__/Month.react-test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import sinon from 'sinon';
4 | import moment from 'moment';
5 | import Calendar from 'calendarjs';
6 | import {
7 | mount,
8 | } from 'enzyme';
9 | import Month from '../src/js/components/month';
10 | import helpers from '../src/js/util/helpers';
11 |
12 | import { RENDER_DAYS } from '../src/js/constants/week';
13 | import {
14 | DEFAULT_TIMESLOTS,
15 | DEFAULT_TIMESLOT_FORMAT,
16 | DEFAULT_TIMESLOT_SHOW_FORMAT,
17 | } from '../src/js/constants/day';
18 |
19 | const cal = new Calendar(2017, 4);
20 |
21 | describe('Render tests', () => {
22 | test('Renders Correctly with min props.', () => {
23 | const weeks = cal.generate();
24 | const tree = renderer.create(
25 |
38 | )
39 | .toJSON();
40 |
41 | expect(tree).toMatchSnapshot();
42 | });
43 |
44 | test('Renders Correctly with all props.', () => {
45 | const weeks = cal.generate();
46 | const onWeekOutOfMonth = sinon.spy();
47 | const tree = renderer.create(
48 |
62 | )
63 | .toJSON();
64 |
65 | expect(tree).toMatchSnapshot();
66 | });
67 | });
68 |
69 | describe('Functionality tests', () => {
70 | test('Current week contains currentDate', () => {
71 | const weeks = cal.generate();
72 | const currentDate = moment([2017, 3, 1]);
73 | const component = mount(
74 |
87 | );
88 |
89 | let weekIndex = null;
90 | weeks.some((week, index) => {
91 | let currentDateFound = week.some((day) => {
92 | return helpers.getMomentFromCalendarJSDateElement(day).format() === currentDate.format();
93 | });
94 |
95 | if (currentDateFound) {
96 | weekIndex = index;
97 | return true;
98 | }
99 | });
100 |
101 |
102 | expect(component.state().currentWeekIndex).toEqual(weekIndex);
103 | });
104 |
105 | test('onWeekOutOfMonth callback called if currentDate is start of the month and user tries to go back', () => {
106 | const weeks = cal.generate();
107 | const currentDate = moment([2017, 3, 1]).startOf('month');
108 | const onWeekOutOfMonth = sinon.spy();
109 | const component = mount(
110 |
124 | );
125 |
126 | component.find('.tsc-month__action-element--left').simulate('click');
127 | expect(onWeekOutOfMonth).toHaveProperty('callCount', 1);
128 | });
129 |
130 | test('onWeekOutOfMonth callback called if currentDate is end of the month and user tries to go next', () => {
131 | const weeks = cal.generate();
132 | const currentDate = moment([2017, 3, 1]).endOf('month');
133 | const onWeekOutOfMonth = sinon.spy();
134 | const component = mount(
135 |
149 | );
150 |
151 | component.find('.tsc-month__action-element--right').simulate('click');
152 | expect(onWeekOutOfMonth).toHaveProperty('callCount', 1);
153 | });
154 |
155 | test('Users can go to next week if available', () => {
156 | const weeks = cal.generate();
157 | const currentDate = moment([2017, 3, 1]).startOf('month');
158 | const component = mount(
159 |
172 | );
173 | const weekIndexBeforeClick = component.state().currentWeekIndex;
174 | component.find('.tsc-month__action-element--right').simulate('click');
175 | const weekIndexAfterClick = component.state().currentWeekIndex;
176 | expect(weekIndexAfterClick).toEqual(weekIndexBeforeClick + 1);
177 | });
178 |
179 | test('Users can go to prev week if available', () => {
180 | const weeks = cal.generate();
181 | const currentDate = moment([2017, 3, 1]).endOf('month');
182 | const component = mount(
183 |
196 | );
197 |
198 | const weekIndexBeforeClick = component.state().currentWeekIndex;
199 | component.find('.tsc-month__action-element--left').simulate('click');
200 | const weekIndexAfterClick = component.state().currentWeekIndex;
201 | expect(weekIndexAfterClick).toEqual(weekIndexBeforeClick - 1);
202 | });
203 |
204 | });
205 |
--------------------------------------------------------------------------------
/__tests__/Timeslot.react-test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import sinon from 'sinon';
4 | import Timeslot from '../src/js/components/timeslot';
5 | import {
6 | shallow,
7 | mount,
8 | } from 'enzyme';
9 |
10 | import {
11 | DEFAULT,
12 | SELECTED,
13 | DISABLED,
14 | } from '../src/js/constants/timeslot';
15 |
16 | import { DEFAULT_TIMESLOTS } from '../src/js/constants/day';
17 |
18 |
19 | it('Renders Correctly with All props.', () => {
20 | const onClickSpy = sinon.spy();
21 | const tree = renderer.create(
22 |
29 | )
30 | .toJSON();
31 |
32 | expect(tree).toMatchSnapshot();
33 | });
34 |
35 |
36 | it('Renders when customClassNames prop is not provided', () => {
37 | const onClickSpy = sinon.spy();
38 | const tree = renderer.create(
39 |
45 | )
46 | .toJSON();
47 |
48 | expect(tree).toMatchSnapshot();
49 | });
50 |
51 | it('Renders when customClassNames is null', () => {
52 | const onClickSpy = sinon.spy();
53 | const tree = renderer.create(
54 |
61 | )
62 | .toJSON();
63 |
64 | expect(tree).toMatchSnapshot();
65 | });
66 |
67 | it('Assumes default status if not provided', () => {
68 | const onClickSpy = sinon.spy();
69 | const timeslot = mount(
70 |
75 | );
76 |
77 | expect(timeslot.props().status).toEqual(DEFAULT);
78 | });
79 |
80 | it('Does not call parent onClick prop if status is Disabled', () => {
81 | const onClickSpy = sinon.spy();
82 | const timeslot = mount(
83 |
89 | );
90 |
91 | timeslot.simulate('click');
92 | expect(onClickSpy).toHaveProperty('callCount', 0);
93 | });
94 |
95 | it('Does call parent onClick prop if status is Default', () => {
96 | const onClickSpy = sinon.spy();
97 | const timeslot = mount(
98 |
104 | );
105 |
106 | timeslot.simulate('click');
107 | expect(onClickSpy).toHaveProperty('callCount', 1);
108 | });
109 |
110 | it('Does call parent onClick prop if status is Selected', () => {
111 | const onClickSpy = sinon.spy();
112 | const timeslot = mount(
113 |
119 | );
120 |
121 | timeslot.simulate('click');
122 | expect(onClickSpy).toHaveProperty('callCount', 1);
123 | });
124 |
125 | it('Has default tsc-timeslot class.', () => {
126 | const onClickSpy = sinon.spy();
127 | const timeslot = shallow(
128 |
134 | );
135 |
136 | expect(timeslot.hasClass('tsc-timeslot')).toEqual(true);
137 | });
138 |
139 | it('Adds selected class when Status prop is Selected', () => {
140 | const onClickSpy = sinon.spy();
141 | const timeslot = shallow(
142 |
148 | );
149 |
150 | expect(timeslot.hasClass('tsc-timeslot--selected')).toEqual(true);
151 | });
152 |
153 | it('Adds all classes in customClassNames when customClassNames is an object', () => {
154 | const onClickSpy = sinon.spy();
155 | const customClasses = {
156 | 'added-class': true,
157 | 'added-class-two': true,
158 | };
159 |
160 | const timeslot = shallow(
161 |
168 | );
169 |
170 | expect(timeslot.is('.added-class.added-class-two')).toEqual(true);
171 | });
172 |
173 | it('Does not adds classes with value = false in customClassNames', () => {
174 | const onClickSpy = sinon.spy();
175 | const customClasses = {
176 | 'not-added-class': false,
177 | };
178 |
179 | const timeslot = shallow(
180 |
187 | );
188 |
189 | expect(timeslot.is('.not-added-class')).toEqual(false);
190 | });
191 |
--------------------------------------------------------------------------------
/__tests__/Week.react-test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import sinon from 'sinon';
4 | import moment from 'moment';
5 | import Calendar from 'calendarjs';
6 | import {
7 | shallow,
8 | mount,
9 | } from 'enzyme';
10 |
11 | import Week from '../src/js/components/week';
12 | import Day from '../src/js/components/day';
13 | import Timeslot from '../src/js/components/timeslot';
14 |
15 | import { RENDER_DAYS } from '../src/js/constants/week';
16 | import {
17 | DEFAULT_TIMESLOTS,
18 | DEFAULT_TIMESLOT_FORMAT,
19 | DEFAULT_TIMESLOT_SHOW_FORMAT,
20 | } from '../src/js/constants/day';
21 |
22 | const cal = new Calendar(2017, 4);
23 |
24 | describe('Render tests', () => {
25 | test('Renders Correctly with min props.', () => {
26 | const onClickSpy = sinon.spy();
27 | const weeks = cal.generate();
28 |
29 | const tree = renderer.create(
30 |
43 | )
44 | .toJSON();
45 |
46 | expect(tree).toMatchSnapshot();
47 | });
48 |
49 | test('Expect to Render 3 Days', () => {
50 | const onClickSpy = sinon.spy();
51 | const weeks = cal.generate();
52 |
53 | const component = mount(
54 |
67 | );
68 |
69 | const day = component.find(Day);
70 |
71 | expect(day).toHaveLength(3);
72 | });
73 |
74 | test('Timeslot click triggers onTimeslotClick', () => {
75 | const onClickSpy = sinon.spy();
76 | const weeks = cal.generate();
77 |
78 | const component = mount(
79 |
92 | );
93 |
94 | const timeslot = component.find('.tsc-timeslot').not('.tsc-timeslot--disabled').first();
95 | timeslot.simulate('click');
96 |
97 | expect(onClickSpy).toHaveProperty('callCount', 1);
98 | });
99 |
100 | test('Week Renders only 3 Days when specifying days to render.', () => {
101 | const onClickSpy = sinon.spy();
102 | const weeks = cal.generate();
103 |
104 | const component = mount(
105 |
126 | );
127 |
128 | const days = component.find(Day);
129 | expect(days).toHaveLength(3);
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/__tests__/__snapshots__/Calendar.react-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render tests Renders Correctly. 1`] = `
4 |
7 |
10 |
14 | ‹
15 |
16 |
19 | April - 2017
20 |
21 |
25 | ›
26 |
27 |
28 |
31 |
34 |
38 | ‹
39 |
40 |
43 | Apr 23rd - Apr 29th
44 |
45 |
49 | ›
50 |
51 |
52 |
55 |
58 |
61 |
62 | Sunday (23)
63 |
64 |
65 |
69 | 12:00 AM - 1:00 AM
70 |
71 |
75 | 1:00 AM - 2:00 AM
76 |
77 |
81 | 2:00 AM - 3:00 AM
82 |
83 |
87 | 3:00 AM - 4:00 AM
88 |
89 |
93 | 4:00 AM - 5:00 AM
94 |
95 |
99 | 5:00 AM - 6:00 AM
100 |
101 |
105 | 6:00 AM - 7:00 AM
106 |
107 |
111 | 7:00 AM - 8:00 AM
112 |
113 |
117 | 8:00 AM - 9:00 AM
118 |
119 |
123 | 9:00 AM - 10:00 AM
124 |
125 |
129 | 10:00 AM - 11:00 AM
130 |
131 |
135 | 11:00 AM - 12:00 PM
136 |
137 |
141 | 12:00 PM - 1:00 PM
142 |
143 |
147 | 1:00 PM - 2:00 PM
148 |
149 |
153 | 2:00 PM - 3:00 PM
154 |
155 |
159 | 3:00 PM - 4:00 PM
160 |
161 |
165 | 4:00 PM - 5:00 PM
166 |
167 |
171 | 5:00 PM - 6:00 PM
172 |
173 |
177 | 6:00 PM - 7:00 PM
178 |
179 |
183 | 7:00 PM - 8:00 PM
184 |
185 |
189 | 8:00 PM - 9:00 PM
190 |
191 |
195 | 9:00 PM - 10:00 PM
196 |
197 |
201 | 10:00 PM - 11:00 PM
202 |
203 |
207 | 11:00 PM - 12:00 AM
208 |
209 |
210 |
213 |
216 |
217 | Monday (24)
218 |
219 |
220 |
224 | 12:00 AM - 1:00 AM
225 |
226 |
230 | 1:00 AM - 2:00 AM
231 |
232 |
236 | 2:00 AM - 3:00 AM
237 |
238 |
242 | 3:00 AM - 4:00 AM
243 |
244 |
248 | 4:00 AM - 5:00 AM
249 |
250 |
254 | 5:00 AM - 6:00 AM
255 |
256 |
260 | 6:00 AM - 7:00 AM
261 |
262 |
266 | 7:00 AM - 8:00 AM
267 |
268 |
272 | 8:00 AM - 9:00 AM
273 |
274 |
278 | 9:00 AM - 10:00 AM
279 |
280 |
284 | 10:00 AM - 11:00 AM
285 |
286 |
290 | 11:00 AM - 12:00 PM
291 |
292 |
296 | 12:00 PM - 1:00 PM
297 |
298 |
302 | 1:00 PM - 2:00 PM
303 |
304 |
308 | 2:00 PM - 3:00 PM
309 |
310 |
314 | 3:00 PM - 4:00 PM
315 |
316 |
320 | 4:00 PM - 5:00 PM
321 |
322 |
326 | 5:00 PM - 6:00 PM
327 |
328 |
332 | 6:00 PM - 7:00 PM
333 |
334 |
338 | 7:00 PM - 8:00 PM
339 |
340 |
344 | 8:00 PM - 9:00 PM
345 |
346 |
350 | 9:00 PM - 10:00 PM
351 |
352 |
356 | 10:00 PM - 11:00 PM
357 |
358 |
362 | 11:00 PM - 12:00 AM
363 |
364 |
365 |
368 |
371 |
372 | Tuesday (25)
373 |
374 |
375 |
379 | 12:00 AM - 1:00 AM
380 |
381 |
385 | 1:00 AM - 2:00 AM
386 |
387 |
391 | 2:00 AM - 3:00 AM
392 |
393 |
397 | 3:00 AM - 4:00 AM
398 |
399 |
403 | 4:00 AM - 5:00 AM
404 |
405 |
409 | 5:00 AM - 6:00 AM
410 |
411 |
415 | 6:00 AM - 7:00 AM
416 |
417 |
421 | 7:00 AM - 8:00 AM
422 |
423 |
427 | 8:00 AM - 9:00 AM
428 |
429 |
433 | 9:00 AM - 10:00 AM
434 |
435 |
439 | 10:00 AM - 11:00 AM
440 |
441 |
445 | 11:00 AM - 12:00 PM
446 |
447 |
451 | 12:00 PM - 1:00 PM
452 |
453 |
457 | 1:00 PM - 2:00 PM
458 |
459 |
463 | 2:00 PM - 3:00 PM
464 |
465 |
469 | 3:00 PM - 4:00 PM
470 |
471 |
475 | 4:00 PM - 5:00 PM
476 |
477 |
481 | 5:00 PM - 6:00 PM
482 |
483 |
487 | 6:00 PM - 7:00 PM
488 |
489 |
493 | 7:00 PM - 8:00 PM
494 |
495 |
499 | 8:00 PM - 9:00 PM
500 |
501 |
505 | 9:00 PM - 10:00 PM
506 |
507 |
511 | 10:00 PM - 11:00 PM
512 |
513 |
517 | 11:00 PM - 12:00 AM
518 |
519 |
520 |
523 |
526 |
527 | Wednesday (26)
528 |
529 |
530 |
534 | 12:00 AM - 1:00 AM
535 |
536 |
540 | 1:00 AM - 2:00 AM
541 |
542 |
546 | 2:00 AM - 3:00 AM
547 |
548 |
552 | 3:00 AM - 4:00 AM
553 |
554 |
558 | 4:00 AM - 5:00 AM
559 |
560 |
564 | 5:00 AM - 6:00 AM
565 |
566 |
570 | 6:00 AM - 7:00 AM
571 |
572 |
576 | 7:00 AM - 8:00 AM
577 |
578 |
582 | 8:00 AM - 9:00 AM
583 |
584 |
588 | 9:00 AM - 10:00 AM
589 |
590 |
594 | 10:00 AM - 11:00 AM
595 |
596 |
600 | 11:00 AM - 12:00 PM
601 |
602 |
606 | 12:00 PM - 1:00 PM
607 |
608 |
612 | 1:00 PM - 2:00 PM
613 |
614 |
618 | 2:00 PM - 3:00 PM
619 |
620 |
624 | 3:00 PM - 4:00 PM
625 |
626 |
630 | 4:00 PM - 5:00 PM
631 |
632 |
636 | 5:00 PM - 6:00 PM
637 |
638 |
642 | 6:00 PM - 7:00 PM
643 |
644 |
648 | 7:00 PM - 8:00 PM
649 |
650 |
654 | 8:00 PM - 9:00 PM
655 |
656 |
660 | 9:00 PM - 10:00 PM
661 |
662 |
666 | 10:00 PM - 11:00 PM
667 |
668 |
672 | 11:00 PM - 12:00 AM
673 |
674 |
675 |
678 |
681 |
682 | Thursday (27)
683 |
684 |
685 |
689 | 12:00 AM - 1:00 AM
690 |
691 |
695 | 1:00 AM - 2:00 AM
696 |
697 |
701 | 2:00 AM - 3:00 AM
702 |
703 |
707 | 3:00 AM - 4:00 AM
708 |
709 |
713 | 4:00 AM - 5:00 AM
714 |
715 |
719 | 5:00 AM - 6:00 AM
720 |
721 |
725 | 6:00 AM - 7:00 AM
726 |
727 |
731 | 7:00 AM - 8:00 AM
732 |
733 |
737 | 8:00 AM - 9:00 AM
738 |
739 |
743 | 9:00 AM - 10:00 AM
744 |
745 |
749 | 10:00 AM - 11:00 AM
750 |
751 |
755 | 11:00 AM - 12:00 PM
756 |
757 |
761 | 12:00 PM - 1:00 PM
762 |
763 |
767 | 1:00 PM - 2:00 PM
768 |
769 |
773 | 2:00 PM - 3:00 PM
774 |
775 |
779 | 3:00 PM - 4:00 PM
780 |
781 |
785 | 4:00 PM - 5:00 PM
786 |
787 |
791 | 5:00 PM - 6:00 PM
792 |
793 |
797 | 6:00 PM - 7:00 PM
798 |
799 |
803 | 7:00 PM - 8:00 PM
804 |
805 |
809 | 8:00 PM - 9:00 PM
810 |
811 |
815 | 9:00 PM - 10:00 PM
816 |
817 |
821 | 10:00 PM - 11:00 PM
822 |
823 |
827 | 11:00 PM - 12:00 AM
828 |
829 |
830 |
833 |
836 |
837 | Friday (28)
838 |
839 |
840 |
844 | 12:00 AM - 1:00 AM
845 |
846 |
850 | 1:00 AM - 2:00 AM
851 |
852 |
856 | 2:00 AM - 3:00 AM
857 |
858 |
862 | 3:00 AM - 4:00 AM
863 |
864 |
868 | 4:00 AM - 5:00 AM
869 |
870 |
874 | 5:00 AM - 6:00 AM
875 |
876 |
880 | 6:00 AM - 7:00 AM
881 |
882 |
886 | 7:00 AM - 8:00 AM
887 |
888 |
892 | 8:00 AM - 9:00 AM
893 |
894 |
898 | 9:00 AM - 10:00 AM
899 |
900 |
904 | 10:00 AM - 11:00 AM
905 |
906 |
910 | 11:00 AM - 12:00 PM
911 |
912 |
916 | 12:00 PM - 1:00 PM
917 |
918 |
922 | 1:00 PM - 2:00 PM
923 |
924 |
928 | 2:00 PM - 3:00 PM
929 |
930 |
934 | 3:00 PM - 4:00 PM
935 |
936 |
940 | 4:00 PM - 5:00 PM
941 |
942 |
946 | 5:00 PM - 6:00 PM
947 |
948 |
952 | 6:00 PM - 7:00 PM
953 |
954 |
958 | 7:00 PM - 8:00 PM
959 |
960 |
964 | 8:00 PM - 9:00 PM
965 |
966 |
970 | 9:00 PM - 10:00 PM
971 |
972 |
976 | 10:00 PM - 11:00 PM
977 |
978 |
982 | 11:00 PM - 12:00 AM
983 |
984 |
985 |
988 |
991 |
992 | Saturday (29)
993 |
994 |
995 |
999 | 12:00 AM - 1:00 AM
1000 |
1001 |
1005 | 1:00 AM - 2:00 AM
1006 |
1007 |
1011 | 2:00 AM - 3:00 AM
1012 |
1013 |
1017 | 3:00 AM - 4:00 AM
1018 |
1019 |
1023 | 4:00 AM - 5:00 AM
1024 |
1025 |
1029 | 5:00 AM - 6:00 AM
1030 |
1031 |
1035 | 6:00 AM - 7:00 AM
1036 |
1037 |
1041 | 7:00 AM - 8:00 AM
1042 |
1043 |
1047 | 8:00 AM - 9:00 AM
1048 |
1049 |
1053 | 9:00 AM - 10:00 AM
1054 |
1055 |
1059 | 10:00 AM - 11:00 AM
1060 |
1061 |
1065 | 11:00 AM - 12:00 PM
1066 |
1067 |
1071 | 12:00 PM - 1:00 PM
1072 |
1073 |
1077 | 1:00 PM - 2:00 PM
1078 |
1079 |
1083 | 2:00 PM - 3:00 PM
1084 |
1085 |
1089 | 3:00 PM - 4:00 PM
1090 |
1091 |
1095 | 4:00 PM - 5:00 PM
1096 |
1097 |
1101 | 5:00 PM - 6:00 PM
1102 |
1103 |
1107 | 6:00 PM - 7:00 PM
1108 |
1109 |
1113 | 7:00 PM - 8:00 PM
1114 |
1115 |
1119 | 8:00 PM - 9:00 PM
1120 |
1121 |
1125 | 9:00 PM - 10:00 PM
1126 |
1127 |
1131 | 10:00 PM - 11:00 PM
1132 |
1133 |
1137 | 11:00 PM - 12:00 AM
1138 |
1139 |
1140 |
1141 |
1142 |
1143 | `;
1144 |
--------------------------------------------------------------------------------
/__tests__/__snapshots__/Day.react-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render tests Renders Correctly if timeslots is an array of array. 1`] = `
4 |
7 |
10 |
11 | Friday (28)
12 |
13 |
14 |
18 | 12:00 AM - 1:00 AM
19 |
20 |
24 | 1:00 AM - 2:00 AM
25 |
26 |
30 | 3:00 AM
31 |
32 |
33 | `;
34 |
35 | exports[`Render tests Renders Correctly if timeslots is an array of strings. 1`] = `
36 |
39 |
42 |
43 | Friday (28)
44 |
45 |
46 |
50 | 12:00 AM
51 |
52 |
56 | 1:00 AM
57 |
58 |
62 | 2:00 AM
63 |
64 |
65 | `;
66 |
67 | exports[`Render tests Renders Correctly with min props. 1`] = `
68 |
71 |
74 |
75 | Friday (28)
76 |
77 |
78 |
82 | 12:00 AM - 1:00 AM
83 |
84 |
88 | 1:00 AM - 2:00 AM
89 |
90 |
94 | 2:00 AM - 3:00 AM
95 |
96 |
100 | 3:00 AM - 4:00 AM
101 |
102 |
106 | 4:00 AM - 5:00 AM
107 |
108 |
112 | 5:00 AM - 6:00 AM
113 |
114 |
118 | 6:00 AM - 7:00 AM
119 |
120 |
124 | 7:00 AM - 8:00 AM
125 |
126 |
130 | 8:00 AM - 9:00 AM
131 |
132 |
136 | 9:00 AM - 10:00 AM
137 |
138 |
142 | 10:00 AM - 11:00 AM
143 |
144 |
148 | 11:00 AM - 12:00 PM
149 |
150 |
154 | 12:00 PM - 1:00 PM
155 |
156 |
160 | 1:00 PM - 2:00 PM
161 |
162 |
166 | 2:00 PM - 3:00 PM
167 |
168 |
172 | 3:00 PM - 4:00 PM
173 |
174 |
178 | 4:00 PM - 5:00 PM
179 |
180 |
184 | 5:00 PM - 6:00 PM
185 |
186 |
190 | 6:00 PM - 7:00 PM
191 |
192 |
196 | 7:00 PM - 8:00 PM
197 |
198 |
202 | 8:00 PM - 9:00 PM
203 |
204 |
208 | 9:00 PM - 10:00 PM
209 |
210 |
214 | 10:00 PM - 11:00 PM
215 |
216 |
220 | 11:00 PM - 12:00 AM
221 |
222 |
223 | `;
224 |
--------------------------------------------------------------------------------
/__tests__/__snapshots__/Month.react-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render tests Renders Correctly with all props. 1`] = `
4 |
7 |
10 |
14 | ‹
15 |
16 |
19 | Mar 26th - Apr 1st
20 |
21 |
25 | ›
26 |
27 |
28 |
31 |
34 |
37 |
38 | Sunday (26)
39 |
40 |
41 |
45 | 12:00 AM - 1:00 AM
46 |
47 |
51 | 1:00 AM - 2:00 AM
52 |
53 |
57 | 2:00 AM - 3:00 AM
58 |
59 |
63 | 3:00 AM - 4:00 AM
64 |
65 |
69 | 4:00 AM - 5:00 AM
70 |
71 |
75 | 5:00 AM - 6:00 AM
76 |
77 |
81 | 6:00 AM - 7:00 AM
82 |
83 |
87 | 7:00 AM - 8:00 AM
88 |
89 |
93 | 8:00 AM - 9:00 AM
94 |
95 |
99 | 9:00 AM - 10:00 AM
100 |
101 |
105 | 10:00 AM - 11:00 AM
106 |
107 |
111 | 11:00 AM - 12:00 PM
112 |
113 |
117 | 12:00 PM - 1:00 PM
118 |
119 |
123 | 1:00 PM - 2:00 PM
124 |
125 |
129 | 2:00 PM - 3:00 PM
130 |
131 |
135 | 3:00 PM - 4:00 PM
136 |
137 |
141 | 4:00 PM - 5:00 PM
142 |
143 |
147 | 5:00 PM - 6:00 PM
148 |
149 |
153 | 6:00 PM - 7:00 PM
154 |
155 |
159 | 7:00 PM - 8:00 PM
160 |
161 |
165 | 8:00 PM - 9:00 PM
166 |
167 |
171 | 9:00 PM - 10:00 PM
172 |
173 |
177 | 10:00 PM - 11:00 PM
178 |
179 |
183 | 11:00 PM - 12:00 AM
184 |
185 |
186 |
189 |
192 |
193 | Monday (27)
194 |
195 |
196 |
200 | 12:00 AM - 1:00 AM
201 |
202 |
206 | 1:00 AM - 2:00 AM
207 |
208 |
212 | 2:00 AM - 3:00 AM
213 |
214 |
218 | 3:00 AM - 4:00 AM
219 |
220 |
224 | 4:00 AM - 5:00 AM
225 |
226 |
230 | 5:00 AM - 6:00 AM
231 |
232 |
236 | 6:00 AM - 7:00 AM
237 |
238 |
242 | 7:00 AM - 8:00 AM
243 |
244 |
248 | 8:00 AM - 9:00 AM
249 |
250 |
254 | 9:00 AM - 10:00 AM
255 |
256 |
260 | 10:00 AM - 11:00 AM
261 |
262 |
266 | 11:00 AM - 12:00 PM
267 |
268 |
272 | 12:00 PM - 1:00 PM
273 |
274 |
278 | 1:00 PM - 2:00 PM
279 |
280 |
284 | 2:00 PM - 3:00 PM
285 |
286 |
290 | 3:00 PM - 4:00 PM
291 |
292 |
296 | 4:00 PM - 5:00 PM
297 |
298 |
302 | 5:00 PM - 6:00 PM
303 |
304 |
308 | 6:00 PM - 7:00 PM
309 |
310 |
314 | 7:00 PM - 8:00 PM
315 |
316 |
320 | 8:00 PM - 9:00 PM
321 |
322 |
326 | 9:00 PM - 10:00 PM
327 |
328 |
332 | 10:00 PM - 11:00 PM
333 |
334 |
338 | 11:00 PM - 12:00 AM
339 |
340 |
341 |
344 |
347 |
348 | Tuesday (28)
349 |
350 |
351 |
355 | 12:00 AM - 1:00 AM
356 |
357 |
361 | 1:00 AM - 2:00 AM
362 |
363 |
367 | 2:00 AM - 3:00 AM
368 |
369 |
373 | 3:00 AM - 4:00 AM
374 |
375 |
379 | 4:00 AM - 5:00 AM
380 |
381 |
385 | 5:00 AM - 6:00 AM
386 |
387 |
391 | 6:00 AM - 7:00 AM
392 |
393 |
397 | 7:00 AM - 8:00 AM
398 |
399 |
403 | 8:00 AM - 9:00 AM
404 |
405 |
409 | 9:00 AM - 10:00 AM
410 |
411 |
415 | 10:00 AM - 11:00 AM
416 |
417 |
421 | 11:00 AM - 12:00 PM
422 |
423 |
427 | 12:00 PM - 1:00 PM
428 |
429 |
433 | 1:00 PM - 2:00 PM
434 |
435 |
439 | 2:00 PM - 3:00 PM
440 |
441 |
445 | 3:00 PM - 4:00 PM
446 |
447 |
451 | 4:00 PM - 5:00 PM
452 |
453 |
457 | 5:00 PM - 6:00 PM
458 |
459 |
463 | 6:00 PM - 7:00 PM
464 |
465 |
469 | 7:00 PM - 8:00 PM
470 |
471 |
475 | 8:00 PM - 9:00 PM
476 |
477 |
481 | 9:00 PM - 10:00 PM
482 |
483 |
487 | 10:00 PM - 11:00 PM
488 |
489 |
493 | 11:00 PM - 12:00 AM
494 |
495 |
496 |
499 |
502 |
503 | Wednesday (29)
504 |
505 |
506 |
510 | 12:00 AM - 1:00 AM
511 |
512 |
516 | 1:00 AM - 2:00 AM
517 |
518 |
522 | 2:00 AM - 3:00 AM
523 |
524 |
528 | 3:00 AM - 4:00 AM
529 |
530 |
534 | 4:00 AM - 5:00 AM
535 |
536 |
540 | 5:00 AM - 6:00 AM
541 |
542 |
546 | 6:00 AM - 7:00 AM
547 |
548 |
552 | 7:00 AM - 8:00 AM
553 |
554 |
558 | 8:00 AM - 9:00 AM
559 |
560 |
564 | 9:00 AM - 10:00 AM
565 |
566 |
570 | 10:00 AM - 11:00 AM
571 |
572 |
576 | 11:00 AM - 12:00 PM
577 |
578 |
582 | 12:00 PM - 1:00 PM
583 |
584 |
588 | 1:00 PM - 2:00 PM
589 |
590 |
594 | 2:00 PM - 3:00 PM
595 |
596 |
600 | 3:00 PM - 4:00 PM
601 |
602 |
606 | 4:00 PM - 5:00 PM
607 |
608 |
612 | 5:00 PM - 6:00 PM
613 |
614 |
618 | 6:00 PM - 7:00 PM
619 |
620 |
624 | 7:00 PM - 8:00 PM
625 |
626 |
630 | 8:00 PM - 9:00 PM
631 |
632 |
636 | 9:00 PM - 10:00 PM
637 |
638 |
642 | 10:00 PM - 11:00 PM
643 |
644 |
648 | 11:00 PM - 12:00 AM
649 |
650 |
651 |
654 |
657 |
658 | Thursday (30)
659 |
660 |
661 |
665 | 12:00 AM - 1:00 AM
666 |
667 |
671 | 1:00 AM - 2:00 AM
672 |
673 |
677 | 2:00 AM - 3:00 AM
678 |
679 |
683 | 3:00 AM - 4:00 AM
684 |
685 |
689 | 4:00 AM - 5:00 AM
690 |
691 |
695 | 5:00 AM - 6:00 AM
696 |
697 |
701 | 6:00 AM - 7:00 AM
702 |
703 |
707 | 7:00 AM - 8:00 AM
708 |
709 |
713 | 8:00 AM - 9:00 AM
714 |
715 |
719 | 9:00 AM - 10:00 AM
720 |
721 |
725 | 10:00 AM - 11:00 AM
726 |
727 |
731 | 11:00 AM - 12:00 PM
732 |
733 |
737 | 12:00 PM - 1:00 PM
738 |
739 |
743 | 1:00 PM - 2:00 PM
744 |
745 |
749 | 2:00 PM - 3:00 PM
750 |
751 |
755 | 3:00 PM - 4:00 PM
756 |
757 |
761 | 4:00 PM - 5:00 PM
762 |
763 |
767 | 5:00 PM - 6:00 PM
768 |
769 |
773 | 6:00 PM - 7:00 PM
774 |
775 |
779 | 7:00 PM - 8:00 PM
780 |
781 |
785 | 8:00 PM - 9:00 PM
786 |
787 |
791 | 9:00 PM - 10:00 PM
792 |
793 |
797 | 10:00 PM - 11:00 PM
798 |
799 |
803 | 11:00 PM - 12:00 AM
804 |
805 |
806 |
809 |
812 |
813 | Friday (31)
814 |
815 |
816 |
820 | 12:00 AM - 1:00 AM
821 |
822 |
826 | 1:00 AM - 2:00 AM
827 |
828 |
832 | 2:00 AM - 3:00 AM
833 |
834 |
838 | 3:00 AM - 4:00 AM
839 |
840 |
844 | 4:00 AM - 5:00 AM
845 |
846 |
850 | 5:00 AM - 6:00 AM
851 |
852 |
856 | 6:00 AM - 7:00 AM
857 |
858 |
862 | 7:00 AM - 8:00 AM
863 |
864 |
868 | 8:00 AM - 9:00 AM
869 |
870 |
874 | 9:00 AM - 10:00 AM
875 |
876 |
880 | 10:00 AM - 11:00 AM
881 |
882 |
886 | 11:00 AM - 12:00 PM
887 |
888 |
892 | 12:00 PM - 1:00 PM
893 |
894 |
898 | 1:00 PM - 2:00 PM
899 |
900 |
904 | 2:00 PM - 3:00 PM
905 |
906 |
910 | 3:00 PM - 4:00 PM
911 |
912 |
916 | 4:00 PM - 5:00 PM
917 |
918 |
922 | 5:00 PM - 6:00 PM
923 |
924 |
928 | 6:00 PM - 7:00 PM
929 |
930 |
934 | 7:00 PM - 8:00 PM
935 |
936 |
940 | 8:00 PM - 9:00 PM
941 |
942 |
946 | 9:00 PM - 10:00 PM
947 |
948 |
952 | 10:00 PM - 11:00 PM
953 |
954 |
958 | 11:00 PM - 12:00 AM
959 |
960 |
961 |
964 |
967 |
968 | Saturday (1)
969 |
970 |
971 |
975 | 12:00 AM - 1:00 AM
976 |
977 |
981 | 1:00 AM - 2:00 AM
982 |
983 |
987 | 2:00 AM - 3:00 AM
988 |
989 |
993 | 3:00 AM - 4:00 AM
994 |
995 |
999 | 4:00 AM - 5:00 AM
1000 |
1001 |
1005 | 5:00 AM - 6:00 AM
1006 |
1007 |
1011 | 6:00 AM - 7:00 AM
1012 |
1013 |
1017 | 7:00 AM - 8:00 AM
1018 |
1019 |
1023 | 8:00 AM - 9:00 AM
1024 |
1025 |
1029 | 9:00 AM - 10:00 AM
1030 |
1031 |
1035 | 10:00 AM - 11:00 AM
1036 |
1037 |
1041 | 11:00 AM - 12:00 PM
1042 |
1043 |
1047 | 12:00 PM - 1:00 PM
1048 |
1049 |
1053 | 1:00 PM - 2:00 PM
1054 |
1055 |
1059 | 2:00 PM - 3:00 PM
1060 |
1061 |
1065 | 3:00 PM - 4:00 PM
1066 |
1067 |
1071 | 4:00 PM - 5:00 PM
1072 |
1073 |
1077 | 5:00 PM - 6:00 PM
1078 |
1079 |
1083 | 6:00 PM - 7:00 PM
1084 |
1085 |
1089 | 7:00 PM - 8:00 PM
1090 |
1091 |
1095 | 8:00 PM - 9:00 PM
1096 |
1097 |
1101 | 9:00 PM - 10:00 PM
1102 |
1103 |
1107 | 10:00 PM - 11:00 PM
1108 |
1109 |
1113 | 11:00 PM - 12:00 AM
1114 |
1115 |
1116 |
1117 |
1118 | `;
1119 |
1120 | exports[`Render tests Renders Correctly with min props. 1`] = `
1121 |
1124 |
1127 |
1131 | ‹
1132 |
1133 |
1136 | Mar 26th - Apr 1st
1137 |
1138 |
1142 | ›
1143 |
1144 |
1145 |
1148 |
1151 |
1154 |
1155 | Sunday (26)
1156 |
1157 |
1158 |
1162 | 12:00 AM - 1:00 AM
1163 |
1164 |
1168 | 1:00 AM - 2:00 AM
1169 |
1170 |
1174 | 2:00 AM - 3:00 AM
1175 |
1176 |
1180 | 3:00 AM - 4:00 AM
1181 |
1182 |
1186 | 4:00 AM - 5:00 AM
1187 |
1188 |
1192 | 5:00 AM - 6:00 AM
1193 |
1194 |
1198 | 6:00 AM - 7:00 AM
1199 |
1200 |
1204 | 7:00 AM - 8:00 AM
1205 |
1206 |
1210 | 8:00 AM - 9:00 AM
1211 |
1212 |
1216 | 9:00 AM - 10:00 AM
1217 |
1218 |
1222 | 10:00 AM - 11:00 AM
1223 |
1224 |
1228 | 11:00 AM - 12:00 PM
1229 |
1230 |
1234 | 12:00 PM - 1:00 PM
1235 |
1236 |
1240 | 1:00 PM - 2:00 PM
1241 |
1242 |
1246 | 2:00 PM - 3:00 PM
1247 |
1248 |
1252 | 3:00 PM - 4:00 PM
1253 |
1254 |
1258 | 4:00 PM - 5:00 PM
1259 |
1260 |
1264 | 5:00 PM - 6:00 PM
1265 |
1266 |
1270 | 6:00 PM - 7:00 PM
1271 |
1272 |
1276 | 7:00 PM - 8:00 PM
1277 |
1278 |
1282 | 8:00 PM - 9:00 PM
1283 |
1284 |
1288 | 9:00 PM - 10:00 PM
1289 |
1290 |
1294 | 10:00 PM - 11:00 PM
1295 |
1296 |
1300 | 11:00 PM - 12:00 AM
1301 |
1302 |
1303 |
1306 |
1309 |
1310 | Monday (27)
1311 |
1312 |
1313 |
1317 | 12:00 AM - 1:00 AM
1318 |
1319 |
1323 | 1:00 AM - 2:00 AM
1324 |
1325 |
1329 | 2:00 AM - 3:00 AM
1330 |
1331 |
1335 | 3:00 AM - 4:00 AM
1336 |
1337 |
1341 | 4:00 AM - 5:00 AM
1342 |
1343 |
1347 | 5:00 AM - 6:00 AM
1348 |
1349 |
1353 | 6:00 AM - 7:00 AM
1354 |
1355 |
1359 | 7:00 AM - 8:00 AM
1360 |
1361 |
1365 | 8:00 AM - 9:00 AM
1366 |
1367 |
1371 | 9:00 AM - 10:00 AM
1372 |
1373 |
1377 | 10:00 AM - 11:00 AM
1378 |
1379 |
1383 | 11:00 AM - 12:00 PM
1384 |
1385 |
1389 | 12:00 PM - 1:00 PM
1390 |
1391 |
1395 | 1:00 PM - 2:00 PM
1396 |
1397 |
1401 | 2:00 PM - 3:00 PM
1402 |
1403 |
1407 | 3:00 PM - 4:00 PM
1408 |
1409 |
1413 | 4:00 PM - 5:00 PM
1414 |
1415 |
1419 | 5:00 PM - 6:00 PM
1420 |
1421 |
1425 | 6:00 PM - 7:00 PM
1426 |
1427 |
1431 | 7:00 PM - 8:00 PM
1432 |
1433 |
1437 | 8:00 PM - 9:00 PM
1438 |
1439 |
1443 | 9:00 PM - 10:00 PM
1444 |
1445 |
1449 | 10:00 PM - 11:00 PM
1450 |
1451 |
1455 | 11:00 PM - 12:00 AM
1456 |
1457 |
1458 |
1461 |
1464 |
1465 | Tuesday (28)
1466 |
1467 |
1468 |
1472 | 12:00 AM - 1:00 AM
1473 |
1474 |
1478 | 1:00 AM - 2:00 AM
1479 |
1480 |
1484 | 2:00 AM - 3:00 AM
1485 |
1486 |
1490 | 3:00 AM - 4:00 AM
1491 |
1492 |
1496 | 4:00 AM - 5:00 AM
1497 |
1498 |
1502 | 5:00 AM - 6:00 AM
1503 |
1504 |
1508 | 6:00 AM - 7:00 AM
1509 |
1510 |
1514 | 7:00 AM - 8:00 AM
1515 |
1516 |
1520 | 8:00 AM - 9:00 AM
1521 |
1522 |
1526 | 9:00 AM - 10:00 AM
1527 |
1528 |
1532 | 10:00 AM - 11:00 AM
1533 |
1534 |
1538 | 11:00 AM - 12:00 PM
1539 |
1540 |
1544 | 12:00 PM - 1:00 PM
1545 |
1546 |
1550 | 1:00 PM - 2:00 PM
1551 |
1552 |
1556 | 2:00 PM - 3:00 PM
1557 |
1558 |
1562 | 3:00 PM - 4:00 PM
1563 |
1564 |
1568 | 4:00 PM - 5:00 PM
1569 |
1570 |
1574 | 5:00 PM - 6:00 PM
1575 |
1576 |
1580 | 6:00 PM - 7:00 PM
1581 |
1582 |
1586 | 7:00 PM - 8:00 PM
1587 |
1588 |
1592 | 8:00 PM - 9:00 PM
1593 |
1594 |
1598 | 9:00 PM - 10:00 PM
1599 |
1600 |
1604 | 10:00 PM - 11:00 PM
1605 |
1606 |
1610 | 11:00 PM - 12:00 AM
1611 |
1612 |
1613 |
1616 |
1619 |
1620 | Wednesday (29)
1621 |
1622 |
1623 |
1627 | 12:00 AM - 1:00 AM
1628 |
1629 |
1633 | 1:00 AM - 2:00 AM
1634 |
1635 |
1639 | 2:00 AM - 3:00 AM
1640 |
1641 |
1645 | 3:00 AM - 4:00 AM
1646 |
1647 |
1651 | 4:00 AM - 5:00 AM
1652 |
1653 |
1657 | 5:00 AM - 6:00 AM
1658 |
1659 |
1663 | 6:00 AM - 7:00 AM
1664 |
1665 |
1669 | 7:00 AM - 8:00 AM
1670 |
1671 |
1675 | 8:00 AM - 9:00 AM
1676 |
1677 |
1681 | 9:00 AM - 10:00 AM
1682 |
1683 |
1687 | 10:00 AM - 11:00 AM
1688 |
1689 |
1693 | 11:00 AM - 12:00 PM
1694 |
1695 |
1699 | 12:00 PM - 1:00 PM
1700 |
1701 |
1705 | 1:00 PM - 2:00 PM
1706 |
1707 |
1711 | 2:00 PM - 3:00 PM
1712 |
1713 |
1717 | 3:00 PM - 4:00 PM
1718 |
1719 |
1723 | 4:00 PM - 5:00 PM
1724 |
1725 |
1729 | 5:00 PM - 6:00 PM
1730 |
1731 |
1735 | 6:00 PM - 7:00 PM
1736 |
1737 |
1741 | 7:00 PM - 8:00 PM
1742 |
1743 |
1747 | 8:00 PM - 9:00 PM
1748 |
1749 |
1753 | 9:00 PM - 10:00 PM
1754 |
1755 |
1759 | 10:00 PM - 11:00 PM
1760 |
1761 |
1765 | 11:00 PM - 12:00 AM
1766 |
1767 |
1768 |
1771 |
1774 |
1775 | Thursday (30)
1776 |
1777 |
1778 |
1782 | 12:00 AM - 1:00 AM
1783 |
1784 |
1788 | 1:00 AM - 2:00 AM
1789 |
1790 |
1794 | 2:00 AM - 3:00 AM
1795 |
1796 |
1800 | 3:00 AM - 4:00 AM
1801 |
1802 |
1806 | 4:00 AM - 5:00 AM
1807 |
1808 |
1812 | 5:00 AM - 6:00 AM
1813 |
1814 |
1818 | 6:00 AM - 7:00 AM
1819 |
1820 |
1824 | 7:00 AM - 8:00 AM
1825 |
1826 |
1830 | 8:00 AM - 9:00 AM
1831 |
1832 |
1836 | 9:00 AM - 10:00 AM
1837 |
1838 |
1842 | 10:00 AM - 11:00 AM
1843 |
1844 |
1848 | 11:00 AM - 12:00 PM
1849 |
1850 |
1854 | 12:00 PM - 1:00 PM
1855 |
1856 |
1860 | 1:00 PM - 2:00 PM
1861 |
1862 |
1866 | 2:00 PM - 3:00 PM
1867 |
1868 |
1872 | 3:00 PM - 4:00 PM
1873 |
1874 |
1878 | 4:00 PM - 5:00 PM
1879 |
1880 |
1884 | 5:00 PM - 6:00 PM
1885 |
1886 |
1890 | 6:00 PM - 7:00 PM
1891 |
1892 |
1896 | 7:00 PM - 8:00 PM
1897 |
1898 |
1902 | 8:00 PM - 9:00 PM
1903 |
1904 |
1908 | 9:00 PM - 10:00 PM
1909 |
1910 |
1914 | 10:00 PM - 11:00 PM
1915 |
1916 |
1920 | 11:00 PM - 12:00 AM
1921 |
1922 |
1923 |
1926 |
1929 |
1930 | Friday (31)
1931 |
1932 |
1933 |
1937 | 12:00 AM - 1:00 AM
1938 |
1939 |
1943 | 1:00 AM - 2:00 AM
1944 |
1945 |
1949 | 2:00 AM - 3:00 AM
1950 |
1951 |
1955 | 3:00 AM - 4:00 AM
1956 |
1957 |
1961 | 4:00 AM - 5:00 AM
1962 |
1963 |
1967 | 5:00 AM - 6:00 AM
1968 |
1969 |
1973 | 6:00 AM - 7:00 AM
1974 |
1975 |
1979 | 7:00 AM - 8:00 AM
1980 |
1981 |
1985 | 8:00 AM - 9:00 AM
1986 |
1987 |
1991 | 9:00 AM - 10:00 AM
1992 |
1993 |
1997 | 10:00 AM - 11:00 AM
1998 |
1999 |
2003 | 11:00 AM - 12:00 PM
2004 |
2005 |
2009 | 12:00 PM - 1:00 PM
2010 |
2011 |
2015 | 1:00 PM - 2:00 PM
2016 |
2017 |
2021 | 2:00 PM - 3:00 PM
2022 |
2023 |
2027 | 3:00 PM - 4:00 PM
2028 |
2029 |
2033 | 4:00 PM - 5:00 PM
2034 |
2035 |
2039 | 5:00 PM - 6:00 PM
2040 |
2041 |
2045 | 6:00 PM - 7:00 PM
2046 |
2047 |
2051 | 7:00 PM - 8:00 PM
2052 |
2053 |
2057 | 8:00 PM - 9:00 PM
2058 |
2059 |
2063 | 9:00 PM - 10:00 PM
2064 |
2065 |
2069 | 10:00 PM - 11:00 PM
2070 |
2071 |
2075 | 11:00 PM - 12:00 AM
2076 |
2077 |
2078 |
2081 |
2084 |
2085 | Saturday (1)
2086 |
2087 |
2088 |
2092 | 12:00 AM - 1:00 AM
2093 |
2094 |
2098 | 1:00 AM - 2:00 AM
2099 |
2100 |
2104 | 2:00 AM - 3:00 AM
2105 |
2106 |
2110 | 3:00 AM - 4:00 AM
2111 |
2112 |
2116 | 4:00 AM - 5:00 AM
2117 |
2118 |
2122 | 5:00 AM - 6:00 AM
2123 |
2124 |
2128 | 6:00 AM - 7:00 AM
2129 |
2130 |
2134 | 7:00 AM - 8:00 AM
2135 |
2136 |
2140 | 8:00 AM - 9:00 AM
2141 |
2142 |
2146 | 9:00 AM - 10:00 AM
2147 |
2148 |
2152 | 10:00 AM - 11:00 AM
2153 |
2154 |
2158 | 11:00 AM - 12:00 PM
2159 |
2160 |
2164 | 12:00 PM - 1:00 PM
2165 |
2166 |
2170 | 1:00 PM - 2:00 PM
2171 |
2172 |
2176 | 2:00 PM - 3:00 PM
2177 |
2178 |
2182 | 3:00 PM - 4:00 PM
2183 |
2184 |
2188 | 4:00 PM - 5:00 PM
2189 |
2190 |
2194 | 5:00 PM - 6:00 PM
2195 |
2196 |
2200 | 6:00 PM - 7:00 PM
2201 |
2202 |
2206 | 7:00 PM - 8:00 PM
2207 |
2208 |
2212 | 8:00 PM - 9:00 PM
2213 |
2214 |
2218 | 9:00 PM - 10:00 PM
2219 |
2220 |
2224 | 10:00 PM - 11:00 PM
2225 |
2226 |
2230 | 11:00 PM - 12:00 AM
2231 |
2232 |
2233 |
2234 |
2235 | `;
2236 |
--------------------------------------------------------------------------------
/__tests__/__snapshots__/Timeslot.react-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Renders Correctly with All props. 1`] = `
4 |
8 | 1:00 PM - 2:00 AM
9 |
10 | `;
11 |
12 | exports[`Renders when customClassNames is null 1`] = `
13 |
17 | 1:00 PM - 2:00 AM
18 |
19 | `;
20 |
21 | exports[`Renders when customClassNames prop is not provided 1`] = `
22 |
26 | 1:00 PM - 2:00 AM
27 |
28 | `;
29 |
--------------------------------------------------------------------------------
/__tests__/__snapshots__/Week.react-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render tests Renders Correctly with min props. 1`] = `
4 |
7 |
10 |
13 |
14 | Sunday (26)
15 |
16 |
17 |
21 | 12:00 AM - 1:00 AM
22 |
23 |
27 | 1:00 AM - 2:00 AM
28 |
29 |
33 | 2:00 AM - 3:00 AM
34 |
35 |
39 | 3:00 AM - 4:00 AM
40 |
41 |
45 | 4:00 AM - 5:00 AM
46 |
47 |
51 | 5:00 AM - 6:00 AM
52 |
53 |
57 | 6:00 AM - 7:00 AM
58 |
59 |
63 | 7:00 AM - 8:00 AM
64 |
65 |
69 | 8:00 AM - 9:00 AM
70 |
71 |
75 | 9:00 AM - 10:00 AM
76 |
77 |
81 | 10:00 AM - 11:00 AM
82 |
83 |
87 | 11:00 AM - 12:00 PM
88 |
89 |
93 | 12:00 PM - 1:00 PM
94 |
95 |
99 | 1:00 PM - 2:00 PM
100 |
101 |
105 | 2:00 PM - 3:00 PM
106 |
107 |
111 | 3:00 PM - 4:00 PM
112 |
113 |
117 | 4:00 PM - 5:00 PM
118 |
119 |
123 | 5:00 PM - 6:00 PM
124 |
125 |
129 | 6:00 PM - 7:00 PM
130 |
131 |
135 | 7:00 PM - 8:00 PM
136 |
137 |
141 | 8:00 PM - 9:00 PM
142 |
143 |
147 | 9:00 PM - 10:00 PM
148 |
149 |
153 | 10:00 PM - 11:00 PM
154 |
155 |
159 | 11:00 PM - 12:00 AM
160 |
161 |
162 |
165 |
168 |
169 | Monday (27)
170 |
171 |
172 |
176 | 12:00 AM - 1:00 AM
177 |
178 |
182 | 1:00 AM - 2:00 AM
183 |
184 |
188 | 2:00 AM - 3:00 AM
189 |
190 |
194 | 3:00 AM - 4:00 AM
195 |
196 |
200 | 4:00 AM - 5:00 AM
201 |
202 |
206 | 5:00 AM - 6:00 AM
207 |
208 |
212 | 6:00 AM - 7:00 AM
213 |
214 |
218 | 7:00 AM - 8:00 AM
219 |
220 |
224 | 8:00 AM - 9:00 AM
225 |
226 |
230 | 9:00 AM - 10:00 AM
231 |
232 |
236 | 10:00 AM - 11:00 AM
237 |
238 |
242 | 11:00 AM - 12:00 PM
243 |
244 |
248 | 12:00 PM - 1:00 PM
249 |
250 |
254 | 1:00 PM - 2:00 PM
255 |
256 |
260 | 2:00 PM - 3:00 PM
261 |
262 |
266 | 3:00 PM - 4:00 PM
267 |
268 |
272 | 4:00 PM - 5:00 PM
273 |
274 |
278 | 5:00 PM - 6:00 PM
279 |
280 |
284 | 6:00 PM - 7:00 PM
285 |
286 |
290 | 7:00 PM - 8:00 PM
291 |
292 |
296 | 8:00 PM - 9:00 PM
297 |
298 |
302 | 9:00 PM - 10:00 PM
303 |
304 |
308 | 10:00 PM - 11:00 PM
309 |
310 |
314 | 11:00 PM - 12:00 AM
315 |
316 |
317 |
320 |
323 |
324 | Tuesday (28)
325 |
326 |
327 |
331 | 12:00 AM - 1:00 AM
332 |
333 |
337 | 1:00 AM - 2:00 AM
338 |
339 |
343 | 2:00 AM - 3:00 AM
344 |
345 |
349 | 3:00 AM - 4:00 AM
350 |
351 |
355 | 4:00 AM - 5:00 AM
356 |
357 |
361 | 5:00 AM - 6:00 AM
362 |
363 |
367 | 6:00 AM - 7:00 AM
368 |
369 |
373 | 7:00 AM - 8:00 AM
374 |
375 |
379 | 8:00 AM - 9:00 AM
380 |
381 |
385 | 9:00 AM - 10:00 AM
386 |
387 |
391 | 10:00 AM - 11:00 AM
392 |
393 |
397 | 11:00 AM - 12:00 PM
398 |
399 |
403 | 12:00 PM - 1:00 PM
404 |
405 |
409 | 1:00 PM - 2:00 PM
410 |
411 |
415 | 2:00 PM - 3:00 PM
416 |
417 |
421 | 3:00 PM - 4:00 PM
422 |
423 |
427 | 4:00 PM - 5:00 PM
428 |
429 |
433 | 5:00 PM - 6:00 PM
434 |
435 |
439 | 6:00 PM - 7:00 PM
440 |
441 |
445 | 7:00 PM - 8:00 PM
446 |
447 |
451 | 8:00 PM - 9:00 PM
452 |
453 |
457 | 9:00 PM - 10:00 PM
458 |
459 |
463 | 10:00 PM - 11:00 PM
464 |
465 |
469 | 11:00 PM - 12:00 AM
470 |
471 |
472 |
475 |
478 |
479 | Wednesday (29)
480 |
481 |
482 |
486 | 12:00 AM - 1:00 AM
487 |
488 |
492 | 1:00 AM - 2:00 AM
493 |
494 |
498 | 2:00 AM - 3:00 AM
499 |
500 |
504 | 3:00 AM - 4:00 AM
505 |
506 |
510 | 4:00 AM - 5:00 AM
511 |
512 |
516 | 5:00 AM - 6:00 AM
517 |
518 |
522 | 6:00 AM - 7:00 AM
523 |
524 |
528 | 7:00 AM - 8:00 AM
529 |
530 |
534 | 8:00 AM - 9:00 AM
535 |
536 |
540 | 9:00 AM - 10:00 AM
541 |
542 |
546 | 10:00 AM - 11:00 AM
547 |
548 |
552 | 11:00 AM - 12:00 PM
553 |
554 |
558 | 12:00 PM - 1:00 PM
559 |
560 |
564 | 1:00 PM - 2:00 PM
565 |
566 |
570 | 2:00 PM - 3:00 PM
571 |
572 |
576 | 3:00 PM - 4:00 PM
577 |
578 |
582 | 4:00 PM - 5:00 PM
583 |
584 |
588 | 5:00 PM - 6:00 PM
589 |
590 |
594 | 6:00 PM - 7:00 PM
595 |
596 |
600 | 7:00 PM - 8:00 PM
601 |
602 |
606 | 8:00 PM - 9:00 PM
607 |
608 |
612 | 9:00 PM - 10:00 PM
613 |
614 |
618 | 10:00 PM - 11:00 PM
619 |
620 |
624 | 11:00 PM - 12:00 AM
625 |
626 |
627 |
630 |
633 |
634 | Thursday (30)
635 |
636 |
637 |
641 | 12:00 AM - 1:00 AM
642 |
643 |
647 | 1:00 AM - 2:00 AM
648 |
649 |
653 | 2:00 AM - 3:00 AM
654 |
655 |
659 | 3:00 AM - 4:00 AM
660 |
661 |
665 | 4:00 AM - 5:00 AM
666 |
667 |
671 | 5:00 AM - 6:00 AM
672 |
673 |
677 | 6:00 AM - 7:00 AM
678 |
679 |
683 | 7:00 AM - 8:00 AM
684 |
685 |
689 | 8:00 AM - 9:00 AM
690 |
691 |
695 | 9:00 AM - 10:00 AM
696 |
697 |
701 | 10:00 AM - 11:00 AM
702 |
703 |
707 | 11:00 AM - 12:00 PM
708 |
709 |
713 | 12:00 PM - 1:00 PM
714 |
715 |
719 | 1:00 PM - 2:00 PM
720 |
721 |
725 | 2:00 PM - 3:00 PM
726 |
727 |
731 | 3:00 PM - 4:00 PM
732 |
733 |
737 | 4:00 PM - 5:00 PM
738 |
739 |
743 | 5:00 PM - 6:00 PM
744 |
745 |
749 | 6:00 PM - 7:00 PM
750 |
751 |
755 | 7:00 PM - 8:00 PM
756 |
757 |
761 | 8:00 PM - 9:00 PM
762 |
763 |
767 | 9:00 PM - 10:00 PM
768 |
769 |
773 | 10:00 PM - 11:00 PM
774 |
775 |
779 | 11:00 PM - 12:00 AM
780 |
781 |
782 |
785 |
788 |
789 | Friday (31)
790 |
791 |
792 |
796 | 12:00 AM - 1:00 AM
797 |
798 |
802 | 1:00 AM - 2:00 AM
803 |
804 |
808 | 2:00 AM - 3:00 AM
809 |
810 |
814 | 3:00 AM - 4:00 AM
815 |
816 |
820 | 4:00 AM - 5:00 AM
821 |
822 |
826 | 5:00 AM - 6:00 AM
827 |
828 |
832 | 6:00 AM - 7:00 AM
833 |
834 |
838 | 7:00 AM - 8:00 AM
839 |
840 |
844 | 8:00 AM - 9:00 AM
845 |
846 |
850 | 9:00 AM - 10:00 AM
851 |
852 |
856 | 10:00 AM - 11:00 AM
857 |
858 |
862 | 11:00 AM - 12:00 PM
863 |
864 |
868 | 12:00 PM - 1:00 PM
869 |
870 |
874 | 1:00 PM - 2:00 PM
875 |
876 |
880 | 2:00 PM - 3:00 PM
881 |
882 |
886 | 3:00 PM - 4:00 PM
887 |
888 |
892 | 4:00 PM - 5:00 PM
893 |
894 |
898 | 5:00 PM - 6:00 PM
899 |
900 |
904 | 6:00 PM - 7:00 PM
905 |
906 |
910 | 7:00 PM - 8:00 PM
911 |
912 |
916 | 8:00 PM - 9:00 PM
917 |
918 |
922 | 9:00 PM - 10:00 PM
923 |
924 |
928 | 10:00 PM - 11:00 PM
929 |
930 |
934 | 11:00 PM - 12:00 AM
935 |
936 |
937 |
940 |
943 |
944 | Saturday (1)
945 |
946 |
947 |
951 | 12:00 AM - 1:00 AM
952 |
953 |
957 | 1:00 AM - 2:00 AM
958 |
959 |
963 | 2:00 AM - 3:00 AM
964 |
965 |
969 | 3:00 AM - 4:00 AM
970 |
971 |
975 | 4:00 AM - 5:00 AM
976 |
977 |
981 | 5:00 AM - 6:00 AM
982 |
983 |
987 | 6:00 AM - 7:00 AM
988 |
989 |
993 | 7:00 AM - 8:00 AM
994 |
995 |
999 | 8:00 AM - 9:00 AM
1000 |
1001 |
1005 | 9:00 AM - 10:00 AM
1006 |
1007 |
1011 | 10:00 AM - 11:00 AM
1012 |
1013 |
1017 | 11:00 AM - 12:00 PM
1018 |
1019 |
1023 | 12:00 PM - 1:00 PM
1024 |
1025 |
1029 | 1:00 PM - 2:00 PM
1030 |
1031 |
1035 | 2:00 PM - 3:00 PM
1036 |
1037 |
1041 | 3:00 PM - 4:00 PM
1042 |
1043 |
1047 | 4:00 PM - 5:00 PM
1048 |
1049 |
1053 | 5:00 PM - 6:00 PM
1054 |
1055 |
1059 | 6:00 PM - 7:00 PM
1060 |
1061 |
1065 | 7:00 PM - 8:00 PM
1066 |
1067 |
1071 | 8:00 PM - 9:00 PM
1072 |
1073 |
1077 | 9:00 PM - 10:00 PM
1078 |
1079 |
1083 | 10:00 PM - 11:00 PM
1084 |
1085 |
1089 | 11:00 PM - 12:00 AM
1090 |
1091 |
1092 |
1093 | `;
1094 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React Timeslot Calendar Demo App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-timeslot-calendar",
3 | "version": "0.2.0",
4 | "description": "A calendar based on timeslots which can be set as available, occupied and so on.",
5 | "main": "build/build.min.js",
6 | "scripts": {
7 | "build": "npm run eslint && npm run prod",
8 | "eslint": "./node_modules/.bin/eslint src/js/**/*.jsx",
9 | "prod": "./node_modules/.bin/webpack --config webpack.config.js -p",
10 | "analyze": "./node_modules/.bin/webpack --config webpack.config.js --json -p | ./node_modules/.bin/webpack-bundle-size-analyzer",
11 | "dev": "./node_modules/.bin/webpack-dev-server --config webpack.demo.config.js --hot",
12 | "demo": "./node_modules/.bin/webpack --config webpack.demo.config.js",
13 | "test": "npm run eslint && jest --verbose -e",
14 | "prepublish": "npm run build"
15 | },
16 | "keywords": [
17 | "react",
18 | "calendar",
19 | "timeslot",
20 | "scheduling",
21 | "schedule"
22 | ],
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/lrojas94/react-timeslot-calendar"
26 | },
27 | "author": "Luis Rojas & Jaime Rojas",
28 | "license": "MIT",
29 | "devDependencies": {
30 | "babel-core": "^6.24.1",
31 | "babel-jest": "^20.0.3",
32 | "babel-loader": "^7.0.0",
33 | "babel-preset-es2015": "^6.24.1",
34 | "babel-preset-react": "^6.24.1",
35 | "classnames": "^2.2.5",
36 | "css-loader": "^0.28.4",
37 | "enzyme": "^2.8.2",
38 | "eslint": "^3.19.0",
39 | "eslint-plugin-jest": "^20.0.3",
40 | "eslint-plugin-react": "^7.0.1",
41 | "highlight.js": "^9.12.0",
42 | "html-loader": "^0.4.5",
43 | "jest": "^20.0.4",
44 | "markdown-loader": "^2.0.0",
45 | "marked": "^0.3.6",
46 | "node-sass": "^4.5.3",
47 | "prop-types": "^15.5.10",
48 | "react": "^15.5.4",
49 | "react-dom": "^15.5.4",
50 | "react-test-renderer": "^15.5.4",
51 | "sass-loader": "^6.0.5",
52 | "sinon": "^2.3.2",
53 | "style-loader": "^0.18.1",
54 | "webpack": "^2.6.1",
55 | "webpack-bundle-size-analyzer": "^2.7.0",
56 | "webpack-dev-server": "^2.4.5"
57 | },
58 | "dependencies": {
59 | "calendarjs": "^0.1.0",
60 | "moment": "^2.18.1"
61 | },
62 | "peerDependencies": {
63 | "react": "^15.5.4"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/public/atom-one-dark.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Atom One Dark by Daniel Gamage
4 | Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
5 |
6 | base: #282c34
7 | mono-1: #abb2bf
8 | mono-2: #818896
9 | mono-3: #5c6370
10 | hue-1: #56b6c2
11 | hue-2: #61aeee
12 | hue-3: #c678dd
13 | hue-4: #98c379
14 | hue-5: #e06c75
15 | hue-5-2: #be5046
16 | hue-6: #d19a66
17 | hue-6-2: #e6c07b
18 |
19 | */
20 |
21 | .hljs {
22 | display: block;
23 | overflow-x: auto;
24 | padding: 0.5em;
25 | color: #abb2bf;
26 | background: #282c34;
27 | }
28 |
29 | .hljs-comment,
30 | .hljs-quote {
31 | color: #5c6370;
32 | font-style: italic;
33 | }
34 |
35 | .hljs-doctag,
36 | .hljs-keyword,
37 | .hljs-formula {
38 | color: #c678dd;
39 | }
40 |
41 | .hljs-section,
42 | .hljs-name,
43 | .hljs-selector-tag,
44 | .hljs-deletion,
45 | .hljs-subst {
46 | color: #e06c75;
47 | }
48 |
49 | .hljs-literal {
50 | color: #56b6c2;
51 | }
52 |
53 | .hljs-string,
54 | .hljs-regexp,
55 | .hljs-addition,
56 | .hljs-attribute,
57 | .hljs-meta-string {
58 | color: #98c379;
59 | }
60 |
61 | .hljs-built_in,
62 | .hljs-class .hljs-title {
63 | color: #e6c07b;
64 | }
65 |
66 | .hljs-attr,
67 | .hljs-variable,
68 | .hljs-template-variable,
69 | .hljs-type,
70 | .hljs-selector-class,
71 | .hljs-selector-attr,
72 | .hljs-selector-pseudo,
73 | .hljs-number {
74 | color: #d19a66;
75 | }
76 |
77 | .hljs-symbol,
78 | .hljs-bullet,
79 | .hljs-link,
80 | .hljs-meta,
81 | .hljs-selector-id,
82 | .hljs-title {
83 | color: #61aeee;
84 | }
85 |
86 | .hljs-emphasis {
87 | font-style: italic;
88 | }
89 |
90 | .hljs-strong {
91 | font-weight: bold;
92 | }
93 |
94 | .hljs-link {
95 | text-decoration: underline;
96 | }
97 |
--------------------------------------------------------------------------------
/src/js/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lrojas94/react-timeslot-calendar/04d98812448b8e3f93400c59e05707feb3ba72b2/src/js/components/.gitkeep
--------------------------------------------------------------------------------
/src/js/components/calendar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import moment from 'moment';
4 | import CalendarJS from 'calendarjs';
5 | import Month from './month.jsx';
6 |
7 | export default class Calendar extends React.Component {
8 | constructor(props) {
9 |
10 | super(props);
11 |
12 | this._updateInputProps(this.props.startDateInputProps, this.props.endDateInputProps);
13 | this._updateTimeslotProps(this.props.timeslotProps);
14 | this._updateRenderDays(this.props.renderDays);
15 |
16 | this.state = {
17 | currentDate: moment(props.initialDate),
18 | selectedTimeslots: [],
19 | };
20 | }
21 |
22 | render() {
23 | return (
24 |
25 | { this._renderActions() }
26 | { this._renderMonth() }
27 | { this._renderInputs() }
28 |
29 | );
30 | }
31 |
32 | _renderActions() {
33 | const {
34 | currentDate,
35 | } = this.state;
36 |
37 | const actionTitle = `${currentDate.format('MMMM - YYYY')}`;
38 |
39 | return (
40 |
41 |
42 | ‹
43 |
44 |
45 | { actionTitle }
46 |
47 |
48 | ›
49 |
50 |
51 | );
52 | }
53 |
54 | _renderMonth() {
55 | const {
56 | currentDate,
57 | selectedTimeslots,
58 | } = this.state;
59 |
60 | const {
61 | timeslots,
62 | initialDate,
63 | } = this.props;
64 |
65 | const cal = new CalendarJS(currentDate.year(), currentDate.month() + 1);
66 | const weeks = cal.generate();
67 |
68 | return (
69 |
81 | );
82 | }
83 |
84 | _renderInputs() {
85 | const {
86 | selectedTimeslots,
87 | } = this.state;
88 |
89 | const {
90 | startDate,
91 | endDate,
92 | } = this.inputProps;
93 |
94 | //Determines if multiple input or single one.
95 | const inputPrefix = selectedTimeslots.length > 1 ? '[]' : '';
96 |
97 | return selectedTimeslots.map((timeslot, index) => {
98 | return (
99 |
100 |
106 |
112 |
113 | );
114 | });
115 | }
116 |
117 | _onWeekOutOfMonth(updateDate) {
118 | this.setState({
119 | currentDate: updateDate,
120 | });
121 |
122 | return;
123 | }
124 |
125 | _onGoToNextMonth() {
126 | const {
127 | currentDate,
128 | } = this.state;
129 |
130 | let nextDate = currentDate.clone()
131 | .startOf('month')
132 | .add(1, 'months')
133 | .startOf('month');
134 |
135 | this.setState({
136 | currentDate: nextDate,
137 | });
138 | }
139 |
140 | _onGoToPrevMonth() {
141 | const {
142 | currentDate,
143 | } = this.state;
144 |
145 | let nextDate = currentDate.clone()
146 | .startOf('month')
147 | .subtract(1, 'months')
148 | .startOf('month');
149 |
150 | this.setState({
151 | currentDate: nextDate,
152 | });
153 | }
154 |
155 | _formatDisabledTimeslots() {
156 | const {
157 | disabledTimeslots,
158 | } = this.props;
159 |
160 | return disabledTimeslots.map((timeslot) => {
161 | let timeslotMoment = Object.assign({}, timeslot);
162 | timeslotMoment.startDate = moment(timeslotMoment.startDate, timeslotMoment.format);
163 | timeslotMoment.endDate = moment(timeslotMoment.endDate, timeslotMoment.format);
164 |
165 | return timeslotMoment;
166 | });
167 | }
168 |
169 | _onTimeslotClick(newTimeslot) {
170 | const {
171 | selectedTimeslots,
172 | } = this.state;
173 |
174 | const {
175 | maxTimeslots,
176 | onSelectTimeslot,
177 | } = this.props;
178 |
179 | const newSelectedTimeslots = selectedTimeslots.slice();
180 |
181 | let existentTimeslotIndex = -1;
182 | const timeslotExists = newSelectedTimeslots.some((timeslot, index) => {
183 | existentTimeslotIndex = index;
184 | return newTimeslot.startDate.format() === timeslot.startDate.format();
185 | });
186 |
187 | if (timeslotExists) {
188 | newSelectedTimeslots.splice(existentTimeslotIndex, 1);
189 | }
190 | else {
191 | newSelectedTimeslots.push(newTimeslot);
192 | }
193 |
194 | if (newSelectedTimeslots.length > maxTimeslots) {
195 | newSelectedTimeslots.splice(0, 1);
196 | }
197 |
198 | this.setState({
199 | selectedTimeslots: newSelectedTimeslots,
200 | currentDate: moment(newTimeslot.startDate),
201 | }, () => {
202 | // State was set:
203 | onSelectTimeslot && onSelectTimeslot(newSelectedTimeslots, newTimeslot);
204 | });
205 | }
206 |
207 | _updateInputProps(startDateInputProps, endDateInputProps) {
208 | const defaultStartDateProps = {
209 | name: 'tsc-startDate',
210 | classes: 'tsc-hidden-input',
211 | type: 'hidden',
212 | };
213 |
214 | const defaultEndDateProps = {
215 | name: 'tsc-endDate',
216 | classes: 'tsc-hidden-input',
217 | type: 'hidden',
218 | };
219 |
220 | this.inputProps = {
221 | startDate: Object.assign({}, defaultStartDateProps, startDateInputProps),
222 | endDate: Object.assign({}, defaultEndDateProps, endDateInputProps),
223 | };
224 | }
225 |
226 | _updateTimeslotProps(timeslotProps) {
227 | const defaultProps = {
228 | format: 'h',
229 | showFormat: 'h:mm A',
230 | };
231 |
232 | this.timeslotProps = Object.assign({}, defaultProps, timeslotProps);
233 | }
234 |
235 | _updateRenderDays(renderDays) {
236 | const defaultRenderDays = {
237 | sunday: true,
238 | monday: true,
239 | tuesday: true,
240 | wednesday: true,
241 | thursday: true,
242 | friday: true,
243 | saturday: true,
244 | };
245 |
246 | this.renderDays = Object.assign({}, defaultRenderDays, renderDays);
247 | }
248 |
249 | componentWillReceiveProps(nextProps) {
250 | this._updateInputProps(nextProps.startDateInputProps, nextProps.endDateInputProps);
251 | this._updateTimeslotProps(nextProps.timeslotProps);
252 | this._updateRenderDays(nextProps.renderDays);
253 | }
254 |
255 | }
256 |
257 | Calendar.defaultProps = {
258 | disabledTimeslots: [],
259 | maxTimeslots: 1,
260 | inputProps: {
261 | names: {},
262 | },
263 | startDateInputProps: {},
264 | endDateInputProps: {},
265 | };
266 |
267 | /**
268 | * @type {String} initialDate: The initial date in which to place the calendar. Must be MomentJS parseable.
269 | * @type {Array} timeslots: An array of timeslots to be displayed in each day.
270 | * @type {Object} timeslotProps: An object with keys and values for timeslot props (format, viewFormat)
271 | * @type {Array} selectedTimeslots: Initial value for selected timeslot inputs. Expects Dates formatted as Strings.
272 | * @type {Array} disabledTimeslots: Initial value for selected timeslot inputs. Expects Dates formatted as Strings.
273 | * @type {Integer} maxTimexlots: maximum ammount of timeslots to select.
274 | * @type {Object} renderDays: An array of days which states which days of the week to render. By default renders all days.
275 | * @type {Object} startDateInputProps: properties for the startDate Inputs. Includes name, class, type (hidden, text...)
276 | * @type {Object} endDateInputProps: properties for the endDate Inputs. Includes name, class, type (hidden, text...)
277 | */
278 | Calendar.propTypes = {
279 | initialDate: PropTypes.string.isRequired,
280 | timeslots: PropTypes.array.isRequired,
281 | timeslotProps: PropTypes.object,
282 | selectedTimeslots: PropTypes.array,
283 | disabledTimeslots: PropTypes.array,
284 | maxTimeslots: PropTypes.number,
285 | renderDays: PropTypes.object,
286 | startDateInputProps: PropTypes.object,
287 | endDateInputProps: PropTypes.object,
288 | onSelectTimeslot: PropTypes.func,
289 | };
290 |
--------------------------------------------------------------------------------
/src/js/components/day.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classnames from 'classnames';
4 | import moment from 'moment';
5 | import Timeslot from './timeslot.jsx';
6 |
7 | import {
8 | DEFAULT_TIMESLOT_FORMAT,
9 | DEFAULT_TIMESLOT_SHOW_FORMAT,
10 | } from '../constants/day.js';
11 |
12 | import {
13 | DEFAULT,
14 | DISABLED,
15 | SELECTED,
16 | } from '../constants/timeslot.js';
17 |
18 | export default class Day extends React.Component {
19 |
20 | render() {
21 | const dayClassNames = classnames({
22 | 'tsc-day': true,
23 | });
24 |
25 | return (
26 |
27 | { this._renderTitle() }
28 | { this._renderTimeSlots() }
29 |
30 | );
31 | }
32 |
33 | _renderTitle() {
34 | const {
35 | renderTitle,
36 | momentTime,
37 | } = this.props;
38 |
39 | return (
40 |
41 | {renderTitle(momentTime)}
42 |
43 | );
44 | }
45 |
46 | _renderTimeSlots() {
47 | const {
48 | timeslots,
49 | timeslotProps,
50 | selectedTimeslots,
51 | disabledTimeslots,
52 | momentTime,
53 | initialDate,
54 | } = this.props;
55 |
56 | return timeslots.map((slot, index) => {
57 | let description = '';
58 | for (let i = 0; i < slot.length; i ++){
59 | description += moment(slot[i], timeslotProps.format).format(timeslotProps.showFormat);
60 | if (i < (slot.length - 1)){
61 | description += ' - ';
62 | }
63 | }
64 | let timeslotDates = {
65 | startDate: momentTime.clone().add(slot[0], timeslotProps.format),
66 | endDate: momentTime.clone().add(slot[slot.length - 1], timeslotProps.format),
67 | };
68 |
69 | let status = DEFAULT;
70 | if (timeslotDates.startDate.isBefore(initialDate) || timeslotDates.startDate.isSame(initialDate)) {
71 | status = DISABLED;
72 | }
73 |
74 | const isSelected = selectedTimeslots.some((selectedTimeslot) => {
75 | return timeslotDates.startDate.format() === selectedTimeslot.startDate.format();
76 | });
77 |
78 | const isDisabled = disabledTimeslots.some((disabledTimeslot) => {
79 | return disabledTimeslot.startDate.isBetween(timeslotDates.startDate, timeslotDates.endDate, null, '[)') ||
80 | disabledTimeslot.endDate.isBetween(timeslotDates.startDate, timeslotDates.endDate, null, '(]');
81 | });
82 |
83 | if (isDisabled) {
84 | status = DISABLED;
85 | }
86 | else if (isSelected) {
87 | status = SELECTED;
88 | }
89 |
90 |
91 | return (
92 |
98 | );
99 | });
100 | }
101 |
102 | _onTimeslotClick(index) {
103 | const {
104 | timeslots,
105 | timeslotFormat,
106 | momentTime,
107 | onTimeslotClick,
108 | } = this.props;
109 |
110 | const timeslot = {
111 | startDate: momentTime.clone().add(timeslots[index][0], timeslotFormat),
112 | endDate: momentTime.clone().add(timeslots[index][1], timeslotFormat),
113 | };
114 |
115 | onTimeslotClick(timeslot);
116 | }
117 | }
118 |
119 | Day.defaultProps = {
120 | timeslotFormat: DEFAULT_TIMESLOT_FORMAT,
121 | timeslotShowFormat: DEFAULT_TIMESLOT_SHOW_FORMAT,
122 | renderTitle: (momentTime) => {
123 | return momentTime.format('dddd (D)');
124 | },
125 | };
126 |
127 | /**
128 | * @type {Array} timeslots: Array of timeslots.
129 | * @type {Object} timeslotProps: An object with keys and values for timeslot props (format, viewFormat)
130 | * @type {Array} selectedTimeslots: Selected Timeslots Set used to add the SELECTED status if needed when renderizing timeslots.
131 | * @type {Array} disabledTimeslots: Disabled Timeslots Set used to add the DISABLED status if needed when renderizing timeslots.
132 | * @type {String} timeslotFormat: format used by moment when identifying the timeslot
133 | * @type {String} timslotShowFormat: format to show used by moment when formating timeslot hours for final view.
134 | * @type {Function} onTimeslotClick: Function to be excecuted when clicked.
135 | * @type {Function} renderTitle: Function to be used when rendering the title.
136 | * @type {Object} momentTime: MomentJS datetime object.
137 | * @type {Ojbect} initialDate: Moment JS Date used to initialize the Calendar and which progresses further into the tree.
138 | */
139 | Day.propTypes = {
140 | timeslots: PropTypes.array.isRequired,
141 | timeslotProps: PropTypes.object,
142 | selectedTimeslots: PropTypes.array,
143 | disabledTimeslots: PropTypes.array,
144 | timeslotFormat: PropTypes.string.isRequired,
145 | timeslotShowFormat: PropTypes.string.isRequired,
146 | onTimeslotClick: PropTypes.func.isRequired,
147 | renderTitle: PropTypes.func.isRequired,
148 | momentTime: PropTypes.object.isRequired,
149 | initialDate: PropTypes.object.isRequired,
150 | };
151 |
--------------------------------------------------------------------------------
/src/js/components/month.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import helpers from './../util/helpers';
4 | import Week from './week.jsx';
5 |
6 | export default class Month extends React.Component {
7 | constructor(props) {
8 | super(props);
9 |
10 | this.state = {
11 | currentWeekIndex: this._getStartingWeek(props.currentDate, props.weeks),
12 | };
13 | }
14 |
15 | _getStartingWeek(currentDate, weeks) {
16 | // find out staring week:
17 | const currentDateWithoutTime = currentDate.startOf('day');
18 | let startingWeek = 0;
19 | weeks.some((week, index) => {
20 | let weekContainsDate = week.some((day) => {
21 | const momentDay = helpers.getMomentFromCalendarJSDateElement(day);
22 | return momentDay.format() === currentDateWithoutTime.format();
23 | });
24 |
25 | if (weekContainsDate) {
26 | startingWeek = index;
27 | return weekContainsDate;
28 | }
29 | });
30 |
31 | return startingWeek;
32 | }
33 |
34 | render() {
35 |
36 | return (
37 |
38 | { this._renderActions() }
39 | { this._renderWeek() }
40 |
41 | );
42 | }
43 |
44 | _renderActions() {
45 | const {
46 | weeks,
47 | } = this.props;
48 |
49 | const {
50 | currentWeekIndex,
51 | } = this.state;
52 |
53 | const currentWeek = weeks[currentWeekIndex];
54 | const startDate = helpers.getMomentFromCalendarJSDateElement(currentWeek[0]);
55 | const endDate = helpers.getMomentFromCalendarJSDateElement(currentWeek[currentWeek.length - 1]);
56 | const actionTitle = `${startDate.format('MMM Do')} - ${endDate.format('MMM Do')}`;
57 |
58 | return (
59 |
60 |
61 | ‹
62 |
63 |
64 | { actionTitle }
65 |
66 |
67 | ›
68 |
69 |
70 | );
71 | }
72 |
73 | _renderWeek() {
74 | const {
75 | currentWeekIndex,
76 | } = this.state;
77 |
78 | const {
79 | weeks,
80 | initialDate,
81 | timeslots,
82 | timeslotProps,
83 | selectedTimeslots,
84 | disabledTimeslots,
85 | renderDays,
86 | } = this.props;
87 |
88 | return (
89 |
99 | );
100 | }
101 |
102 | _onTimeslotClick(timeslot) {
103 | const {
104 | onTimeslotClick,
105 | } = this.props;
106 |
107 | onTimeslotClick(timeslot);
108 | }
109 |
110 | /**
111 | * Handles prev week button click.
112 | */
113 | _onPrevWeekClicked() {
114 | const {
115 | currentWeekIndex,
116 | } = this.state;
117 |
118 | const {
119 | onWeekOutOfMonth,
120 | weeks,
121 | } = this.props;
122 |
123 | if (currentWeekIndex - 1 >= 0) {
124 | this.setState({
125 | currentWeekIndex: currentWeekIndex - 1,
126 | });
127 | }
128 | else if (onWeekOutOfMonth) {
129 | const firstDayOfPrevWeek = helpers.getMomentFromCalendarJSDateElement(weeks[0][0]).clone().subtract(1, 'days');
130 | onWeekOutOfMonth(firstDayOfPrevWeek);
131 | }
132 | }
133 |
134 | /**
135 | * Handles next week button click.
136 | */
137 | _onNextWeekClicked() {
138 | const {
139 | currentWeekIndex,
140 | } = this.state;
141 |
142 | const {
143 | weeks,
144 | onWeekOutOfMonth,
145 | } = this.props;
146 |
147 | if (currentWeekIndex + 1 < weeks.length) {
148 | this.setState({
149 | currentWeekIndex: currentWeekIndex + 1,
150 | });
151 | }
152 | else if (onWeekOutOfMonth) {
153 | const lastDay = weeks[currentWeekIndex].length - 1;
154 | const firstDayOfNextWeek = helpers.getMomentFromCalendarJSDateElement(weeks[currentWeekIndex][lastDay]).clone().add(1, 'days');
155 | onWeekOutOfMonth(firstDayOfNextWeek);
156 | }
157 | }
158 |
159 | componentWillReceiveProps(nextProps) {
160 | this.setState({
161 | currentWeekIndex: this._getStartingWeek(nextProps.currentDate, nextProps.weeks),
162 | });
163 | }
164 | }
165 |
166 | /**
167 | * @type {Object} currentDate: Base currentDate to get the month from - Usually first day of the month
168 | * @type {Array} weeks: A list of weeks based on calendarJS
169 | * @type {Function} onWeekOutOfMonth: A callback to call when user goes out of the month
170 | * @type {Function} onTimeslotClick: Function to be excecuted when clicked.
171 | * @type {Object} initialDate: Moment JS Date used to initialize the Calendar and which progresses further into the tree.
172 | * @type {Array} timeslots: An array of timeslots to be displayed in each day.
173 | * @type {Object} timeslotProps: An object with keys and values for timeslot props (format, viewFormat)
174 | * @type {Array} selectedTimeslots: Selected Timeslots Set used further into the tree to add the classes needed to when renderizing timeslots.
175 | * @type {Array} DisabledTimeslots: Disabled Timeslots Set used further into the tree to add the classes needed to when renderizing timeslots.
176 | * @type {Object} renderDays: An array of days which states which days of the week to render. By default renders all days.
177 | */
178 | Month.propTypes = {
179 | currentDate: PropTypes.object.isRequired,
180 | weeks: PropTypes.array.isRequired,
181 | onWeekOutOfMonth: PropTypes.func,
182 | onTimeslotClick: PropTypes.func,
183 | initialDate: PropTypes.object.isRequired,
184 | timeslots: PropTypes.array.isRequired,
185 | timeslotProps: PropTypes.object,
186 | selectedTimeslots: PropTypes.array,
187 | disabledTimeslots: PropTypes.array,
188 | renderDays: PropTypes.object,
189 | };
190 |
--------------------------------------------------------------------------------
/src/js/components/timeslot.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classnames from 'classnames';
4 |
5 | import {
6 | DEFAULT,
7 | SELECTED,
8 | DISABLED,
9 | } from '../constants/timeslot.js';
10 |
11 | export default class Timeslot extends React.Component {
12 | render() {
13 | const {
14 | description,
15 | status,
16 | customClassNames,
17 | } = this.props;
18 |
19 | const timeslotClassNames = classnames({
20 | 'tsc-timeslot': true,
21 | 'tsc-timeslot--selected': status == SELECTED,
22 | 'tsc-timeslot--disabled': status == DISABLED,
23 | }, customClassNames);
24 |
25 | return (
26 |
27 | { description }
28 |
29 | );
30 | }
31 |
32 | _onTimeslotClick(event) {
33 | event.preventDefault();
34 | const {
35 | status,
36 | onClick,
37 | } = this.props;
38 |
39 | if (status !== DISABLED) {
40 | onClick();
41 | }
42 | }
43 | }
44 |
45 | Timeslot.defaultProps = {
46 | status: DEFAULT,
47 | };
48 |
49 | /**
50 | * @type {String} description: The contents to be displayed by the timeslot. Default format will resume to something similar to "7:00 PM - 8:00 PM"
51 | * @type {String} status: allows the div to change format based on the current status of the element (disabled, selected, default)
52 | * @type (Function) onClick: Function to be excecuted when clicked.
53 | */
54 | Timeslot.propTypes = {
55 | description: PropTypes.string.isRequired,
56 | status: PropTypes.oneOf([
57 | DEFAULT,
58 | SELECTED,
59 | DISABLED,
60 | ]),
61 | onClick: PropTypes.func.isRequired,
62 | customClassNames: PropTypes.oneOfType([
63 | PropTypes.string,
64 | PropTypes.object,
65 | ]),
66 | };
67 |
--------------------------------------------------------------------------------
/src/js/components/week.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import helpers from './../util/helpers';
4 | import Day from './day.jsx';
5 |
6 | export default class Week extends React.Component {
7 | render() {
8 |
9 | return (
10 |
11 | { this._renderWeekDays() }
12 |
13 | );
14 | }
15 |
16 | _renderWeekDays() {
17 | const {
18 | weekToRender,
19 | initialDate,
20 | timeslots,
21 | timeslotProps,
22 | selectedTimeslots,
23 | disabledTimeslots,
24 | renderDays,
25 | } = this.props;
26 |
27 | return weekToRender.map((day, index) => {
28 | let formattedDate = helpers.getMomentFromCalendarJSDateElement(day);
29 | const weekDay = formattedDate.format('dddd').toLowerCase();
30 | if (renderDays[weekDay]){
31 | return (
32 |
42 | );
43 | }
44 | });
45 | }
46 |
47 | _onTimeslotClick(timeslot) {
48 | const {
49 | onTimeslotClick,
50 | } = this.props;
51 |
52 | onTimeslotClick(timeslot);
53 | }
54 | }
55 |
56 | /**
57 | * @type {Array} weekToRender: Week to render. Each day should also have the requested timeslots, unless default configuration is desired.
58 | * @type {Function} onTimeslotClick: Function to be excecuted when clicked.
59 | * @type {Object} initialDate: Moment JS Date used to initialize the Calendar and which progresses further into the tree.
60 | * @type {Array} timeslots: Timeslots Set of Timeslot elements to render. Progresses further into the tree.
61 | * @type {Object} timeslotProps: An object with keys and values for timeslot props (format, viewFormat)
62 | * @type {Array} selectedTimeslots: Selected Timeslots Set used further into the tree to add the classes needed to when renderizing timeslots.
63 | * @type {Array} disabledTimeslots: Disabled Timeslots Set used further into the tree to add the classes needed to when renderizing timeslots.
64 | * @type {Object} renderDays: An array of days which states which days of the week to render. By default renders all days.
65 | */
66 | Week.propTypes = {
67 | weekToRender: PropTypes.array.isRequired,
68 | onTimeslotClick: PropTypes.func.isRequired,
69 | initialDate: PropTypes.object.isRequired,
70 | timeslots : PropTypes.array.isRequired,
71 | timeslotProps: PropTypes.object,
72 | selectedTimeslots: PropTypes.array,
73 | disabledTimeslots: PropTypes.array,
74 | renderDays: PropTypes.object,
75 | };
76 |
--------------------------------------------------------------------------------
/src/js/constants/day.js:
--------------------------------------------------------------------------------
1 | export const DEFAULT_TIMESLOT_FORMAT = 'h';
2 | export const DEFAULT_TIMESLOT_SHOW_FORMAT = 'h:mm A';
3 | export const DEFAULT_TIMESLOTS = Array(24).fill(null).map((n, i) => [`${ i }`, `${ i + 1 }`] );
4 |
--------------------------------------------------------------------------------
/src/js/constants/timeslot.js:
--------------------------------------------------------------------------------
1 | export const DEFAULT = 'DEFAULT';
2 | export const SELECTED = 'SELECTED';
3 | export const DISABLED = 'DISABLED';
4 |
--------------------------------------------------------------------------------
/src/js/constants/week.js:
--------------------------------------------------------------------------------
1 | export const RENDER_DAYS = {
2 | sunday: true,
3 | monday: true,
4 | tuesday: true,
5 | wednesday: true,
6 | thursday: true,
7 | friday: true,
8 | saturday: true,
9 | };
10 |
--------------------------------------------------------------------------------
/src/js/demo/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lrojas94/react-timeslot-calendar/04d98812448b8e3f93400c59e05707feb3ba72b2/src/js/demo/.gitkeep
--------------------------------------------------------------------------------
/src/js/demo/app.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import moment from 'moment';
4 |
5 | import '../../styles/demo/main.scss';
6 |
7 | import ReactTimeslotCalendar from './../react-timeslot-calendar.jsx';
8 | import MarkdownSnippet from './../util/markdown-snippet.jsx';
9 | /** Code snippets **/
10 | import customTimeslotSnippet from './snippets/custom-timeslot.md';
11 |
12 | export default class App extends React.Component {
13 | constructor(props) {
14 | super(props);
15 |
16 | this.initialDate = moment([2017, 3, 24]);
17 |
18 | }
19 | render() {
20 | return (
21 |
22 |
React Timeslot Calendar
23 | { this._customTimeslotSnippetRender() }
24 |
25 | );
26 | }
27 |
28 | _customTimeslotSnippetRender() {
29 | return (
30 |
31 |
Using Custom Timeslots and Callback
32 |
33 | {
42 | console.log('All Timeslots:');
43 | console.log(timeslots);
44 |
45 | console.log('Last selected timeslot:');
46 | console.log(lastSelected);
47 | } }
48 | />
49 |
50 | );
51 | }
52 |
53 |
54 | }
55 |
56 | ReactDOM.render(, document.getElementById('react-timeslot-calendar'));
57 |
--------------------------------------------------------------------------------
/src/js/demo/snippets/custom-timeslot.md:
--------------------------------------------------------------------------------
1 | ```js
2 | {
11 | // Do stuff with timeslots.
12 | console.log(lastSelected.startDate);
13 | }}
14 | />
15 | ```
16 |
--------------------------------------------------------------------------------
/src/js/react-timeslot-calendar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import '../styles/main.scss';
5 | import Calendar from './components/calendar.jsx';
6 | import {
7 | DEFAULT_TIMESLOTS,
8 | } from './constants/day.js';
9 |
10 |
11 | export default class ReactTimeslotCalendar extends React.Component {
12 |
13 | render() {
14 | return (
15 |
18 | );
19 | }
20 | }
21 |
22 | ReactTimeslotCalendar.defaultProps = {
23 | timeslots: DEFAULT_TIMESLOTS,
24 | };
25 |
26 |
27 | /**
28 | * @type {String} initialDate: The initial date in which to place the calendar. Must be MomentJS parseable.
29 | * @type {Array} timeslots: An array of timeslots to be displayed in each day.
30 | * @type {Object} timeslotProps: An object with keys and values for timeslot props (format, viewFormat)
31 | * @type {Array} selectedTimeslots: Initial value for selected timeslot inputs. Expects Dates formatted as Strings.
32 | * @type {Array} disabledTimeslots: Initial value for selected timeslot inputs. Expects: StartDate, EndDate, Format.
33 | * @type {Integer} maxTimexlots: maximum ammount of timeslots to select.
34 | * @type {Object} renderDays: An array of days which states which days of the week to render. By default renders all days.
35 | * @type {Object} startDateInputProps: properties for the startDate Inputs. Includes name, class, type (hidden, text...)
36 | * @type {Object} endDateInputProps: properties for the endDate Inputs. Includes name, class, type (hidden, text...)
37 | * @type {Object} onSelectTimeslot: Function which takes as parameters 1) The array of selected timeslots and 2) The latest selected timeslot.
38 | */
39 | ReactTimeslotCalendar.propTypes = {
40 | initialDate: PropTypes.string.isRequired,
41 | timeslots: PropTypes.array.isRequired,
42 | timeslotProps: PropTypes.object,
43 | selectedTimeslots: PropTypes.array,
44 | disabledTimeslots: PropTypes.array,
45 | maxTimeslots: PropTypes.number,
46 | renderDays: PropTypes.object,
47 | startDateInputProps: PropTypes.object,
48 | endDateInputProps: PropTypes.object,
49 | onSelectTimeslot: PropTypes.func,
50 | };
51 |
--------------------------------------------------------------------------------
/src/js/util/helpers.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | let helpers = {};
4 | export default helpers;
5 |
6 | helpers.getMomentFromCalendarJSDateElement = (dayElement) => {
7 | return moment([
8 | dayElement.year,
9 | dayElement.month - 1,
10 | dayElement.date,
11 | ]);
12 | };
13 |
--------------------------------------------------------------------------------
/src/js/util/markdown-snippet.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class MarkdownSnippet extends React.Component {
5 | render() {
6 | const {
7 | snippet,
8 | } = this.props;
9 |
10 | return (
11 |
12 | );
13 | }
14 | }
15 |
16 | MarkdownSnippet.propTypes = {
17 | snippet: PropTypes.string,
18 | };
19 |
--------------------------------------------------------------------------------
/src/styles/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lrojas94/react-timeslot-calendar/04d98812448b8e3f93400c59e05707feb3ba72b2/src/styles/.gitkeep
--------------------------------------------------------------------------------
/src/styles/abstract/_variables.scss:
--------------------------------------------------------------------------------
1 | $color-clouds: #ecf0f1;
2 | $color-main-orange: #FF6235;
3 | $color-main-black: #313232;
4 | $color-white: #FFFFFF;
5 | $default-fonts: 'Open Sans', sans-serif;
6 | /**
7 | * Timeslot Component variables:
8 | */
9 | $timeslot-default-bg-color: $color-white;
10 | $timeslot-selected-bg-color: #FF3E00;
11 | $timeslot-disabled-bg-color: #EEEDEB;
12 | $timeslot-default-color: $color-main-black;
13 | $timeslot-selected-color: $color-white;
14 | $timeslot-disabled-color: #C6C3BD;
15 | $timeslot-border-radius: 0.25em;
16 | $timeslot-margin: 0.8em;
17 | $timeslot-fonts: 'Open Sans', sans-serif;
18 |
19 | /**
20 | * Day Component variables
21 | */
22 | $day-default-color: $color-main-orange;
23 | $day-fonts: 'Open Sans', sans-serif;
24 |
25 | /**
26 | * Month Variables
27 | */
28 | $month-bg-color: #F2F1EF;
29 | /**
30 | * Calendar variables
31 | */
32 | $calendar-bg-color: #F2F1EF;
33 | $color-clouds: #ecf0f1;
34 |
--------------------------------------------------------------------------------
/src/styles/components/_calendar.scss:
--------------------------------------------------------------------------------
1 | .tsc-calendar {
2 | display: flex;
3 | flex: 1;
4 | flex-direction: column;
5 | color: $color-main-black;
6 | background: $calendar-bg-color;
7 | padding: 0.8em;
8 | }
9 |
10 | .tsc-calendar__actions {
11 | display: flex;
12 | flex: 1;
13 | border-bottom: 0.075em solid darken($color-clouds, 5%);
14 | }
15 |
16 | .tsc-calendar__action {
17 | display: flex;
18 | justify-content: center;
19 | align-items: center;
20 | font-weight: 700;
21 | color: $color-main-black;
22 | font-family: $default-fonts;
23 | text-align: center;
24 | }
25 |
26 | .tsc-calendar__action-element {
27 | font-size: 1.5em;
28 | padding: 0 1em;
29 | cursor: pointer;
30 | }
31 |
32 | .tsc-calendar__action-title {
33 | flex-grow: 2;
34 | padding: 1em;
35 | text-transform: uppercase;
36 | }
37 |
--------------------------------------------------------------------------------
/src/styles/components/_day.scss:
--------------------------------------------------------------------------------
1 | .tsc-day {
2 | display: flex;
3 | flex: 1;
4 | flex-direction: column;
5 | }
6 |
7 | .tsc-day__title {
8 | color: $day-default-color;
9 | font-family: $day-fonts;
10 | text-align: center;
11 | font-weight: 700;
12 | padding: 1em;
13 | text-transform: uppercase;
14 | }
15 |
--------------------------------------------------------------------------------
/src/styles/components/_month.scss:
--------------------------------------------------------------------------------
1 | .tsc-month {
2 | display: flex;
3 | flex-direction: column;
4 | background: $month-bg-color;
5 | }
6 |
7 | .tsc-month__actions {
8 | display: flex
9 | }
10 |
11 | .tsc-month__action {
12 | display: flex;
13 | justify-content: center;
14 | align-items: center;
15 | font-weight: 700;
16 | color: $day-default-color;
17 | font-family: $day-fonts;
18 | text-align: center;
19 | }
20 |
21 | .tsc-month__action-element {
22 | font-size: 1.5em;
23 | padding: 0 1em;
24 | cursor: pointer;
25 | }
26 |
27 | .tsc-month__action-title {
28 | flex-grow: 2;
29 | padding: 1em;
30 | }
31 |
--------------------------------------------------------------------------------
/src/styles/components/_timeslot.scss:
--------------------------------------------------------------------------------
1 | @mixin timeslotSelected {
2 | background-color: $timeslot-selected-bg-color;
3 | color: $timeslot-selected-color;
4 | }
5 |
6 | .tsc-timeslot {
7 | display: flex;
8 | flex: 1;
9 | justify-content: center;
10 | padding: 1em;
11 | font-size: 0.9em;
12 | background-color: $timeslot-default-bg-color;
13 | transition: background-color 0.5s, color 0.5s, box-shadow cubic-bezier(.25,.8,.25,1) 0.3s;
14 | border-radius: $timeslot-border-radius;
15 | text-align: center;
16 | margin: $timeslot-margin;
17 | font-family: $timeslot-fonts;
18 | color: $timeslot-default-color;
19 | box-shadow: 0 0.0625em 0.188em rgba(0,0,0,0.12), 0 0.0625em 0.125em rgba(0,0,0,0.24);
20 |
21 | &:not(.tsc-timeslot--disabled) {
22 | cursor: pointer;
23 | &:hover{
24 | @include timeslotSelected;
25 | box-shadow: 0 0.188em 0.563em rgba(0,0,0,0.25), 0 0.125em 0.125em rgba(0,0,0,0.22);
26 | }
27 | }
28 | }
29 |
30 | .tsc-timeslot--selected {
31 | @include timeslotSelected;
32 | }
33 |
34 | .tsc-timeslot--disabled {
35 | background-color: $timeslot-disabled-bg-color;
36 | color: $timeslot-disabled-color;
37 | }
38 |
--------------------------------------------------------------------------------
/src/styles/components/_week.scss:
--------------------------------------------------------------------------------
1 | .tsc-week {
2 | display: flex;
3 | flex: 1;
4 | flex-direction: row;
5 | align-items: stretch;
6 | }
7 |
8 | @media only screen and (max-width: 767px) {
9 | .tsc-week {
10 | flex-direction: column;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/styles/demo/main.scss:
--------------------------------------------------------------------------------
1 | @import "../abstract/variables";
2 | body, {
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | body,
8 | html,
9 | #react-timeslot-calendar, {
10 | min-height: 100%;
11 | }
12 |
13 | .app {
14 | background: $month-bg-color;
15 | font-family: $default-fonts;
16 | min-height: 100%;
17 | display: flex;
18 | flex-direction: column;
19 |
20 | h1, h3 {
21 | text-align: center;
22 | font-weight: 100;
23 | }
24 |
25 | h1 {
26 | color: $timeslot-selected-bg-color;
27 | padding-bottom: 0.5em;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/styles/main.scss:
--------------------------------------------------------------------------------
1 | @import "abstract/variables";
2 | /**
3 | * Components
4 | */
5 | @import "components/calendar";
6 | @import "components/month";
7 | @import "components/week";
8 | @import "components/day";
9 | @import "components/timeslot";
10 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackBundleSizeAnalyzerPlugin = require('webpack-bundle-size-analyzer').WebpackBundleSizeAnalyzerPlugin;
3 | const path = require('path');
4 |
5 | module.exports = {
6 | entry: './src/js/react-timeslot-calendar.jsx',
7 |
8 | output: {
9 | path: path.join(__dirname, './build'),
10 | filename: 'build.min.js',
11 | libraryTarget: 'umd',
12 | },
13 | plugins: [
14 | new WebpackBundleSizeAnalyzerPlugin('./reports/plain-report.txt'),
15 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
16 | new webpack.DefinePlugin({
17 | 'process.env': {
18 | 'NODE_ENV': JSON.stringify('production'),
19 | },
20 | }),
21 | ],
22 | module: {
23 | loaders: [
24 | {
25 | test: /\.jsx?$/,
26 | exclude: /node_modules/,
27 | loader: 'babel-loader',
28 | query: {
29 | presets: ['react', 'es2015'],
30 | },
31 | },
32 | {
33 | test: /\.scss$/,
34 | use: [{
35 | loader: 'style-loader', // creates style nodes from JS strings
36 | }, {
37 | loader: 'css-loader', // translates CSS into CommonJS
38 | }, {
39 | loader: 'sass-loader', // compiles Sass to CSS
40 | }],
41 | },
42 | ],
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/webpack.demo.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | const path = require('path');
3 | const hljs = require('highlight.js');
4 | const marked = require('marked');
5 |
6 | const renderer = new marked.Renderer();
7 | renderer.code = function(code, language){
8 | return '' +
9 | hljs.highlight(language, code).value +
10 | '
';
11 | };
12 |
13 |
14 | module.exports = {
15 |
16 | entry: './src/js/demo/app.jsx',
17 |
18 | output: {
19 | filename: './public/build.js',
20 | libraryTarget: 'umd',
21 | },
22 | plugins: [
23 | new webpack.DefinePlugin({
24 | 'process.env': {
25 | 'NODE_ENV': JSON.stringify('production'),
26 | },
27 | }),
28 | ],
29 | module: {
30 | loaders: [
31 | {
32 | test: /\.jsx$/,
33 | exclude: /node_modules/,
34 | loader: 'babel-loader',
35 | query: {
36 | presets: ['react', 'es2015'],
37 | },
38 | },
39 | {
40 | test: /\.scss$/,
41 | use: [{
42 | loader: 'style-loader', // creates style nodes from JS strings
43 | }, {
44 | loader: 'css-loader', // translates CSS into CommonJS
45 | }, {
46 | loader: 'sass-loader', // compiles Sass to CSS
47 | }],
48 | },
49 | {
50 | test: /\.md$/,
51 | use: [{
52 | loader: 'html-loader',
53 | }, {
54 | loader: 'markdown-loader',
55 | options: {
56 | renderer,
57 | },
58 | }],
59 | },
60 | ],
61 | },
62 | };
63 |
--------------------------------------------------------------------------------