diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index db4bd8305..21a24e280 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -10,7 +10,7 @@ A clear and concise description of what the bug is.
_Please provide one or both of the following:_
-Code (If you have a branch somewhere that shows your issue) or link to codesandbox example. [Here's a base template for this library](https://codesandbox.io/s/lyjr9p0119):
+Code (If you have a branch somewhere that shows your issue) or link to codesandbox example. [Here's a base template for this library](https://codesandbox.io/s/w6xvqzno4w):
OR
diff --git a/.gitignore b/.gitignore
index 71054179f..2db738b4f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,8 @@ bower_components
modules
build
gh-pages
-lib
+/lib
+package-lock.json
# vscode stuff
.vscode
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97cb27c0e..a83167594 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,61 @@ and this project adheres (more or less) to [Semantic Versioning](http://semver.o
## Unreleased
+## 0.23.0
+
+- improve unit tests coverage #426 - @ilaiwi
+- stack items by group #384 - @acemac
+- fix bug where `canMove` prop gets ignored #484 - @acemac + @ilaiwi
+- fix sidebar re-render when groupHeights do not change #478 - @SDupZ
+
+### Stack per group
+
+now you can stack choose to stack items in individual groups by providing the property `stackItems` in group object. The property in group overrides the timeline prop `stackItems`.
+
+```
+const groups = [{ id: 1, title: 'group 1', stackItems: false }, { id: 2, title: 'group 2', stackItems: true }]
+
+const items = [
+ {
+ id: 1,
+ group: 1,
+ title: 'item 1',
+ start_time: moment(),
+ end_time: moment().add(1, 'hour')
+ },
+ {
+ id: 2,
+ group: 2,
+ title: 'item 2',
+ start_time: moment().add(-0.5, 'hour'),
+ end_time: moment().add(0.5, 'hour')
+ },
+ {
+ id: 3,
+ group: 1,
+ title: 'item 3',
+ start_time: moment().add(2, 'hour'),
+ end_time: moment().add(3, 'hour')
+ }
+]
+
+ReactDOM.render(
+
+ Rendered by react!
+
+
,
+ document.getElementById('root')
+)
+```
+
+
+## 0.22.0
+
### Fixed
* Provided a new key `groupLabelKey` to allow splitting of the key used to render the Sidebar and the InfoLabel visible during drag operations. `groupTitleKey` continues to be used to render the Sidebar. #442 @thiagosatoshi
diff --git a/README.md b/README.md
index 5ad25df48..9f06b30a7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# React Calendar Timeline
-A modern and responsive react timeline component.
+A modern and responsive React timeline component.

@@ -16,7 +16,7 @@ yarn add react-calendar-timeline
npm install --save react-calendar-timeline
```
-`react-calendar-timeline` has `react`, `react-dom`, [`moment`](http://momentjs.com/) and [`interactjs`](http://interactjs.io/docs/) as peer dependencies.
+`react-calendar-timeline` has [react](https://reactjs.org/), [react-dom](https://reactjs.org/docs/react-dom.html), [`moment`](http://momentjs.com/) and [`interactjs`](http://interactjs.io/docs/) as peer dependencies.
# Usage
@@ -83,12 +83,12 @@ Expects either a vanilla JS array or an immutableJS array, consisting of objects
id: 1,
title: 'group 1',
rightTitle: 'title in the right sidebar',
- stackItems?: true,
+ stackItems?: false or 'space' or 'line',
height?: 30
}
```
-If you use right sidebar, you can pass optional `rightTitle` property here.
+If you use the right sidebar, you can pass optional `rightTitle` property here.
If you want to overwrite the calculated height with a custom height, you can pass a `height` property as an int in pixels here. This can be very useful for categorized groups.
## items
@@ -118,7 +118,7 @@ Expects either a vanilla JS array or an immutableJS array, consisting of objects
}
```
-The preferred (fastest) option is to give unix timestamps in milliseconds for `start_time` and `end_time`. Objects that convert to them (JavaScript Date or moment()) will also work, but will be a lot slower.
+The preferred (fastest) option is to give Unix timestamps in milliseconds for `start_time` and `end_time`. Objects that convert to them (JavaScript `Date` or `moment()`) will also work, but will be a lot slower.
## defaultTimeStart and defaultTimeEnd
@@ -126,7 +126,7 @@ Unless overridden by `visibleTimeStart` and `visibleTimeEnd`, specify where the
## visibleTimeStart and visibleTimeEnd
-The exact viewport of the calendar. When these are specified, scrolling in the calendar must be orchestrated by the `onTimeChange` function. This parameter expects a unix timestamp in milliseconds.
+The exact viewport of the calendar. When these are specified, scrolling in the calendar must be orchestrated by the `onTimeChange` function. This parameter expects a Unix timestamp in milliseconds.
**Note that you need to provide either `defaultTimeStart/End` or `visibleTimeStart/End` for the timeline to function**
@@ -179,8 +179,7 @@ The minimum width, in pixels, of a timeline entry when it's possible to resize.
## stickyOffset
-At what height from the top of the screen should we start "sticking" the header (i.e. position: sticky)? This is useful if for example you already have
-a sticky navbar and want to push the timeline header down further. Defaults `0`.
+At what height from the top of the screen should we start "sticking" the header (i.e. position: sticky)? This is useful if for example you already have a sticky navbar and want to push the timeline header down further. Defaults `0`.
## stickyHeader
@@ -216,7 +215,7 @@ Largest time the calendar can zoom to in milliseconds. Default `5 * 365.24 * 864
## clickTolerance
-How many pixels we can drag the background for it to be counted as a click on the background. Defualt: `3`
+How many pixels we can drag the background for it to be counted as a click on the background. Default `3`
## canMove
@@ -238,6 +237,11 @@ Append a special `.rct-drag-right` handle to the elements and only resize if dra
Stack items under each other, so there is no visual overlap when times collide. Can be overridden in the `groups` array. Defaults to `false`.
+can be assigned to
+- false
+- space (saves space in stacking)
+- line (stack each item in a line)
+
## traditionalZoom
Zoom in when scrolling the mouse up/down. Defaults to `false`
@@ -332,6 +336,11 @@ function (action, item, time, resizeEdge) {
}
```
+## onUpdateMove(itemId, time, newGroup, action,resizeEdge)
+
+this function is called after moveResizeValidator on every drag update
+
+
## headerLabelFormats and subHeaderLabelFormats
The formats passed to moment to render times in the header and subheader. Defaults to these:
@@ -435,9 +444,9 @@ Called when the bounds in the calendar's canvas change. Use it for example to lo
## itemRenderer
-Render prop function used to render a customized item. The function provides multiple paramerters that can be used to render each item.
+Render prop function used to render a customized item. The function provides multiple parameters that can be used to render each item.
-Paramters provided to the function has two types: context params which have the state of the item and timeline, and prop getters functions
+Parameters provided to the function has two types: context params which have the state of the item and timeline, and prop getters functions
#### Render props params
@@ -468,10 +477,12 @@ Paramters provided to the function has two types: context params which have the
| `selected` | `boolean` | returns if the item is selected. |
| `dragging` | `boolean` | returns if the item is being dragged |
| `dragStart` | `object` | returns `x` and `y` of the start dragging point of the item. |
+| `dragTime` | `number` | current drag time. |
| `dragGroupDelta` | `number` | returns number of groups the item moved. if negative, moving was to top. If positive, moving was to down |
| `resizing` | `boolean` | returns if the item is being resized. |
| `resizeEdge` | `left`, `right` | the side from which the component is being resized form |
| `resizeStart` | `number` | returns the x value from where the component start moving |
+| `resizeTime` | `number` | current resize time |
| `width` | `boolean` | returns the width of the item (same as in dimensions) |
##### prop getters functions
@@ -488,7 +499,7 @@ Rather than applying props on the element yourself and to avoid your props being
* `getItemProps` returns the props you should apply to the root item element. The returned props are:
* key: item id
- * ref: function to get item referance
+ * ref: function to get item reference
* className: classnames to be applied to the item
* onMouseDown: event handler
* onMouseUp: event handler
@@ -498,9 +509,9 @@ Rather than applying props on the element yourself and to avoid your props being
* onContextMenu: event handler
* style: inline object style
- \*\* _the given styles will only override the styles that are not a requirement for postioning the item. Other styles like `color`, `radius` and others_
+ \*\* _the given styles will only override the styles that are not a requirement for positioning the item. Other styles like `color`, `radius` and others_
- These properties can be override using the prop argument with proprties:
+ These properties can be override using the prop argument with properties:
* className: class names to be added
* onMouseDown: event handler will be called after the component's event handler
@@ -709,10 +720,10 @@ const twoSeconds = 2000
{({ styles, date }) =>
- // date is value of current date. Use this to render special styles for the marker
- // or any other custom logic based on date:
- // e.g. styles = {...styles, backgroundColor: isDateInAfternoon(date) ? 'red' : 'limegreen'}
- return
+ // date is value of current date. Use this to render special styles for the marker
+ // or any other custom logic based on date:
+ // e.g. styles = {...styles, backgroundColor: isDateInAfternoon(date) ? 'red' : 'limegreen'}
+
}
```
@@ -735,9 +746,7 @@ const today = Date.now()
//custom renderer
- {({ styles, date }) =>
- return
- }
+ {({ styles, date }) => }
// multiple CustomMarkers
@@ -767,10 +776,10 @@ Custom renderer for this marker. Ensure that you always pass `styles` to the roo
//custom renderer
{({ styles, date }) =>
- // date is value of current date. Use this to render special styles for the marker
- // or any other custom logic based on date:
- // e.g. styles = {...styles, backgroundColor: isDateInAfternoon(date) ? 'red' : 'limegreen'}
- return
+ // date is value of current date. Use this to render special styles for the marker
+ // or any other custom logic based on date:
+ // e.g. styles = {...styles, backgroundColor: isDateInAfternoon(date) ? 'red' : 'limegreen'}
+
}
```
@@ -1318,7 +1327,7 @@ You need to include the `Timeline.css` file, either via static file reference or
## How can I have items with different colors?
-Now you can use item renderer for rendering items with different colors [itemRenderer](https://github.com/namespace-ee/react-calendar-timeline#itemrenderer).
+Now you can use item renderer for rendering items with different colors [itemRenderer](https://github.com/namespace-ee/react-calendar-timeline#itemrenderer).
Please refer to [examples](https://github.com/namespace-ee/react-calendar-timeline/tree/master/examples#custom-item-rendering) for a sandbox example
## How can I add a sidebar on the right?
@@ -1450,3 +1459,6 @@ npm version patch
```
-->
+
+## License
+[MIT licensed](/LICENSE.md).
diff --git a/__fixtures__/itemsAndGroups.js b/__fixtures__/itemsAndGroups.js
index 0ad6371d2..f98b3810b 100644
--- a/__fixtures__/itemsAndGroups.js
+++ b/__fixtures__/itemsAndGroups.js
@@ -1,19 +1,18 @@
-import moment from 'moment'
export const items = [
{
id: '0',
group: '1',
- start_time: moment('2018-10-26T10:46:40.000').valueOf(),
- end_time: moment('2018-10-26T12:40:03.877').valueOf(),
+ start_time: 1540540000000,
+ end_time: 1540546803877,
canMove: false,
canResize: false
},
{
id: '5',
group: '1',
- start_time: moment('2018-10-26T08:46:40.000').valueOf(),
- end_time: moment('2018-10-26T14:40:03.877').valueOf(),
+ start_time: 1540532800000,
+ end_time: 1540554003877,
canMove: false,
canResize: false,
className: ''
@@ -21,8 +20,8 @@ export const items = [
{
id: '6',
group: '1',
- start_time: moment('2018-10-26T13:46:40.000').valueOf(),
- end_time: moment('2018-10-26T20:40:03.877').valueOf(),
+ start_time: 1540550800000,
+ end_time: 1540575603877,
canMove: false,
canResize: false,
className: ''
@@ -30,16 +29,16 @@ export const items = [
{
id: '1',
group: '1',
- start_time: moment('2018-10-26T19:06:40.000').valueOf(),
- end_time: moment('2018-10-26T23:14:35.919').valueOf(),
+ start_time: 1540570000000,
+ end_time: 1540584875919,
canMove: true,
canResize: 'both'
},
{
id: '2',
group: '1',
- start_time: moment('2018-10-27T08:00:00.000').valueOf(),
- end_time: moment('2018-10-27T13:39:57.548').valueOf(),
+ start_time: 1540620000000,
+ end_time: 1540640397548,
canMove: false,
canResize: false,
className: ''
@@ -47,8 +46,8 @@ export const items = [
{
id: '3',
group: '3',
- start_time: moment('2018-10-27T18:00:00.000').valueOf(),
- end_time: moment('2018-10-27T23:39:57.548').valueOf(),
+ start_time: 1540656000000,
+ end_time: 1540676397548,
canMove: false,
canResize: false,
className: ''
diff --git a/__fixtures__/stateAndProps.js b/__fixtures__/stateAndProps.js
index c343c4905..6427947ec 100644
--- a/__fixtures__/stateAndProps.js
+++ b/__fixtures__/stateAndProps.js
@@ -1,10 +1,9 @@
import { defaultKeys } from 'lib/default-config'
-import moment from 'moment'
import {items} from './itemsAndGroups'
export const props = {
keys: defaultKeys,
lineHeight: 30,
- stackItems: true,
+ stackItems: 'space',
itemHeightRatio: 0.75
}
@@ -13,8 +12,8 @@ export const propsNoStack = {
stackItems: false,
}
-export const visibleTimeStart = moment('2018-10-26T00:00:00.000')
-export const visibleTimeEnd = moment('2018-10-27T00:00:00.000')
+export const visibleTimeStart = 1540501200000
+export const visibleTimeEnd = 1540587600000
export const state = {
draggingItem: undefined,
@@ -23,9 +22,9 @@ export const state = {
resizingEdge: null,
resizeTime: null,
newGroupOrder: null,
- canvasTimeStart: moment('2018-10-25T00:00:00.000').valueOf(),
- visibleTimeEnd: visibleTimeEnd.valueOf(),
- visibleTimeStart: visibleTimeStart.valueOf(),
+ canvasTimeStart: 1540414800000,
+ visibleTimeEnd: visibleTimeEnd,
+ visibleTimeStart: visibleTimeStart,
canvasTimeEnd: 1540674000000,
width: 1000,
}
diff --git a/__tests__/components/Headers/CustomHeader.test.js b/__tests__/components/Headers/CustomHeader.test.js
new file mode 100644
index 000000000..06883ce1f
--- /dev/null
+++ b/__tests__/components/Headers/CustomHeader.test.js
@@ -0,0 +1,242 @@
+import React from 'react'
+import { render, cleanup, prettyDOM } from 'react-testing-library'
+import Timeline from 'lib/Timeline'
+import DateHeader from 'lib/headers/DateHeader'
+import SidebarHeader from 'lib/headers/SidebarHeader'
+import TimelineHeaders from 'lib/headers/TimelineHeaders'
+import CustomHeader from 'lib/headers/CustomHeader'
+
+import 'jest-dom/extend-expect'
+import moment from 'moment'
+
+import { items, groups } from '../../../__fixtures__/itemsAndGroups'
+import { visibleTimeEnd, visibleTimeStart } from '../../../__fixtures__/stateAndProps'
+
+const defaultProps = {
+ groups,
+ items,
+ defaultTimeStart: moment(visibleTimeStart, 'x'),
+ defaultTimeEnd: moment(visibleTimeEnd, 'x'),
+}
+
+describe('CustomHeader Component Test', () => {
+
+ afterEach(cleanup)
+ // Render The Example In The Docs
+ it('Given CustomHeader When render Then it should renderd Correctly in the timeline', () => {
+ const { getByTestId } = render(
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+
+
+ {({
+ headerContext: { intervals },
+ getRootProps,
+ getIntervalProps,
+ showPeriod
+ }) => {
+ return (
+
+ {intervals.map(interval => {
+ const intervalStyle = {
+ // height: 30,
+ lineHeight: '30px',
+ textAlign: 'center',
+ borderLeft: '1px solid black',
+ cursor: 'pointer',
+ backgroundColor: 'Turquoise',
+ color: 'white'
+ }
+ return (
+
{
+ showPeriod(interval.startTime, interval.endTime)
+ }}
+ {...getIntervalProps({
+ interval,
+ style: intervalStyle
+ })}
+ >
+
+ {interval.startTime.format('YYYY')}
+
+
+ )
+ })}
+
+ )
+ }}
+
+
+
+ )
+
+ expect(getByTestId('customHeader')).toBeInTheDocument()
+ })
+ it('Given CustomHeader When pass a unit to it Then it should take it', () => {
+ const { getByTestId, rerender } = render(getCustomHeadersInTimeline({ unit: "year" }));
+ expect(getByTestId('customHeader')).toHaveTextContent('01/01/2018')
+
+ rerender(getCustomHeadersInTimeline({ unit: "month" }));
+ expect(getByTestId('customHeader')).toHaveTextContent('10/01/2018')
+
+ rerender(getCustomHeadersInTimeline({ unit: "day" }));
+ expect(getByTestId('customHeader')).toHaveTextContent('10/25/2018')
+ expect(getByTestId('customHeader')).toHaveTextContent('10/26/2018')
+ expect(getByTestId('customHeader')).toHaveTextContent('10/27/2018')
+ })
+ it('Given CustomHeader When pass a style props with (width, position) Then it should not ovverride the default values', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({ props: { style: { width: 0, position: 'fixed' } } }));
+ const { width, position } = getComputedStyle(getByTestId('customHeader'))
+ expect(width).not.toBe('0px')
+ expect(position).not.toBe('fixed')
+ })
+
+ it('Given CustomHeader When pass a style props other than (width, position) Then it should renderd Correctly', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({ props: { style: { height: 150 } } }));
+ const { height } = getComputedStyle(getByTestId('customHeader'))
+ expect(height).toBe("150px")
+ })
+
+ it('Given CustomHeader When pass an interval style with (width, position, left) Then it should not ovverride the default values', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({
+ intervalStyle: {
+ width: 0,
+ position: 'fixed',
+ left: 0
+ }
+ }));
+ const { width, position, left } = getComputedStyle(getByTestId('customHeaderInterval'))
+ expect(width).not.toBe('0px')
+ expect(position).not.toBe('fixed')
+ expect(left).not.toBe('0px')
+ })
+ it('Given CustomHeader When pass an interval style other than (width, position) Then it should rendered correctly', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({
+ intervalStyle: {
+ lineHeight: '30px',
+ textAlign: 'center',
+ borderLeft: '1px solid black',
+ cursor: 'pointer',
+ color: 'white'
+ }
+ }));
+ const { lineHeight, textAlign, borderLeft, cursor, color } = getComputedStyle(getByTestId('customHeaderInterval'))
+ expect(lineHeight).toBe('30px')
+ expect(textAlign).toBe('center')
+ expect(borderLeft).toBe('1px solid black')
+ expect(cursor).toBe('pointer')
+ expect(color).toBe('white')
+ })
+ it('Given a CustomHeader When pass a jsx as a children Then it Should be rendered Correctly', () => {
+ const { getByText } = render(getCustomHeadersInTimeline())
+ expect(getByText('Should Be Rendered')).toBeInTheDocument()
+ })
+ it('Given a CustomHeader When not pass any unit prop Then it Should take the default timeline unit (year)', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline())
+ expect(getByTestId('customHeader')).toHaveTextContent('01/01/2018')
+ })
+ it('Given DateHeader When Rendered with day unit Then getIntervalStyle function shuold be act correctly', () => {
+ const props = getIntervalStyle({ startTime: moment("2018-10-27T21:00:00.000"), canvasTimeStart: 1540414800000, ratio: 0.5, unit: 'month', labelWidth: 150 })
+ expect(props).toEqual({
+ left: 124200000,
+ width: 150,
+ position: 'absolute'
+ })
+ })
+ it('Given CustomHeader When pass a style in props obj to props renderer Then it should not override correctly render it', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({props: {'aria-hidden': false}}))
+ expect(getByTestId('customHeader')).toHaveAttribute('aria-hidden')
+
+ })
+ describe('Testing Diffrent Unit Values', () => {
+ it('Given CustomHeader When pass a year unit then it should render it correctly', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({ unit: 'year' }));
+ expect(getByTestId('customHeader')).toHaveTextContent('01/01/2018')
+ })
+ it('Given CustomHeader When pass a month unit then it should render it correctly', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({ unit: 'month' }));
+ expect(getByTestId('customHeader')).toHaveTextContent('10/01/2018')
+ })
+ it('Given CustomHeader When pass a day unit then it should render it correctly', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({ unit: 'day' }));
+ expect(getByTestId('customHeader')).toHaveTextContent('10/25/2018')
+ })
+ it('Given CustomHeader When pass a hour unit then it should render it correctly', () => {
+ const { getByTestId } = render(getCustomHeadersInTimeline({ unit: 'hour' }));
+ expect(getByTestId('customHeader')).toHaveTextContent('10/25/2018')
+ })
+ })
+
+})
+
+function getCustomHeadersInTimeline({ unit = "year", props, intervalStyle } = {}) {
+
+ return (
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+
+
+ {({
+ headerContext: { intervals },
+ getRootProps,
+ getIntervalProps,
+ showPeriod
+ }, extraProps) => {
+ return (
+
+ {intervals.map(interval => {
+ return (
+
{
+ showPeriod(interval.startTime, interval.endTime)
+ }}
+ {...getIntervalProps({
+ interval,
+ style: intervalStyle
+ })}
+ >
+
+ {interval.startTime.format('MM/DD/YYYY')}
+
+
+ )
+ })}
+
+ Should Be Rendered
+
+
+
+ )
+ }}
+
+
+
+ )
+}
+
+
+const getIntervalStyle = ({ startTime, canvasTimeStart, ratio, unit, labelWidth, style }) => {
+ const left = Math.round((startTime.valueOf() - canvasTimeStart) * ratio)
+ const unitValue = startTime.get(unit === 'day' ? 'date' : unit)
+ const firstOfType = unitValue === (unit === 'day' ? 1 : 0)
+ const leftCorrect = firstOfType ? 1 : 0
+ return {
+ ...style,
+ left: left - leftCorrect,
+ width: labelWidth,
+ position: 'absolute'
+ }
+}
diff --git a/__tests__/components/Headers/DateHeader.test.js b/__tests__/components/Headers/DateHeader.test.js
new file mode 100644
index 000000000..857d2dbf0
--- /dev/null
+++ b/__tests__/components/Headers/DateHeader.test.js
@@ -0,0 +1,401 @@
+import React from 'react'
+import { render, cleanup, within } from 'react-testing-library'
+import Timeline from 'lib/Timeline'
+import DateHeader from 'lib/headers/DateHeader'
+import SidebarHeader from 'lib/headers/SidebarHeader'
+import TimelineHeaders from 'lib/headers/TimelineHeaders'
+import 'jest-dom/extend-expect'
+import moment from 'moment'
+import { items, groups } from '../../../__fixtures__/itemsAndGroups'
+import { visibleTimeEnd, visibleTimeStart } from '../../../__fixtures__/stateAndProps'
+import { TimelineStateProvider } from 'lib/timeline/TimelineStateContext'
+import { TimelineHeadersProvider } from 'lib/headers/HeadersContext'
+const defaultProps = {
+ groups,
+ items,
+ defaultTimeStart: moment(visibleTimeStart, 'x'),
+ defaultTimeEnd: moment(visibleTimeEnd, 'x'),
+}
+
+describe("Testing DateHeader Component", () => {
+ beforeEach(() => {
+
+ Element.prototype.getBoundingClientRect = jest.fn(() => {
+ return {
+ width: 1000,
+ height: 120,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ }
+ });
+ })
+ afterEach(cleanup)
+
+ // Testing The Example In The Docs
+ it("Given DateHeader When rendered Then it should be rendered correctly in the timeLine", () => {
+ const { getAllByTestId } = render(
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+
+ {
+ return
+ {intervalContext.intervalText}
+
+ }}
+ />
+
+
+ )
+
+ expect(getAllByTestId('dateHeader')).toHaveLength(3)
+
+ })
+
+ it("Given Dateheader When pass a string typed labelFormat Then it should render the intervals with hte given format", () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ unit: "day", labelFormat: "MM/DD" }));
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/25')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/26')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/27')
+ expect(getAllByTestId('dateHeader')[0]).toHaveTextContent('Thursday, October 25')
+
+ })
+
+ it("Given Dateheader When pass an object typed labelFormat Then it should render the intervals with hte given format", () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ unit: "day", labelFormat: { day: { long: "MM/DD/YYYY" } } }));
+
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/25/2018')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/26/2018')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/27/2018')
+ expect(getAllByTestId('dateHeader')[0]).toHaveTextContent('Thursday, October 25')
+ })
+ it("Given Dateheader When pass a function typed labelFormat Then it should render the intervals with hte given format", () => {
+ const formatlabel = jest.fn((interval, unit, labelWidth) => interval[0].format("MM/DD/YYYY"))
+ const { getAllByTestId } = render(dateHeaderComponent({ unit: "day", labelFormat: formatlabel }));
+
+ expect(formatlabel).toHaveBeenCalled()
+
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/25/2018')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/26/2018')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/27/2018')
+
+ expect(getAllByTestId('dateHeader')[0]).toHaveTextContent('Thursday, October 25')
+
+ })
+
+ it("Given Dateheader When pass a string typed labelFormat Then it should be called with the right params", () => {
+ const formatlabel = jest.fn((interval, unit, labelWidth) => interval[0].format("MM/DD/YYYY"))
+ render(dateHeaderComponent({ unit: "day", labelFormat: formatlabel }))
+ expect(formatlabel).toHaveBeenCalled()
+ expect(formatlabel).toHaveBeenCalledWith(expect.any(Array), "day", expect.any(Number))
+ })
+
+
+
+ it("Given Dateheader When click on the primary header Then it should change the unit", async () => {
+
+ const formatlabel = jest.fn((interval, unit, labelWidth) => interval[0].format("MM/DD/YYYY"))
+ const { getByTestId, getAllByTestId } = render(dateHeaderComponent({ unit: "day", labelFormat: formatlabel }));
+ // Arrange
+ const primaryHeader = getByTestId('dateHeader')
+ const seconderyHeader = getAllByTestId('dateHeader')[2]
+
+ // Act
+ const primaryFirstClick = within(primaryHeader).getByText('Friday, October 26, 2018').parentElement
+ primaryFirstClick.click()
+ const primarySecondClick = within(primaryHeader).getByText('October 2018').parentElement
+ primarySecondClick.click()
+
+ // Assert
+ expect(seconderyHeader).toHaveTextContent('January')
+ expect(primaryHeader).toHaveTextContent('2018')
+ })
+
+ it("Given Dateheader When click on the secondary header Then it should change the unit", async () => {
+
+ const formatlabel = jest.fn((interval, unit, labelWidth) => interval[0].format("MM/DD/YYYY"))
+ const { getByTestId, getAllByTestId } = render(dateHeaderComponent({ unit: "day", labelFormat: formatlabel }));
+ // Arrange
+ const primaryHeader = getByTestId('dateHeader')
+ const seconderyHeader = getAllByTestId('dateHeader')[2]
+
+ const primaryFirstClick = within(primaryHeader).getByText('Friday, October 26, 2018').parentElement
+ primaryFirstClick.click()
+ const primarySecondClick = within(primaryHeader).getByText('October 2018').parentElement
+
+ primarySecondClick.click()
+
+ // Act
+ const secondaryFirstClick = within(seconderyHeader).queryByText('January')
+ secondaryFirstClick.click()
+ expect(primaryHeader).toHaveTextContent('December 2016')
+ const secondarySecondClick = within(seconderyHeader).queryByText('1')
+ secondarySecondClick.click()
+ expect(primaryHeader).toHaveTextContent('Wednesday, November 30, 2016')
+ })
+
+ it('Given DateHeadr When click on primary or secondary Then onTimeChange function should be called with the right params', () => {
+ const handleTimeChange = jest.fn((visibleTimeStart, visibleTimeEnd, updateScrollCanvas) =>
+ updateScrollCanvas(visibleTimeStart, visibleTimeEnd))
+ const { getByTestId, getAllByTestId, debug } = render(dateHeaderComponent({ unit: "day", handleTimeChange: handleTimeChange }));
+ const primaryHeader = within(getByTestId('dateHeader')).getByText('Friday, October 26, 2018').parentElement
+ primaryHeader.click()
+ const secondaryHeader = within(getAllByTestId('dateHeader')[1]).getByText('1').parentElement
+ expect(handleTimeChange).toBeCalled()
+ expect(handleTimeChange).toBeCalledTimes(1)
+ expect(handleTimeChange).toBeCalledWith(expect.any(Number), expect.any(Number), expect.any(Function))
+ expect(handleTimeChange).toReturn()
+
+ secondaryHeader.click();
+ expect(handleTimeChange).toBeCalled()
+ expect(handleTimeChange).toBeCalledTimes(1)
+ expect(handleTimeChange).toBeCalledWith(expect.any(Number), expect.any(Number), expect.any(Function))
+ expect(handleTimeChange).toReturn()
+ })
+
+ it('Given Dateheader When pass a className Then it should be applied to DateHeader', () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ labelFormat: "MM/DD/YYYY", className: 'test-class-name' }));
+ expect(getAllByTestId('dateHeader')[1]).toHaveClass('test-class-name')
+ })
+
+ it('Given Interval When pass an ovveride values for (width, left, position) it should not ovverride the default values', () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ labelFormat: "MM/DD/YYYY", props: { style: { width: 100, position: 'fixed' } } }));
+ const { width, position } = getComputedStyle(getAllByTestId('interval')[0])
+ expect(width).not.toBe('100px')
+ expect(position).not.toBe('fixed')
+ })
+
+ it('Given Interval When pass an override (width, position) Then it should render the default values for it', () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ labelFormat: "MM/DD/YYYY", props: { style: { width: 100, position: 'fixed' } } }));
+ const { width, position } = getComputedStyle(getAllByTestId('interval')[0])
+ expect(width).toBe('36px')
+ expect(position).toBe('absolute')
+
+
+ })
+ it('Given Interval When pass any style other than (position, width, left) through the Dateheader Then it should take it', () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ labelFormat: "MM/DD/YYYY", props: { style: { display: 'flex' } } }));
+ const { display } = getComputedStyle(getAllByTestId('interval')[0])
+
+ expect(display).toBe('flex')
+
+ })
+ it('Given unit Dateheader When pass a style Object Then it should render the given style correctly', () => {
+ const { getAllByTestId } = render(dateHeaderComponent({ style: { height: 50 }, labelFormat: "MM/DD/YYYY" }));
+ const { height } = getComputedStyle(getAllByTestId('dateHeader')[1])
+
+ expect(height).toBe('50px')
+ })
+
+ it('Given DateHeader component When pass an intervalRenderer prop then it should be called with the right params', () => {
+ const intervalRenderer = jest.fn(({ getIntervalProps, intervalContext }, props) => {intervalContext.intervalText}
)
+ const { getByTestId, rerender } = render(dateHeaderWithIntervalRenderer({ intervalRenderer: intervalRenderer }))
+ const bluePrint = {
+ getIntervalProps: expect.any(Function),
+ intervalContext: expect.any(Object)
+ }
+ expect(intervalRenderer).toBeCalled()
+ expect(intervalRenderer).toReturn()
+ // because we did not pass a props then the function will called with undefined props
+ expect(intervalRenderer).toBeCalledWith(expect.objectContaining(bluePrint), undefined)
+ rerender(dateHeaderWithIntervalRenderer({ intervalRenderer: intervalRenderer, props: { style: { height: 50 } } }))
+ expect(intervalRenderer).toBeCalledWith(expect.objectContaining(bluePrint), expect.any(Object))
+ expect(getByTestId('myAwesomeInterval')).toBeInTheDocument()
+ })
+ describe('Testing The Label Format Diffrent Cases', () => {
+ it('Given DateHeader When resize the width of the screen to 1000 Then it Should take the long format', () => {
+ Element.prototype.getBoundingClientRect = jest.fn(() => ({ width: 1000 }));
+ const { getAllByTestId, rerender } = render(dateHeaderComponent({
+ unit: "day", labelFormat: {
+ day: { short: "DD", medium: "DD/MM", mediumLong: "MM/YYYY", long: "MM/DD/YYYY", }
+ }
+ }));
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/26/2018')
+ })
+
+ it('Given DateHeader When resize the width of the screen to 250 Then it Should take the mediumLong format', () => {
+ Element.prototype.getBoundingClientRect = jest.fn(() => ({ width: 250 }));
+ const { getAllByTestId } = render(dateHeaderComponent({
+ unit: "day", labelFormat: {
+ day: { short: "DD", medium: "DD/MM", mediumLong: "MM/YYYY", long: "MM/DD/YYYY", }
+ }
+ }));
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/2018')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/2018')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('10/2018')
+ })
+ it('Given DateHeader When resize the width of the screen to 200 Then it Should take the medium format', () => {
+ Element.prototype.getBoundingClientRect = jest.fn(() => ({ width: 200 }));
+ const { getAllByTestId } = render(dateHeaderComponent({
+ unit: "day", labelFormat: {
+ day: { short: "DD", medium: "DD/MM", mediumLong: "MM/YYYY", long: "MM/DD/YYYY", }
+ }
+ }));
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('25/10')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('26/10')
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('27/10')
+ })
+ it('Given DateHeader When resize the width of the screen to 100 Then it Should take the short format', () => {
+ Element.prototype.getBoundingClientRect = jest.fn(() => ({ width: 100 }));
+ const { getAllByTestId } = render(dateHeaderComponent({
+ unit: "day", labelFormat: {
+ day: { short: "DD", medium: "DD/MM", mediumLong: "MM/YYYY", long: "MM/DD/YYYY", }
+ }
+ }));
+ expect(getAllByTestId('dateHeader')[1]).toHaveTextContent('26')
+ })
+ })
+
+ describe('Testing Diffrent Unit Values', () => {
+ it('Given DateHeader When pass a year unit to the timeline then it should take it as default', () => {
+ const children =
+ const {getAllByTestId} = renderDateHeaderWithContext({unit: 'year', children: children});
+ const primaryHeader = getAllByTestId('dateHeader')[0]
+ const secondaryHeader = getAllByTestId('dateHeader')[1]
+ expect(primaryHeader).toHaveTextContent('2019')
+ expect(secondaryHeader).toHaveTextContent('2019')
+ })
+ it('Given DateHeader When pass a month unit to the timeline then it should take it as default', () => {
+ const children =
+ const {getAllByTestId} = renderDateHeaderWithContext({unit: 'month', children: children});
+ const primaryHeader = getAllByTestId('dateHeader')[0]
+ const secondaryHeader = getAllByTestId('dateHeader')[1]
+ expect(primaryHeader).toHaveTextContent('2019')
+ expect(secondaryHeader).toHaveTextContent('January 2019')
+ })
+ it('Given DateHeader When pass a day unit to the timeline then it should take it as default', () => {
+ const children =
+ const {getAllByTestId} = renderDateHeaderWithContext({unit: 'day', children: children});
+ const primaryHeader = getAllByTestId('dateHeader')[0]
+ const secondaryHeader = getAllByTestId('dateHeader')[1]
+ expect(primaryHeader).toHaveTextContent('January 2019')
+ expect(secondaryHeader).toHaveTextContent('Monday, January 21, 2019')
+ })
+ it('Given DateHeader When pass a hour unit to the timeline then it should take it as default', () => {
+ const children =
+ const {getAllByTestId} = renderDateHeaderWithContext({unit: 'hour', children: children});
+ const primaryHeader = getAllByTestId('dateHeader')[0]
+ const secondaryHeader = getAllByTestId('dateHeader')[1]
+ expect(primaryHeader).toHaveTextContent('Monday, January 21, 2019')
+ expect(secondaryHeader).toHaveTextContent('30')
+ })
+ })
+
+})
+
+function dateHeaderComponent({ labelFormat, unit, props, className, style, handleTimeChange } = {}) {
+
+ return (
+
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+ {
+ return
+ {intervalContext.intervalText}
+
+
+ }}
+ />
+
+
+
+ )
+}
+
+function dateHeaderWithIntervalRenderer({ intervalRenderer, props } = {}) {
+
+ return (
+
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+
+
+
+
+ )
+}
+
+
+function renderDateHeaderWithContext({unit, children} = {})
+{
+ const oneDay = 1000 * 60 * 60 * 24
+ const now = Date.now()
+ const visibleTimeStart = now - oneDay
+ const visibleTimeEnd = now + oneDay
+ const defaultTimelineState = {
+ visibleTimeStart,
+ visibleTimeEnd,
+ canvasTimeStart: visibleTimeStart - oneDay,
+ canvasTimeEnd: visibleTimeEnd + oneDay,
+ canvasWidth: 3000,
+ visibleWidth: 1000,
+ showPeriod:()=>{},
+ timelineWidth:1000,
+ timelineUnit:unit
+ }
+
+ return render(
+
+ React.createRef()}
+ rightSidebarWidth={0}
+ >
+ {children}
+
+
+ )
+
+}
diff --git a/__tests__/components/Headers/SideBarHeader.test.js b/__tests__/components/Headers/SideBarHeader.test.js
new file mode 100644
index 000000000..b15c13a3b
--- /dev/null
+++ b/__tests__/components/Headers/SideBarHeader.test.js
@@ -0,0 +1,164 @@
+import React from 'react'
+import { render, cleanup, within } from 'react-testing-library'
+import Timeline from 'lib/Timeline'
+import DateHeader from 'lib/headers/DateHeader'
+import SidebarHeader from 'lib/headers/SidebarHeader'
+import TimelineHeaders from 'lib/headers/TimelineHeaders'
+import 'jest-dom/extend-expect'
+
+import moment from 'moment'
+
+import { items, groups } from '../../../__fixtures__/itemsAndGroups'
+
+
+const defaultProps = {
+ groups,
+ items,
+ defaultTimeStart: moment('1995-12-25').add(-12, 'hour'),
+ defaultTimeEnd: moment('1995-12-25').add(12, 'hour')
+}
+
+describe("Testing SidebarHeader Component", () => {
+ afterEach(cleanup)
+ // Testing The Example In The Docs
+ it("Given SidebarHeader When rendered Then it should shown correctly in the timeline", () => {
+ const { container } = render(
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+ {({ getRootProps }) => {
+ return Right
+ }}
+
+
+
+
+
+ )
+
+ const { getByTestId } = within(container)
+
+ expect(getByTestId('leftSidebarHeader')).toBeInTheDocument()
+ expect(getByTestId('rightSidebarHeader')).toBeInTheDocument()
+
+ expect(getByTestId('leftSidebarHeader').nextElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ expect(getByTestId('rightSidebarHeader').previousElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ })
+
+ it("Given SidebarHeader When passing no variant prop Then it should rendered above the left sidebar", () => {
+ const { getByTestId, getAllByTestId } = renderSidebarHeaderWithCustomValues()
+ expect(getByTestId('sidebarHeader')).toBeInTheDocument()
+ expect(getByTestId('sidebarHeader').nextElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ expect(getAllByTestId('sidebarHeader')).toHaveLength(1)
+
+
+ })
+ it("Given SidebarHeader When passing variant prop with left value Then it should rendered above the left sidebar", () => {
+ const { getByTestId, getAllByTestId } = renderSidebarHeaderWithCustomValues({ variant: "left" })
+ expect(getByTestId('sidebarHeader')).toBeInTheDocument()
+ expect(getByTestId('sidebarHeader').nextElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ expect(getAllByTestId('sidebarHeader')).toHaveLength(1)
+ })
+ it("Given SidebarHeader When passing variant prop with right value Then it should rendered above the right sidebar", () => {
+ const { getByTestId, getAllByTestId } = renderSidebarHeaderWithCustomValues({ variant: "right" })
+ expect(getByTestId('sidebarHeader')).toBeInTheDocument()
+ expect(getByTestId('sidebarHeader').previousElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ expect(getAllByTestId('sidebarHeader')).toHaveLength(1)
+ })
+
+ it("Given SidebarHeader When passing variant prop with unusual value Then it should rendered above the left sidebar by default", () => {
+ const { getByTestId } = renderSidebarHeaderWithCustomValues({ variant: "" })
+ expect(getByTestId('sidebarHeader')).toBeInTheDocument()
+ expect(getByTestId('sidebarHeader').nextElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ })
+
+ it("Given SidebarHeader When passing props to the props getter Then it should rendered correctly", () => {
+ const { getByTestId } = renderSidebarHeaderWithCustomValues({ props: { style: { width: 250 } } })
+ const { width } = getComputedStyle(getByTestId("sidebarHeader"))
+ expect(width).toBe("250px")
+ })
+
+ it("Given SidebarHeader When passing children to it Then it should rendered correctly", () => {
+ const { getByText } = renderSidebarHeaderWithCustomValues()
+ expect(getByText("Should Be Rendred")).toBeInTheDocument()
+ })
+
+ it("Given sidebarheader When pass a variant and props Then it should render both correctly", () => {
+ const { getByTestId } = renderSidebarHeaderWithCustomValues({ props: { style: { width: 250 }, variant: "left" } })
+ const { width } = getComputedStyle(getByTestId("sidebarHeader"))
+ expect(width).toBe("250px")
+ expect(getByTestId('sidebarHeader').nextElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ })
+
+ it("Given two sidebarheaders When pass a variants and props Then it should render both correctly", () => {
+ const { getByText } = renderTwoSidebarHeadersWithCustomValues({ props: { style: { width: 250 } } })
+ const { width: leftWidth } = getComputedStyle(getByText('LeftSideBar'))
+ const { width: rightWidth } = getComputedStyle(getByText('RightSideBar'))
+ expect(leftWidth).toBe("250px")
+ expect(rightWidth).toBe("250px")
+ expect(getByText('LeftSideBar').nextElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ expect(getByText('RightSideBar').previousElementSibling).toHaveAttribute('data-testid', 'headerContainer')
+ })
+
+ it('Given SidebarHeader When Pass an props obj to props renderer Then it should render it correctly', () => {
+ const { getByTestId } = renderSidebarHeaderWithCustomValues({ props: { 'aria-hidden': false } })
+ expect(getByTestId("sidebarHeader")).toHaveAttribute('aria-hidden')
+ })
+
+})
+
+
+function renderSidebarHeaderWithCustomValues({ variant = undefined, props, rightSidebarWidth } = {}) {
+ return render(
+
+
+ {({ getRootProps }, extraProps) => {
+ return SidebarHeader
+
Should Be Rendred
+
+ }}
+
+
+
+
+
+ )
+}
+
+
+function renderTwoSidebarHeadersWithCustomValues({ props, rightSidebarWidth } = {}) {
+ return render(
+
+
+ {({ getRootProps }) => {
+ return LeftSideBar
+
Should Be Rendred
+
+ }}
+
+
+ {({ getRootProps }, props) => {
+ return RightSideBar
+
+ }}
+
+
+
+
+
+ )
+}
+
diff --git a/__tests__/components/Headers/TimelineHeader.test.js b/__tests__/components/Headers/TimelineHeader.test.js
new file mode 100644
index 000000000..96ab239f0
--- /dev/null
+++ b/__tests__/components/Headers/TimelineHeader.test.js
@@ -0,0 +1,239 @@
+import { render, cleanup } from 'react-testing-library'
+import Timeline from 'lib/Timeline'
+import SidebarHeader from 'lib/headers/SidebarHeader'
+import DateHeader from 'lib/headers/DateHeader'
+import TimelineHeaders from 'lib/headers/TimelineHeaders'
+import 'jest-dom/extend-expect'
+
+import React from 'react'
+import moment from 'moment'
+
+import { items, groups } from '../../../__fixtures__/itemsAndGroups'
+import {
+ visibleTimeStart,
+ visibleTimeEnd
+} from '../../../__fixtures__/stateAndProps'
+
+const defaultProps = {
+ groups,
+ items,
+ defaultTimeStart: moment('1995-12-25').add(-12, 'hour'),
+ defaultTimeEnd: moment('1995-12-25').add(12, 'hour')
+}
+
+describe('TimelineHeader', () => {
+ beforeEach(() => {
+ Element.prototype.getBoundingClientRect = jest.fn(() => {
+ return {
+ width: 1000,
+ height: 120,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ }
+ });
+ })
+ afterEach(cleanup)
+ /**
+ * Testing The Default Functionality
+ */
+ describe('renders default headers correctly', () => {
+ it('Given TimelineHeader When rendered Then it should render tow date headers by default', () => {
+ const { getAllByTestId, getByTestId } = renderDefaultTimeline()
+ expect(getAllByTestId('dateHeader')).toHaveLength(2)
+ expect(getByTestId('headerContainer').children).toHaveLength(2)
+ })
+ it('Given TimelineHeader When rendered Then it should render a left sidebar by default', () => {
+ const { getByTestId } = renderDefaultTimeline()
+ expect(getByTestId("sidebarHeader")).toBeInTheDocument()
+ })
+ it('Given TimelineHeader When pass a rightsidebarWidth Then it should render two sidebar headers', () => {
+ let rightSidebarWidth = 150;
+ const { getAllByTestId } = renderDefaultTimeline({ rightSidebarWidth });
+ const sidebarHeaders = getAllByTestId('sidebarHeader')
+
+ expect(sidebarHeaders).toHaveLength(2)
+ expect(sidebarHeaders[0]).toBeInTheDocument()
+ expect(sidebarHeaders[1]).toBeInTheDocument()
+ const { width } = getComputedStyle(sidebarHeaders[1])
+ expect(width).toBe("150px")
+
+
+ })
+ it('Given TimelineHeader When rendered Then it should render a two dateHeadrs default', () => {
+ const { getAllByTestId } = renderDefaultTimeline();
+ const dateHeaders = getAllByTestId("dateHeader")
+
+ expect(dateHeaders).toHaveLength(2)
+ expect(dateHeaders[1].childElementCount).toBeGreaterThan(dateHeaders[0].childElementCount)
+
+ })
+
+ it("Given TimelineHeader When pass a left sidebar as a child Then it should render a left sidebar", () => {
+ const { getByTestId, getAllByTestId } = renderTimelineWithVariantSidebar({ variant: "left" });
+ expect(getByTestId('sidebarHeader')).toBeInTheDocument();
+ expect(getAllByTestId('sidebarHeader')).toHaveLength(1)
+ })
+ it("Given TimelineHeader When pass a right sidebar as a child Then it should render a right sidebar", () => {
+ const { getByTestId, getAllByTestId } = renderTimelineWithVariantSidebar({ variant: "right" });
+ expect(getByTestId('sidebarHeader')).toBeInTheDocument();
+ expect(getAllByTestId('sidebarHeader')).toHaveLength(1)
+ })
+ it("Given TimelineHeader When pass a left and right sidebars as children Then it should render a left and right sidebars", () => {
+ const { getByTestId } = renderTimelineWithLeftAndRightSidebar();
+ expect(getByTestId('left-header')).toBeInTheDocument();
+ expect(getByTestId('right-header')).toBeInTheDocument();
+ })
+
+ it("Given TimelineHeader When pass calendarHeaderStyle with overrided (overflow, width) Then it should not override the deaful values", () => {
+ const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ calendarHeaderStyle: { overflow: 'unset', width: 0 } });
+ const headerContainer = getByTestId('headerContainer')
+ const { width, overflow } = getComputedStyle(headerContainer)
+
+
+ expect(overflow).not.toBe('unset')
+ expect(width).not.toBe("0px")
+
+ })
+ it("Given TimelineHeader When pass rootStyle with overrided (display, width) Then it should not override the deaful values", () => {
+ const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ style: { display: 'none', width: 0 } });
+ const rootDiv = getByTestId('headerRootDiv')
+ const { width, display } = getComputedStyle(rootDiv)
+
+ expect(display).not.toBe('none')
+ expect(width).not.toBe("0px")
+
+ })
+ it("Given TimelineHeader When pass calendarHeaderClassName Then it should be applied to the date header container", () => {
+ const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ calendarHeaderClassName: "testClassName" });
+ expect(getByTestId("headerContainer")).toHaveClass("testClassName")
+ })
+ })
+
+ it('Given TimelineHeader When rendered Then it should render the default styles of the date header container', () => {
+ const { getByTestId } = renderTimelineWithLeftAndRightSidebar();
+ const headerContainer = getByTestId('headerContainer')
+ const { overflow } = getComputedStyle(headerContainer)
+ expect(overflow).toBe('hidden')
+ // The JSDOM will not fire the calc css function
+ })
+
+ it('Given TimelineHeader When rendered Then it should render the default styles of the rootStyle', () => {
+ const { getByTestId } = renderTimelineWithLeftAndRightSidebar();
+ const rootDiv = getByTestId('headerRootDiv')
+ const { width, display } = getComputedStyle(rootDiv)
+
+ expect(display).toBe('flex')
+ expect(width).toBe("100%")
+ })
+
+ /**
+ * Testing The Example Provided In The Docs
+ */
+ it('Given TimelineHeader When pass a headers as children Then it should render them correctly', () => {
+ const { getByText, rerender, queryByText } = render(
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+ {({ getRootProps }) => {
+ return (
+
+ Right
+
+ )
+ }}
+
+
+
+
+ )
+ expect(getByText('Left')).toBeInTheDocument()
+ expect(getByText('Right')).toBeInTheDocument()
+ rerender(
+
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+
+
+ )
+ expect(queryByText('Right')).toBeNull()
+ })
+})
+
+function renderDefaultTimeline(props = {}) {
+ const timelineProps = {
+ ...defaultProps,
+ ...props
+ }
+ return render()
+}
+
+function renderTimelineWithVariantSidebar({ props, variant } = {}) {
+ const timelineProps = {
+ ...defaultProps,
+ ...props
+ }
+ return render(
+
+
+
+ {({ getRootProps }) => {
+ return Header
+ }}
+
+
+
+ )
+}
+
+
+function renderTimelineWithLeftAndRightSidebar({ props, calendarHeaderClassName, calendarHeaderStyle, style } = {}) {
+
+ const timelineProps = {
+ ...defaultProps,
+ ...props
+ }
+
+ return render(
+
+
+
+ {({ getRootProps }) => {
+ return Right
+ }}
+
+
+ {({ getRootProps }) => {
+ return Left
+ }}
+
+
+
+ )
+}
+
+
+
diff --git a/__tests__/index.js b/__tests__/index.js
index 3122950ba..b93609565 100644
--- a/__tests__/index.js
+++ b/__tests__/index.js
@@ -132,7 +132,7 @@ xdescribe('Timeline', () => {
diff --git a/__tests__/utils/calendar/__snapshots__/calculate-scroll-canvas.js.snap b/__tests__/utils/calendar/__snapshots__/calculate-scroll-canvas.js.snap
index 0d4c752b1..757fcc868 100644
--- a/__tests__/utils/calendar/__snapshots__/calculate-scroll-canvas.js.snap
+++ b/__tests__/utils/calendar/__snapshots__/calculate-scroll-canvas.js.snap
@@ -18,7 +18,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 78.74857638888886,
},
"id": "0",
@@ -36,7 +36,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 245.4152430555556,
},
"id": "5",
@@ -54,7 +54,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 287.08190972222224,
},
"id": "6",
@@ -72,7 +72,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 172.1749884259259,
},
"id": "1",
@@ -90,7 +90,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 236.08273148148123,
},
"id": "2",
@@ -108,23 +108,23 @@ Object {
"index": 2,
},
"stack": true,
- "top": 105,
+ "top": 93.75,
"width": 236.08273148148146,
},
"id": "3",
},
],
"groupHeights": Array [
- 67.5,
+ 60,
+ 30,
30,
- 37.5,
],
"groupTops": Array [
0,
- 67.5,
- 97.5,
+ 60,
+ 90,
],
- "height": 135,
+ "height": 120,
"visibleTimeEnd": 1540634400000,
"visibleTimeStart": 1540548000000,
}
@@ -148,7 +148,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 75.59863333333351,
},
"id": "0",
@@ -166,7 +166,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 235.5986333333335,
},
"id": "5",
@@ -184,7 +184,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 275.5986333333335,
},
"id": "6",
@@ -202,7 +202,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 165.28798888888878,
},
"id": "1",
@@ -220,7 +220,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 226.6394222222225,
},
"id": "2",
@@ -238,23 +238,23 @@ Object {
"index": 2,
},
"stack": true,
- "top": 105,
+ "top": 93.75,
"width": 226.6394222222225,
},
"id": "3",
},
],
"groupHeights": Array [
- 67.5,
+ 60,
+ 30,
30,
- 37.5,
],
"groupTops": Array [
0,
- 67.5,
- 97.5,
+ 60,
+ 90,
],
- "height": 135,
+ "height": 120,
"visibleTimeEnd": 1540591200000,
"visibleTimeStart": 1540501200000,
}
diff --git a/__tests__/utils/calendar/__snapshots__/get-next-unit.js.snap b/__tests__/utils/calendar/__snapshots__/get-next-unit.js.snap
new file mode 100644
index 000000000..b56de34c6
--- /dev/null
+++ b/__tests__/utils/calendar/__snapshots__/get-next-unit.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`getNextUnit unknown value to throw error 1`] = `"unit foo in not acceptable"`;
diff --git a/__tests__/utils/calendar/__snapshots__/group-stack.js.snap b/__tests__/utils/calendar/__snapshots__/group-stack.js.snap
index 6f22034d7..bf2b6c159 100644
--- a/__tests__/utils/calendar/__snapshots__/group-stack.js.snap
+++ b/__tests__/utils/calendar/__snapshots__/group-stack.js.snap
@@ -4,6 +4,6 @@ exports[`groupStack works as expected 1`] = `
Object {
"groupHeight": 0,
"itemTop": 7.5,
- "verticalMargin": 37.5,
+ "verticalMargin": 18.75,
}
`;
diff --git a/__tests__/utils/calendar/__snapshots__/stack-group.js.snap b/__tests__/utils/calendar/__snapshots__/stack-group.js.snap
index bbe832509..9fe6769be 100644
--- a/__tests__/utils/calendar/__snapshots__/stack-group.js.snap
+++ b/__tests__/utils/calendar/__snapshots__/stack-group.js.snap
@@ -8,6 +8,20 @@ Object {
`;
exports[`stackGroup should stack list of items 1`] = `
+Object {
+ "groupHeight": 0,
+ "verticalMargin": 3.75,
+}
+`;
+
+exports[`stackGroup should stack list of items lines 1`] = `
+Object {
+ "groupHeight": 180,
+ "verticalMargin": 7.5,
+}
+`;
+
+exports[`stackGroup should stack list of items space 1`] = `
Object {
"groupHeight": 0,
"verticalMargin": 7.5,
diff --git a/__tests__/utils/calendar/__snapshots__/stack-items.js.snap b/__tests__/utils/calendar/__snapshots__/stack-items.js.snap
index 8d27dc42d..9d0000a46 100644
--- a/__tests__/utils/calendar/__snapshots__/stack-items.js.snap
+++ b/__tests__/utils/calendar/__snapshots__/stack-items.js.snap
@@ -16,7 +16,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 236.24572916666602,
},
"id": "0",
@@ -34,7 +34,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 736.245729166666,
},
"id": "5",
@@ -52,7 +52,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 861.245729166666,
},
"id": "6",
@@ -70,7 +70,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 516.5249652777784,
},
"id": "1",
@@ -88,7 +88,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 708.2481944444444,
},
"id": "2",
@@ -106,23 +106,23 @@ Object {
"index": 2,
},
"stack": true,
- "top": 105,
+ "top": 93.75,
"width": 625,
},
"id": "3",
},
],
"groupHeights": Array [
- 67.5,
+ 60,
+ 30,
30,
- 37.5,
],
"groupTops": Array [
0,
- 67.5,
- 97.5,
+ 60,
+ 90,
],
- "height": 135,
+ "height": 120,
}
`;
@@ -142,7 +142,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 111.24572916666602,
},
"id": "0",
@@ -160,7 +160,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 736.245729166666,
},
"id": "5",
@@ -178,7 +178,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 861.245729166666,
},
"id": "6",
@@ -196,7 +196,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 516.5249652777784,
},
"id": "1",
@@ -214,7 +214,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 708.2481944444444,
},
"id": "2",
@@ -232,23 +232,23 @@ Object {
"index": 2,
},
"stack": true,
- "top": 105,
+ "top": 93.75,
"width": 625,
},
"id": "3",
},
],
"groupHeights": Array [
- 67.5,
+ 60,
+ 30,
30,
- 37.5,
],
"groupTops": Array [
0,
- 67.5,
- 97.5,
+ 60,
+ 90,
],
- "height": 135,
+ "height": 120,
}
`;
@@ -268,7 +268,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 361.245729166666,
},
"id": "0",
@@ -286,7 +286,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 736.245729166666,
},
"id": "5",
@@ -304,7 +304,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 861.245729166666,
},
"id": "6",
@@ -322,7 +322,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 516.5249652777784,
},
"id": "1",
@@ -340,7 +340,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 708.2481944444444,
},
"id": "2",
@@ -358,23 +358,23 @@ Object {
"index": 2,
},
"stack": true,
- "top": 105,
+ "top": 93.75,
"width": 625,
},
"id": "3",
},
],
"groupHeights": Array [
- 67.5,
+ 60,
+ 30,
30,
- 37.5,
],
"groupTops": Array [
0,
- 67.5,
- 97.5,
+ 60,
+ 90,
],
- "height": 135,
+ "height": 120,
}
`;
@@ -394,7 +394,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 236.24572916666602,
},
"id": "0",
@@ -412,7 +412,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 736.245729166666,
},
"id": "5",
@@ -430,7 +430,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 861.245729166666,
},
"id": "6",
@@ -448,7 +448,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 37.5,
+ "top": 33.75,
"width": 516.5249652777784,
},
"id": "1",
@@ -466,7 +466,7 @@ Object {
"index": 0,
},
"stack": true,
- "top": 7.5,
+ "top": 3.75,
"width": 708.2481944444444,
},
"id": "2",
@@ -484,23 +484,23 @@ Object {
"index": 2,
},
"stack": true,
- "top": 105,
+ "top": 93.75,
"width": 625,
},
"id": "3",
},
],
"groupHeights": Array [
- 67.5,
+ 60,
+ 30,
30,
- 37.5,
],
"groupTops": Array [
0,
- 67.5,
- 97.5,
+ 60,
+ 90,
],
- "height": 135,
+ "height": 120,
}
`;
diff --git a/__tests__/utils/calendar/calculate-scroll-canvas.js b/__tests__/utils/calendar/calculate-scroll-canvas.js
index ed5ee7048..71f37a27f 100644
--- a/__tests__/utils/calendar/calculate-scroll-canvas.js
+++ b/__tests__/utils/calendar/calculate-scroll-canvas.js
@@ -1,17 +1,15 @@
import { calculateScrollCanvas } from 'lib/utility/calendar'
-import {defaultKeys} from 'lib/default-config'
-import moment from 'moment'
import {items, groups} from '../../../__fixtures__/itemsAndGroups'
import {props, state, visibleTimeStart, visibleTimeEnd} from '../../../__fixtures__/stateAndProps'
describe('calculateScrollCanvas', () => {
it('should calculate new scroll state', () => {
- const newStartTime = visibleTimeStart.clone().add(13, 'h')
- const newEndTime = visibleTimeEnd.clone().add(13, 'h')
+ const newStartTime = visibleTimeStart + 13 * 60 * 60 * 1000
+ const newEndTime = visibleTimeEnd + visibleTimeStart + 13 * 60 * 60 * 1000
const result = calculateScrollCanvas(
- newStartTime.valueOf(),
- newEndTime.valueOf(),
+ newStartTime,
+ newEndTime,
false,
items,
groups,
@@ -23,11 +21,11 @@ describe('calculateScrollCanvas', () => {
expect(result).toHaveProperty('dimensionItems')
})
it('should calculate new scroll state correctly', () => {
- const newStartTime = visibleTimeStart.clone().add(13, 'h')
- const newEndTime = visibleTimeEnd.clone().add(13, 'h')
+ const newStartTime = visibleTimeStart + 13 * 60 * 60 * 1000
+ const newEndTime = visibleTimeEnd + 13 * 60 * 60 * 1000
const result = calculateScrollCanvas(
- newStartTime.valueOf(),
- newEndTime.valueOf(),
+ newStartTime,
+ newEndTime,
false,
items,
groups,
@@ -37,11 +35,11 @@ describe('calculateScrollCanvas', () => {
expect(result).toMatchSnapshot()
})
it('should skip new calculation if new visible start and visible end in canvas', () => {
- const newStartTime = visibleTimeStart.clone().add(1, 'h')
- const newEndTime = visibleTimeEnd.clone().add(1, 'h')
+ const newStartTime = visibleTimeStart + 1 * 60 * 60 * 1000
+ const newEndTime = visibleTimeEnd + 1 * 60 * 60 * 1000
const result = calculateScrollCanvas(
- newStartTime.valueOf(),
- newEndTime.valueOf(),
+ newStartTime,
+ newEndTime,
false,
items,
groups,
@@ -53,8 +51,8 @@ describe('calculateScrollCanvas', () => {
expect(result).not.toHaveProperty('dimensionItems')
})
it('should force new calculation', () => {
- const newStartTime = visibleTimeStart.clone().add(1, 'h')
- const newEndTime = visibleTimeEnd.clone().add(1, 'h')
+ const newStartTime = visibleTimeStart + 1 * 60 * 60 * 1000
+ const newEndTime = visibleTimeEnd + 1 * 60 * 60 * 1000
const result = calculateScrollCanvas(
newStartTime.valueOf(),
newEndTime.valueOf(),
@@ -69,11 +67,11 @@ describe('calculateScrollCanvas', () => {
expect(result).toHaveProperty('dimensionItems')
})
it('should calculate new state if zoom changed ', () => {
- const newStartTime = visibleTimeStart.clone()
- const newEndTime = visibleTimeEnd.clone().add(1, 'h')
+ const newStartTime = visibleTimeStart
+ const newEndTime = visibleTimeEnd + 1 * 60 * 60 * 1000
const result = calculateScrollCanvas(
- newStartTime.valueOf(),
- newEndTime.valueOf(),
+ newStartTime,
+ newEndTime,
false,
items,
groups,
@@ -85,11 +83,11 @@ describe('calculateScrollCanvas', () => {
expect(result).toHaveProperty('dimensionItems')
})
it('should calculate new state if zoom changed correctly', () => {
- const newStartTime = visibleTimeStart.clone()
- const newEndTime = visibleTimeEnd.clone().add(1, 'h')
+ const newStartTime = visibleTimeStart
+ const newEndTime = visibleTimeEnd + 1 * 60 * 60 * 1000
const result = calculateScrollCanvas(
- newStartTime.valueOf(),
- newEndTime.valueOf(),
+ newStartTime,
+ newEndTime,
false,
items,
groups,
diff --git a/__tests__/utils/calendar/get-next-unit.js b/__tests__/utils/calendar/get-next-unit.js
index 40b19bb81..306e4353f 100644
--- a/__tests__/utils/calendar/get-next-unit.js
+++ b/__tests__/utils/calendar/get-next-unit.js
@@ -23,12 +23,11 @@ describe('getNextUnit', () => {
const result = getNextUnit('month')
expect(result).toBe('year')
})
- it('year to empty string', () => {
+ it('year to year', () => {
const result = getNextUnit('year')
- expect(result).toBe('')
+ expect(result).toBe('year')
})
- it('unknown value to empty string', () => {
- const result = getNextUnit('foo')
- expect(result).toBe('')
+ it('unknown value to throw error', () => {
+ expect(() => getNextUnit('foo')).toThrowErrorMatchingSnapshot()
})
})
diff --git a/__tests__/utils/calendar/stack-group.js b/__tests__/utils/calendar/stack-group.js
index be91fa399..678cf455d 100644
--- a/__tests__/utils/calendar/stack-group.js
+++ b/__tests__/utils/calendar/stack-group.js
@@ -2,8 +2,11 @@ import { stackGroup } from 'lib/utility/calendar'
import { dimensionItems } from '../../../__fixtures__/groupOrderAndItemDimentions'
describe('stackGroup', ()=>{
- it('should stack list of items', ()=>{
- expect(stackGroup(dimensionItems, true, 30, 0)).toMatchSnapshot()
+ it('should stack list of items space', ()=>{
+ expect(stackGroup(dimensionItems, 'space', 30, 0)).toMatchSnapshot()
+ })
+ it('should stack list of items lines', ()=>{
+ expect(stackGroup(dimensionItems, 'lines', 30, 0)).toMatchSnapshot()
})
it('should not stack list of items', ()=>{
expect(stackGroup(dimensionItems, false, 30, 0)).toMatchSnapshot()
diff --git a/demo/app/demo-custom-items/index.js b/demo/app/demo-custom-items/index.js
index 73a3e8d10..bf598b8a9 100644
--- a/demo/app/demo-custom-items/index.js
+++ b/demo/app/demo-custom-items/index.js
@@ -207,7 +207,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
lineHeight={40}
showCursorLine
diff --git a/demo/app/demo-element-resize/index.js b/demo/app/demo-element-resize/index.js
index ff25558fe..ca74a17d2 100644
--- a/demo/app/demo-element-resize/index.js
+++ b/demo/app/demo-element-resize/index.js
@@ -69,7 +69,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
resizeDetector={containerResizeDetector}
defaultTimeStart={defaultTimeStart}
diff --git a/demo/app/demo-headers/index.js b/demo/app/demo-headers/index.js
index 0fafd2e29..9f8fc2498 100644
--- a/demo/app/demo-headers/index.js
+++ b/demo/app/demo-headers/index.js
@@ -65,6 +65,9 @@ export default class App extends Component {
}
handleCanvasClick = (groupId, time) => {
+ this.setState(state => ({
+ groups: state.groups
+ }))
console.log('Canvas clicked', groupId, moment(time).format())
}
@@ -82,6 +85,9 @@ export default class App extends Component {
handleItemSelect = (itemId, _, time) => {
console.log('Selected: ' + itemId, moment(time).format())
+ this.setState((state)=>({
+ groups: state.groups.filter(_ => Math.random() > 0.5 )
+ }))
}
handleItemDoubleClick = (itemId, _, time) => {
@@ -194,8 +200,8 @@ export default class App extends Component {
// moveResizeValidator={this.moveResizeValidator}
rightSidebarWidth={150}
rightSidebarContent={Above The Right
}
- stackItems
- >
+ stackItems="space"
+ >
{({ getRootProps }) => {
diff --git a/demo/app/demo-linked-timelines/index.js b/demo/app/demo-linked-timelines/index.js
index 8ec0af119..b68a3a5d6 100644
--- a/demo/app/demo-linked-timelines/index.js
+++ b/demo/app/demo-linked-timelines/index.js
@@ -70,7 +70,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
visibleTimeStart={visibleTimeStart}
visibleTimeEnd={visibleTimeEnd}
@@ -94,7 +94,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
visibleTimeStart={visibleTimeStart}
visibleTimeEnd={visibleTimeEnd}
diff --git a/demo/app/demo-main/index.js b/demo/app/demo-main/index.js
index 19d1145ca..c72697609 100644
--- a/demo/app/demo-main/index.js
+++ b/demo/app/demo-main/index.js
@@ -4,9 +4,13 @@ import moment from 'moment'
import Timeline, {
TimelineMarkers,
+ TimelineHeaders,
TodayMarker,
CustomMarker,
- CursorMarker
+ CursorMarker,
+ CustomHeader,
+ SidebarHeader,
+ DateHeader
} from 'react-calendar-timeline'
import generateFakeData from '../generate-fake-data'
@@ -34,15 +38,151 @@ export default class App extends Component {
constructor(props) {
super(props)
- const { groups, items } = generateFakeData()
- const defaultTimeStart = moment()
+ const items = [
+ // {
+ // id: '4',
+ // group: '1',
+ // title:
+ // '4',
+ // start: 1550538000000,
+ // end: 1550560702870,
+ // canResize: false,
+ // className: '',
+ // bgColor: 'rgba(209, 154, 237, 0.6)',
+ // selectedBgColor: 'rgba(209, 154, 237, 1)',
+ // color: '#55077c',
+ // itemProps: {
+ // 'data-tip':
+ // 'The AGP bus is down, calculate the multi-byte alarm so we can generate the THX driver!'
+ // }
+ // },
+ // {
+ // id: '5',
+ // group: '1',
+ // title:
+ // '5',
+ // start: 1550549700000,
+ // end: 1550569678295,
+ // canMove: true,
+ // canResize: 'both',
+ // className: '',
+ // bgColor: 'rgba(235, 171, 242, 0.6)',
+ // selectedBgColor: 'rgba(235, 171, 242, 1)',
+ // color: '#ad0fbf',
+ // itemProps: {
+ // 'data-tip':
+ // "I'll parse the virtual AI monitor, that should microchip the SDD circuit!"
+ // }
+ // },
+ {
+ id: '1',
+ group: '1',
+ title:
+ '1',
+ start: 1550562300000,
+ end: 1550566800000,
+ canMove: true,
+ canResize: 'both',
+ className: '',
+ bgColor: 'rgba(119, 126, 249, 0.6)',
+ selectedBgColor: 'rgba(119, 126, 249, 1)',
+ color: '#010887',
+ itemProps: {
+ 'data-tip':
+ 'The IB alarm is down, parse the virtual driver so we can copy the COM bus!'
+ }
+ },
+ {
+ id: '3',
+ group: '2',
+ title: '3',
+ start: 1550538000000,
+ end: 1550557157371,
+ canResize: false,
+ className: '',
+ bgColor: 'rgba(184, 141, 239, 0.6)',
+ selectedBgColor: 'rgba(184, 141, 239, 1)',
+ color: '#3c0584',
+ itemProps: {
+ 'data-tip':
+ 'Try to synthesize the AI circuit, maybe it will calculate the cross-platform interface!'
+ }
+ },
+
+
+ {
+ id: '6',
+ group: '2',
+ title: '6',
+ start: 1550551500000,
+ end: 1550571478295,
+ canResize: false,
+ className: '',
+ bgColor: 'rgba(252, 191, 243, 0.6)',
+ selectedBgColor: 'rgba(252, 191, 243, 1)',
+ color: '#ea15ca',
+ itemProps: {
+ 'data-tip': 'We need to input the haptic USB panel!'
+ }
+ },
+ {
+ id: '7',
+ group: '2',
+ title:
+ "7",
+ start: 1550539800000,
+ end: 1550559571292,
+ canResize: false,
+ className: '',
+ bgColor: 'rgba(247, 116, 197, 0.6)',
+ selectedBgColor: 'rgba(247, 116, 197, 1)',
+ color: '#db0288',
+ itemProps: {
+ 'data-tip':
+ "bypassing the driver won't do anything, we need to compress the haptic XML monitor!"
+ }
+ },
+ {
+ id: '8',
+ group: '2',
+ title: '8',
+ start: 1550535300000,
+ end: 1550550380987,
+ canResize: false,
+ className: '',
+ bgColor: 'rgba(244, 129, 173, 0.6)',
+ selectedBgColor: 'rgba(244, 129, 173, 1)',
+ color: '#99043d',
+ itemProps: {
+ 'data-tip':
+ "copying the system won't do anything, we need to quantify the neural SCSI protocol!"
+ }
+ }
+ ]
+
+ const groups = [
+ {
+ id: '1',
+ title: 'Dee',
+ rightTitle: 'Kuhn',
+ label: 'Label Amari',
+ bgColor: '#c0d0f9'
+ },
+ {
+ id: '2',
+ title: 'Brennon',
+ rightTitle: 'Cronin',
+ label: 'Label Maude',
+ bgColor: '#777ef9'
+ }
+ ]
+
+ const defaultTimeStart = moment('19/2/2019', 'dd/mm/yyyy')
.startOf('day')
.toDate()
- const defaultTimeEnd = moment()
- .startOf('day')
- .add(1, 'day')
+ const defaultTimeEnd = moment('19/2/2019', 'dd/mm/yyyy')
+ .endOf('day')
.toDate()
-
this.state = {
groups,
items,
@@ -141,6 +281,10 @@ export default class App extends Component {
return time
}
+ handleMoveUpdate= (...args)=> {
+ console.log(args)
+ }
+
render() {
const { groups, items, defaultTimeStart, defaultTimeEnd } = this.state
@@ -156,7 +300,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems='lines'
itemHeightRatio={0.75}
defaultTimeStart={defaultTimeStart}
defaultTimeEnd={defaultTimeEnd}
@@ -170,7 +314,9 @@ export default class App extends Component {
onItemResize={this.handleItemResize}
onItemDoubleClick={this.handleItemDoubleClick}
onTimeChange={this.handleTimeChange}
- // moveResizeValidator={this.moveResizeValidator}
+ moveResizeValidator={this.moveResizeValidator}
+ width={1000}
+ onUpdateMove={this.handleMoveUpdate}
>
diff --git a/demo/app/demo-performance/index.js b/demo/app/demo-performance/index.js
index 97c940d00..147db94a9 100644
--- a/demo/app/demo-performance/index.js
+++ b/demo/app/demo-performance/index.js
@@ -168,7 +168,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
// resizeDetector={containerResizeDetector}
diff --git a/demo/app/demo-renderers/index.js b/demo/app/demo-renderers/index.js
index 898dfb426..7a3229647 100644
--- a/demo/app/demo-renderers/index.js
+++ b/demo/app/demo-renderers/index.js
@@ -187,7 +187,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
// resizeDetector={containerResizeDetector}
diff --git a/demo/app/demo-sticky-header/index.js b/demo/app/demo-sticky-header/index.js
index adb26d533..d0685849e 100644
--- a/demo/app/demo-sticky-header/index.js
+++ b/demo/app/demo-sticky-header/index.js
@@ -65,7 +65,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
resizeDetector={containerResizeDetector}
defaultTimeStart={defaultTimeStart}
@@ -113,7 +113,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
resizeDetector={containerResizeDetector}
defaultTimeStart={defaultTimeStart}
diff --git a/demo/app/demo-tree-groups/index.js b/demo/app/demo-tree-groups/index.js
index 63cb3ea2e..41dafbcea 100644
--- a/demo/app/demo-tree-groups/index.js
+++ b/demo/app/demo-tree-groups/index.js
@@ -111,7 +111,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
defaultTimeStart={defaultTimeStart}
defaultTimeEnd={defaultTimeEnd}
diff --git a/demo/app/demo-vertical-classes/index.js b/demo/app/demo-vertical-classes/index.js
index c8f0fbc5f..8d023e3b5 100644
--- a/demo/app/demo-vertical-classes/index.js
+++ b/demo/app/demo-vertical-classes/index.js
@@ -93,7 +93,7 @@ export default class App extends Component {
canSelect
itemsSorted
itemTouchSendsClick={false}
- stackItems
+ stackItems="space"
itemHeightRatio={0.75}
defaultTimeStart={defaultTimeStart}
defaultTimeEnd={defaultTimeEnd}
diff --git a/examples/README.md b/examples/README.md
index c05551b01..13bdcad66 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -6,19 +6,19 @@ Below are links to Codesandbox instances for examples of how to use this repo. F
Basic functionality of the timeline, included panning, zooming and rendering of items
-[Example Codesandbox](https://codesandbox.io/s/zr5r289rm4)
+[Example Codesandbox](https://codesandbox.io/s/w6xvqzno4w)
## Item Moving and Resizing
Moving and Resizing examples. Clicking and dragging can move items into different groups. Notice the `handleItemMove` and `handleItemResize` handlers passed to the Timeline.
-[Example Codesandbox](https://codesandbox.io/s/6vo2jkov23)
+[Example Codesandbox](https://codesandbox.io/s/q3rkx1478q)
## Custom Item Rendering
The `itemRenderer` component allows you to customize what contents are shown in the item on the calendar.
-[Example Codesandbox](https://codesandbox.io/s/1p8m477w7)
+[Example Codesandbox](https://codesandbox.io/s/k0wn41y0o7)