diff --git a/README.md b/README.md index 9f06b30a7..2d7692850 100644 --- a/README.md +++ b/README.md @@ -177,30 +177,10 @@ Snapping unit when dragging items. Defaults to `15 * 60 * 1000` or 15min. When s The minimum width, in pixels, of a timeline entry when it's possible to resize. If not reached, you must zoom in to resize more. Default to `20`. -## 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`. - -## stickyHeader - -Specify whether you want the timeline header to be "sticky". Pass `false` if you want the header to fix at top of element and not fix when you scroll down the page. Defaults to `true` - -## headerRef - -Ref callback that gets a DOM reference to the header element. See [FAQ below](#the-timeline-header-doesnt-fix-to-the-top-of-the-container-when-i-scroll-down). - ## lineHeight Height of one line in the calendar in pixels. Default `30` -## headerLabelGroupHeight - -Height of the top header line. Default `30` - -## headerLabelHeight - -Height of the bottom header line. Default `30` - ## itemHeightRatio What percentage of the height of the line is taken by the item? Default `0.65` @@ -335,85 +315,11 @@ function (action, item, time, resizeEdge) { return time } ``` - ## 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: - -```js -import { - defaultHeaderLabelFormats, - defaultSubHeaderLabelFormats -} from 'react-calendar-timeline' - -defaultHeaderLabelFormats == - { - yearShort: 'YY', - yearLong: 'YYYY', - monthShort: 'MM/YY', - monthMedium: 'MM/YYYY', - monthMediumLong: 'MMM YYYY', - monthLong: 'MMMM YYYY', - dayShort: 'L', - dayLong: 'dddd, LL', - hourShort: 'HH', - hourMedium: 'HH:00', - hourMediumLong: 'L, HH:00', - hourLong: 'dddd, LL, HH:00', - time: 'LLL' - } - -defaultSubHeaderLabelFormats == - { - yearShort: 'YY', - yearLong: 'YYYY', - monthShort: 'MM', - monthMedium: 'MMM', - monthLong: 'MMMM', - dayShort: 'D', - dayMedium: 'dd D', - dayMediumLong: 'ddd, Do', - dayLong: 'dddd, Do', - hourShort: 'HH', - hourLong: 'HH:00', - minuteShort: 'mm', - minuteLong: 'HH:mm' - } -``` - -For US time formats (AM/PM), use these: - -```js -import { - defaultHeaderLabelFormats, - defaultSubHeaderLabelFormats -} from 'react-calendar-timeline' - -const usHeaderLabelFormats = Object.assign({}, defaultSubHeaderLabelFormats, { - hourShort: 'h A', - hourMedium: 'h A', - hourMediumLong: 'L, h A', - hourLong: 'dddd, LL, h A' -}) - -const usSubHeaderLabelFormats = Object.assign( - {}, - defaultSubHeaderLabelFormats, - { - hourShort: 'h A', - hourLong: 'h A', - minuteLong: 'h:mm A' - } -) -``` - -... and then pass these as `headerLabelFormats` and `subHeaderLabelFormats` - ## onTimeChange(visibleTimeStart, visibleTimeEnd, updateScrollCanvas) A function that's called when the user tries to scroll. Call the passed `updateScrollCanvas(start, end)` with the updated visibleTimeStart and visibleTimeEnd (as unix timestamps in milliseconds) to change the scroll behavior, for example to limit scrolling. @@ -812,7 +718,7 @@ import Timeline, { return
Left
}} - + @@ -833,7 +739,7 @@ Is the core component wrapper component for custom headers | `className` | `string`| applied to the root component of the headers| | `calendarHeaderStyle`| `object`| applied to the root component of the calendar headers -scrolable div- `DateHeader` and `CustomHeader`)| | `calendarHeaderClassName`| `string`| applied to the root component of the calendar headers -scrolable div- `DateHeader` and `CustomHeader`)| - +| `headerRef` | `function` | used to get the ref of the header element ### `SidebarHeader` @@ -845,6 +751,7 @@ Responsible for rendering the headers above the left and right sidebars. | ----------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `variant`| `left` (default), `right`| renders above the left or right sidebar | | `children` | `Function`| function as a child component to render the header| +| `headerData` | `any`| Contextual data to be passed to the item renderer as a data prop | #### Child function renderer @@ -857,6 +764,7 @@ Rather than applying props on the element yourself and to avoid your props being | property | type | description| | ---------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `getRootProps` | `function(props={})` | returns the props you should apply to the root div element.| +| `data` | `any` | Contextual data passed by `headerData` prop| * `getRootProps` The returned props are: @@ -882,17 +790,19 @@ import Timeline, { return
Left
}} - - {({ getRootProps }) => { - return
Right
+ + {({ getRootProps, data }) => { + return
Right {data.someData}
}}
- + ``` +_Note_ : the Child function renderer can be a component or a function for convenience + ### `DateHeader` @@ -904,39 +814,47 @@ Responsible for rendering the headers above calendar part of the timeline. Consi | ----------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `style`| `object`| applied to the root of the header | | `className` | `string`| applied to the root of the header| -| `unit`| `second`, `minute`, `hour`, `day`, `week`, `month`, `year` | intervals between columns | -| `primaryHeader`| `boolean` | main header with interval unit larger than timeline unit by 1 | -| `secondaryHeader` | `boolean` (`true` by default) | sub header with interval equal to timeline unit | -| `labelFormat` | `Function` or `object` or `string`| controls the how to format the interval label | -| `intervalRenderer`| `Function`| render prop to render each interval in the header | +| `unit`| `second`, `minute`, `hour`, `day`, `week`, `month`, `year` or `primaryHeader` | intervals between columns | +| `labelFormat` | `Function` or `string`| controls the how to format the interval label | +| `intervalRenderer`| `Function`| render prop to render each interval in the header +| `headerData` | `any`| Contextual data to be passed to the item renderer as a data prop | +| `height` | `number` default (30)| height of the header in pixels | + +_Note_: passing `primaryHeader` to unit the header will act as the main header with interval unit larger than timeline unit by 1 #### Interval unit -intervals are decided through three props: `unit`, `primaryHeader` and `secondaryHeader` (default true). `secondaryHeader` is the default if no prop are set. The unit of the intervals will be the same the timeline and a special style is matches the default style of the secondary header from when no custom headers are applied. +intervals are decided through the prop: `unit`. By default, the unit of the intervals will be the same the timeline. -If `primaryHeader` is set to true, it will override `secondaryHeader` and the unit if the timeline will be larger by 1 of the timeline unit. The default style will match the primary header from when no custom headers are applied. +If `primaryHeader` is passed to unit, it will override the unit with a unit a unit larger by 1 of the timeline unit. -If `unit` is set, it will override both `primaryHeader` and `secondaryHeader`. The unit of the header will be the unit passed though the prop and can be any `unit of time` from `momentjs`. The default style will match the primary header from when no custom headers are applied. +If `unit` is set, the unit of the header will be the unit passed though the prop and can be any `unit of time` from `momentjs`. #### Label format -To format each interval label you can use 3 types of props to format which are: +To format each interval label you can use 2 types of props to format which are: - `string`: if a string was passed it will be passed to `startTime` method `format` which is a `momentjs` object . -- `object`: this will give you more flexibility to format the label with respect to `labelWidth`. Internally the `startTime` will be formated with the string corresponding to `formatObject[unit][range]` - The object will be in the following type: +- `Function`: This is the more powerful method and offers the most control over what is rendered. The returned `string` will be rendered inside the interval + + ```typescript + type Unit = `second` | `minute` | `hour` | `day` | `month` | `year` + ([startTime, endTime] : [Moment, Moment], unit: Unit, labelWidth: number, formatOptions: LabelFormat = defaultFormat ) => string + ``` +##### Default format + +by default we provide a responsive format for the dates based on the label width. it follows the following rules: + + The `long`, `mediumLong`, `medium` and `short` will be be decided through the `labelWidth` value according to where it lays upon the following scale: + + ``` + |-----`short`-----50px-----`medium`-----100px-----`mediumLong`-----150px--------`long`----- + ``` + + ```typescript - type unit = `second` | `minute` | `hour` | `day` | `week` | `month` | `year` - interface LabelFormat { - [unit]: { - long: string, - mediumLong: string, - medium: string, - short: string - } - } // default format object const format : LabelFormat = { year: { @@ -972,18 +890,8 @@ To format each interval label you can use 3 types of props to format which are: } ``` - The `long`, `mediumLong`, `medium` and `short` will be be decided through the `labelWidth` value according to where it lays upon the following scale: - - ``` - |-----`short`-----50px-----`medium`-----100px-----`mediumLong`-----150px--------`long`----- - ``` - -- `Function`: This is the more powerful method and offers the most control over what is rendered. The returned `string` will be rendered inside the interval +_Note_: this is only an implementation of the function param. You can do this on your own easily - ```typescript - type Unit = `second` | `minute` | `hour` | `day` | `month` | `year` - ([startTime, endTime] : [Moment, Moment], unit: Unit, labelWidth: number, formatOptions: LabelFormat = defaultFormat ) => string - ``` #### intervalRenderer @@ -991,13 +899,15 @@ Render prop function used to render a customized interval. The function provides Paramters provided to the function has two types: context params which have the state of the item and timeline, and prop getters functions +_Note_ : the renderProp can be a component or a function for convenience + ##### interval context An object contains the following properties: | property | type | description | | ------------------ | -------- | ---------------------------------------------------- | -| `interval` | `array : [Moment, Moment]` | an tuple array conating two moment object the first `startTime` and the second `endTime`| +| `interval` | `object : {startTime, endTime, labelWidth, left}` | an object containing data related to the interval| | `intervalText` | `string` | the string returned from `labelFormat` prop | @@ -1020,6 +930,10 @@ Rather than applying props on the element yourself and to avoid your props being * style: extra inline styles to be applied to the component * onClick: extra click handler added to the normal `showPeriod callback` +##### data + +data passed through headerData + #### example ```jsx @@ -1036,15 +950,17 @@ import Timeline, { return
Left
}}
- + { + data={{someData: 'example'}} + intervalRenderer={({ getIntervalProps, intervalContext, data }) => { return
{intervalContext.intervalText} + {data.example}
}} /> @@ -1176,6 +1092,8 @@ Responsible for rendering the headers above calendar part of the timeline. This | ----------------- | --------------- | ---| | `unit`| `second`, `minute`, `hour`, `day`, `week`, `month`, `year` (default `timelineUnit`) | intervals | | `children` | `Function`| function as a child component to render the header| +| `headerData` | `any`| Contextual data to be passed to the item renderer as a data prop | +| `height` | `number` default (30)| height of the header in pixels | #### unit @@ -1187,6 +1105,8 @@ Function as a child component to render the header Paramters provided to the function has three types: context params which have the state of the item and timeline, prop getters functions and helper functions. +_Note_ : the Child function renderer can be a component or a function for convenience + ``` ({ timelineContext: { @@ -1202,7 +1122,9 @@ Paramters provided to the function has three types: context params which have th }, getRootProps: this.getRootProps, getIntervalProps: this.getIntervalProps, - showPeriod + showPeriod, + //contextual data passed through headerData + data, })=> React.Node ``` @@ -1215,11 +1137,11 @@ An object contains context for `timeline` and `header`: | property | type | description | | ------------------ | -------- | ---------------------------------------------------- | -| `timelineWidth` | `array : [Moment, Moment]` | an tuple array conating two moment object the first `startTime` and the second `endTime`| -| `visibleTimeStart` | `string` | the string returned from `labelFormat` prop | -| `visibleTimeEnd` | `array : [Moment, Moment]` | an tuple array conating two moment object the first `startTime` and the second `endTime`| -| `canvasTimeStart` | `string` | the string returned from `labelFormat` prop | -| `canvasTimeEnd` | `array : [Moment, Moment]` | an tuple array conating two moment object the first `startTime` and the second `endTime`| +| `timelineWidth` | `number` | width of timeline| +| `visibleTimeStart` | `number` | unix milliseconds of start visible time | +| `visibleTimeEnd` | `number` | unix milliseconds of end visible time| +| `canvasTimeStart` | `number` | unix milliseconds of start buffer time | +| `canvasTimeEnd` | `number` |unix milliseconds of end buffer time| ###### Header context @@ -1256,7 +1178,9 @@ Rather than applying props on the element yourself and to avoid your props being | ---------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `showPeriod` | `function(props={})` | returns the props you should apply to the root div element.| +##### data: +pass through the `headerData` prop content #### example @@ -1274,14 +1198,15 @@ import Timeline, { return
Left
}} - + - + {({ headerContext: { intervals }, getRootProps, getIntervalProps, - showPeriod + showPeriod, + data, }) => { return (
@@ -1335,11 +1260,10 @@ Please refer to [examples](https://github.com/namespace-ee/react-calendar-timeli The library supports right sidebar. ![right sidebar demo](doc/right-sidebar.png) -To use it, you need to add two props to the `` component: +To use it, you need to add a props to the `` component: ```jsx rightSidebarWidth={150} -rightSidebarContent={

Second filter

} ``` And add `rightTitle` prop to the groups objects: @@ -1352,33 +1276,12 @@ And add `rightTitle` prop to the groups objects: } ``` -## The timeline header doesn't fix to the top of the container when I scroll down. - -There are two causes of this: - -* you are passing `stickyHeader={false}` to the timeline component. The header by default has sticky behavior unless you tell it not to using this prop. -* the browser you are viewing the timeline in doesn't support `position: sticky`. In this scenario, you will need to polyfill this behavior using the `headerRef`. - -In this example, we use [stickyfill](https://github.com/wilddeer/stickyfill) as our sticky polyfill - -```jsx -// add a handler in your parent component that accepts a DOM element -// with this element, pass the element into a polyfill library - -handleHeaderRef = (el) => { - // polyfill dom element with stickyfill - Stickyfill.addOne(el) -} +If you are using Custom Headers then you need to add `SidebarHeader` component under `TimelineHeader` with variant `right` -// in render, pass this handler to the `headerRef` prop: +## The timeline header doesn't fix to the top of the container when I scroll down. -render() { - -} ``` ## I'm using Babel with Rollup or Webpack 2+ and I'm getting strange bugs with click events diff --git a/__fixtures__/stateAndProps.js b/__fixtures__/stateAndProps.js index 6427947ec..69c4690d9 100644 --- a/__fixtures__/stateAndProps.js +++ b/__fixtures__/stateAndProps.js @@ -1,10 +1,16 @@ import { defaultKeys } from 'lib/default-config' import {items} from './itemsAndGroups' + +export const visibleTimeStart = 1540501200000 +export const visibleTimeEnd = 1540587600000 + export const props = { keys: defaultKeys, lineHeight: 30, stackItems: 'space', - itemHeightRatio: 0.75 + itemHeightRatio: 0.75, + visibleTimeEnd, + visibleTimeStart, } export const propsNoStack = { @@ -12,8 +18,6 @@ export const propsNoStack = { stackItems: false, } -export const visibleTimeStart = 1540501200000 -export const visibleTimeEnd = 1540587600000 export const state = { draggingItem: undefined, diff --git a/__tests__/components/Headers/CustomHeader.test.js b/__tests__/components/Headers/CustomHeader.test.js index 06883ce1f..34779bb93 100644 --- a/__tests__/components/Headers/CustomHeader.test.js +++ b/__tests__/components/Headers/CustomHeader.test.js @@ -5,238 +5,367 @@ 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 { RenderHeadersWrapper } from '../../test-utility/header-renderer' +import { getCustomHeadersInTimeline } from '../../test-utility/headerRenderers' +import { parsePxToNumbers } from '../../test-utility/index' import 'jest-dom/extend-expect' import moment from 'moment' -import { items, groups } from '../../../__fixtures__/itemsAndGroups' -import { visibleTimeEnd, visibleTimeStart } from '../../../__fixtures__/stateAndProps' +describe('CustomHeader Component Test', () => { + afterEach(cleanup) -const defaultProps = { - groups, - items, - defaultTimeStart: moment(visibleTimeStart, 'x'), - defaultTimeEnd: moment(visibleTimeEnd, 'x'), -} + it('Given CustomHeader When pass a unit to it Then header should render that unit', () => { + const { getAllByTestId } = render( + getCustomHeadersInTimeline({ + unit: 'month', + timelineState: { + timelineUnit: 'month', + canvasTimeStart: moment.utc('1/6/2018', 'DD/MM/YYYY').valueOf(), + canvasTimeEnd: moment.utc('1/6/2020', 'DD/MM/YYYY').valueOf(), + visibleTimeStart: moment.utc('1/1/2019', 'DD/MM/YYYY').valueOf(), + visibleTimeEnd: moment.utc('1/1/2020', 'DD/MM/YYYY').valueOf() + } + }) + ) + const intervals = getAllByTestId('customHeaderInterval') + const start = moment(intervals[0].textContent, 'DD/MM/YYYY') + const end = moment(intervals[1].textContent, 'DD/MM/YYYY') + expect(end.diff(start, 'M')).toBe(1) + }) + it('Given CustomHeader When pass a style props with (width, position) Then it should not override 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') + }) -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')} -
-
- ) - })} -
- ) - }} -
-
-
- ) + it('Given CustomHeader When pass a style props other than (width, position) Then it should rendered Correctly', () => { + const { getByTestId } = render( + getCustomHeadersInTimeline({ props: { style: { color: 'white' } } }) + ) + const { color } = getComputedStyle(getByTestId('customHeader')) + expect(color).toBe('white') + }) - 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') + it('Given CustomHeader When pass an interval style with (width, position and left) Then it should not override the default values', () => { + const { getByTestId } = render( + getCustomHeadersInTimeline({ + intervalStyle: { + width: 0, + position: 'fixed', + left: 1222222 + } + }) + ) + const { width, position, left } = getComputedStyle( + getByTestId('customHeaderInterval') + ) + expect(width).not.toBe('0px') + expect(position).not.toBe('fixed') + expect(left).not.toBe('1222222px') + }) + it('Given CustomHeader When pass an interval style other than (width, position and left) 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') + }) - rerender(getCustomHeadersInTimeline({ unit: "month" })); - expect(getByTestId('customHeader')).toHaveTextContent('10/01/2018') + it('Given a CustomHeader When not pass any unit prop Then it Should take the default timeline unit', () => { + const { getAllByTestId } = render( + getCustomHeadersInTimeline({ + timelineState: { + //default unit we are testing + timelineUnit: 'month', + canvasTimeStart: moment.utc('1/6/2018', 'DD/MM/YYYY').valueOf(), + canvasTimeEnd: moment.utc('1/6/2020', 'DD/MM/YYYY').valueOf(), + visibleTimeStart: moment.utc('1/1/2019', 'DD/MM/YYYY').valueOf(), + visibleTimeEnd: moment.utc('1/1/2020', 'DD/MM/YYYY').valueOf() + } + }) + ) + const intervals = getAllByTestId('customHeaderInterval') + const start = moment(intervals[0].textContent, 'DD/MM/YYYY') + const end = moment(intervals[1].textContent, 'DD/MM/YYYY') + expect(end.diff(start, 'M')).toBe(1) + }) - 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 rendered Then intervals don't overlap in position", () => { + const { getAllByTestId } = render(getCustomHeadersInTimeline()) + const intervals = getAllByTestId('customHeaderInterval') + const intervalsCoordinations = intervals.map(interval => { + const { left, width } = getComputedStyle(interval) + return { + left: parsePxToNumbers(left), + right: parsePxToNumbers(left) + parsePxToNumbers(width) + } }) + for (let index = 0; index < intervalsCoordinations.length - 1; index++) { + const a = intervalsCoordinations[index] + const b = intervalsCoordinations[index + 1] + expect(Math.abs(a.right - b.left)).toBeLessThan(0.1) + } + }) - 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 passing child renderer Then showPeriod should be passed', () => { + const showPeriod = () => {} + const renderer = jest.fn(() => { + return
header
}) + render( + + + {renderer} + + + ) + expect(renderer.mock.calls[0][0].showPeriod).toBe(showPeriod) + }) - 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 passing child renderer Then headerContext should be passed', () => { + const renderer = jest.fn(() => { + return
header
}) - 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') + render( + + + {renderer} + + + ) + const headerContext = renderer.mock.calls[0][0].headerContext + expect(headerContext).toBeDefined() + }) + + it('Given CustomHeader When passing child renderer Then headerContext should be passed with intervals and unit', () => { + const renderer = jest.fn(() => { + return
header
}) - 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() + render( + + + {renderer} + + + ) + const headerContext = renderer.mock.calls[0][0].headerContext + const intervals = headerContext.intervals + const unit = headerContext.unit + expect(intervals).toBeDefined() + expect(intervals).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + startTime: expect.any(moment), + endTime: expect.any(moment), + labelWidth: expect.any(Number), + left: expect.any(Number) + }) + ]) + ) + expect(unit).toEqual(expect.any(String)) + }) + + it('Given CustomHeader When passing child renderer Then timelineContext should be passed', () => { + const renderer = jest.fn(() => { + return
header
}) - 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') + render( + + + {renderer} + + + ) + const timelineContext = renderer.mock.calls[0][0].timelineContext + expect(timelineContext).toBeDefined() + expect(timelineContext).toMatchObject({ + timelineWidth: expect.any(Number), + visibleTimeStart: expect.any(Number), + visibleTimeEnd: expect.any(Number), + canvasTimeStart: expect.any(Number), + canvasTimeEnd: expect.any(Number) }) - 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' - }) + }) + + describe('CustomHeader Intervals', () => { + it('Given intervals Then they should have the same width', () => { + const renderer = jest.fn(() => { + return
header
+ }) + render( + + + {renderer} + + + ) + + const headerContext = renderer.mock.calls[0][0].headerContext + const intervals = headerContext.intervals + const widths = intervals.map(interval => interval.labelWidth) + for (let index = 0; index < widths.length - 1; index++) { + const a = widths[index] + const b = widths[index + 1] + + expect(Math.abs(b - a)).toBeLessThan(0.1) + } }) - 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') + it('Given intervals Then left property should be different', () => { + const renderer = jest.fn(() => { + return
header
+ }) + render( + + + {renderer} + + + ) + const headerContext = renderer.mock.calls[0][0].headerContext + const intervals = headerContext.intervals + const lefts = intervals.map(interval => interval.left) + for (let index = 0; index < lefts.length - 1; index++) { + const a = lefts[index] + const b = lefts[index + 1] + expect(a).toBeLessThan(b) + } }) - 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') - }) + }) + + it('Given CustomHeader When passing extra props Then it will be passed to the renderProp', () => { + const renderer = jest.fn(() => { + return
header
}) - -}) + const props = { + data: 'some' + } + render( + + + {renderer} + + + ) + expect(renderer.mock.calls[0][0].data).toBe(props) + }) + // Render The Example In The Docs + it('Given CustomHeader When render Then it should render 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 Custom Header When passing react stateless component to render prop Then it should render', () => { + const Renderer = props => { + return
header
+ } -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' + const { getByText } = render( + + + {Renderer} + + + ) + expect(getByText('header')).toBeInTheDocument() + }) + + it('Given Custom Header When passing react component to render prop Then it should render', () => { + class Renderer extends React.Component { + render() { + return
header
+ } } -} + + const { getByText } = render( + + + {Renderer} + + + ) + expect(getByText('header')).toBeInTheDocument() + }) +}) diff --git a/__tests__/components/Headers/DateHeader.test.js b/__tests__/components/Headers/DateHeader.test.js index 857d2dbf0..460908629 100644 --- a/__tests__/components/Headers/DateHeader.test.js +++ b/__tests__/components/Headers/DateHeader.test.js @@ -1,309 +1,434 @@ import React from 'react' -import { render, cleanup, within } from 'react-testing-library' +import { render, cleanup, within, fireEvent } 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 { RenderHeadersWrapper } from '../../test-utility/header-renderer' 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, - } - }); - }) +describe('Testing DateHeader Component', () => { afterEach(cleanup) + const format = 'MM/DD/YYYY hh:mm a' + // Testing The Example In The Docs - it("Given DateHeader When rendered Then it should be rendered correctly in the timeLine", () => { + it('Given DateHeader When rendered Then it should be rendered correctly in the timeLine', () => { const { getAllByTestId } = render( - + {({ getRootProps }) => { return
Left
}}
- + { - return
- {intervalContext.intervalText} -
+ 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') + describe('DateHeader labelFormat', () => { + afterEach(cleanup) - }) + it('Given Dateheader When pass a string typed labelFormat Then it should render the intervals with the 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') + }) - 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" } } })); + it('Given Dateheader When pass a function typed labelFormat Then it should render the intervals with the given format', () => { + const formatlabel = jest.fn(interval => interval[0].format('MM/DD/YYYY')) + const { getAllByTestId } = render( + dateHeaderComponent({ unit: 'day', labelFormat: formatlabel }) + ) - 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(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')[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 be called with an interval, label width and unit', () => { + const formatlabel = jest.fn(interval => interval[0].format('MM/DD/YYYY')) + render(dateHeaderComponent({ unit: 'day', labelFormat: formatlabel })) - }) + expect(formatlabel).toHaveBeenCalled() - 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)) + formatlabel.mock.calls.forEach(param => { + const [[start, end], unit, labelWidth] = param + expect(moment.isMoment(start)).toBeTruthy() + expect(moment.isMoment(end)).toBeTruthy() + expect(end.diff(start, 'd')).toBe(1) + expect(unit).toBe('day') + expect(labelWidth).toEqual(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 })); + it('Given Dateheader When click on the primary header Then it should change the unit', async () => { + const formatlabel = jest.fn(interval => interval[0].format('MM/DD/YYYY')) + const showPeriod = jest.fn() + const { getByTestId } = render( + dateHeaderComponent({ unit: 'day', labelFormat: formatlabel, showPeriod }) + ) // Arrange const primaryHeader = getByTestId('dateHeader') - const seconderyHeader = getAllByTestId('dateHeader')[2] // Act - const primaryFirstClick = within(primaryHeader).getByText('Friday, October 26, 2018').parentElement + const primaryFirstClick = within(primaryHeader).getByText('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() + expect(showPeriod).toBeCalled() + const [start, end] = showPeriod.mock.calls[0] + expect(start.format('DD/MM/YYYY hh:mm a')).toBe('01/01/2018 12:00 am') + expect(end.format('DD/MM/YYYY hh:mm a')).toBe('31/12/2018 11:59 pm') }) 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' })); + 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]) + it('Given Interval When pass an override values for (width, left, position) it should not override the default values', () => { + const { getAllByTestId } = render( + dateHeaderComponent({ + labelFormat: 'MM/DD/YYYY', + props: { style: { width: 100, position: 'fixed', left: 2342 } } + }) + ) + const { width, position, left } = getComputedStyle( + getAllByTestId('interval')[0] + ) expect(width).not.toBe('100px') expect(position).not.toBe('fixed') + expect(left).not.toBe('2342px') }) - 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' } } })); + it('Given Interval When pass an override (width, position) Then it should ignore these values', () => { + const { getAllByTestId, debug } = render( + dateHeaderComponent({ + labelFormat: 'MM/DD/YYYY', + props: { style: { width: 100, position: 'fixed' } } + }) + ) const { width, position } = getComputedStyle(getAllByTestId('interval')[0]) - expect(width).toBe('36px') + expect(width).not.toBe('1000px') 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 { 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 intervalRenderer = jest.fn( + ({ getIntervalProps, intervalContext, data }) => ( +
+ {intervalContext.intervalText} +
+ ) + ) + const props = { + title: 'some title' + } + render( + dateHeaderWithIntervalRenderer({ + intervalRenderer: intervalRenderer, + props + }) + ) 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() + expect(intervalRenderer.mock.calls[0][0].data).toBe(props) + expect(intervalRenderer.mock.calls[0][0].getIntervalProps).toEqual( + expect.any(Function) + ) + expect(intervalRenderer.mock.calls[0][0].intervalContext).toEqual( + expect.any(Object) + ) }) - 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') + + describe('DateHeader Unit Values', () => { + it('Given DateHeader When not passing a unit then the date header unit should be same as timeline unit', () => { + const { getAllByTestId } = render( + + + interval[0].format(format)} /> + + + ) + const intervals = getAllByTestId('dateHeaderInterval').map( + interval => interval.textContent + ) + for (let index = 0; index < intervals.length - 1; index++) { + const a = intervals[index] + const b = intervals[index + 1] + + const timeStampA = moment(a, format) + const timeStampB = moment(b, format) + const diff = timeStampB.diff(timeStampA, 'day') + expect(diff).toBe(1) + } + }) + it('Given DateHeader When passing a unit then the date header unit should be same as unit passed', () => { + const { getAllByTestId } = render( + + + interval[0].format(format)} + /> + + + ) + const intervals = getAllByTestId('dateHeaderInterval').map( + interval => interval.textContent + ) + for (let index = 0; index < intervals.length - 1; index++) { + const a = intervals[index] + const b = intervals[index + 1] + + const timeStampA = moment(a, format) + const timeStampB = moment(b, format) + const diff = timeStampB.diff(timeStampA, 'day') + expect(diff).toBe(1) + } + }) + + it('Given DateHeader When passing primaryHeader Then the header unit should be bigger the timeline unit', () => { + const { getAllByTestId } = render( + + + interval[0].format(format)} + /> + + + ) + const intervals = getAllByTestId('dateHeaderInterval').map( + interval => interval.textContent + ) + for (let index = 0; index < intervals.length - 1; index++) { + const a = intervals[index] + const b = intervals[index + 1] + + const timeStampA = moment(a, format) + const timeStampB = moment(b, format) + const diff = timeStampB.diff(timeStampA, 'month') + expect(diff).toBe(1) + } + }) + + it('Given DateHeader When not passing unit Then the header unit should be same as the timeline unit', () => { + const { getAllByTestId } = render( + + + interval[0].format(format)} /> + + + ) + const intervals = getAllByTestId('dateHeaderInterval').map( + interval => interval.textContent + ) + for (let index = 0; index < intervals.length - 1; index++) { + const a = intervals[index] + const b = intervals[index + 1] + + const timeStampA = moment(a, format) + const timeStampB = moment(b, format) + const diff = timeStampB.diff(timeStampA, 'day') + expect(diff).toBe(1) + } }) + }) - 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') + describe('DateHeader Interval', () => { + it('Given DateHeader Interval When passing on click event to the prop getter Then it should trigger', () => { + const onClick = jest.fn() + const { getAllByTestId } = render( + + + { + return ( +
+ {intervalContext.intervalText} +
+ ) + }} + /> +
+
+ ) + const intervals = getAllByTestId('interval') + fireEvent.click(intervals[0]) + expect(onClick).toHaveBeenCalled() }) - 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 passing interval renderer Then it should be rendered', () => { + const { getByTestId } = render( + + + { + return ( +
+ {intervalContext.intervalText} +
+ ) + }} + /> +
+
+ ) + expect(getByTestId('interval')).toBeInTheDocument() }) - 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') + it("Given DateHeader When passing interval renderer Then it should called with interval's context", () => { + const renderer = jest.fn(({ getIntervalProps, intervalContext }) => { + return ( +
+ {intervalContext.intervalText} +
+ ) + }) + render( + + + + + + ) + expect(renderer.mock.calls[0][0].intervalContext).toEqual( + expect.objectContaining({ + interval: expect.objectContaining({ + startTime: expect.any(moment), + endTime: expect.any(moment), + labelWidth: expect.any(Number), + left: expect.any(Number) + }), + intervalText: expect.any(String) + }) + ) }) }) + it('Given DateHeader When passing a stateless react component to interval renderer Then it should render', () => { + const Renderer = ({ getIntervalProps, intervalContext }) => { + return ( +
+ {intervalContext.intervalText} +
+ ) + } + const { getByTestId } = render( + + + + {({ getRootProps }) => { + return
Left
+ }} +
+ + + +
+
+ ) - 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') - }) + expect(getByTestId('interval-a')).toBeInTheDocument() }) + it('Given DateHeader When passing a react component to interval renderer Then it should render', () => { + class Renderer extends React.Component { + render() { + const { getIntervalProps, intervalContext } = this.props + return ( +
+ {intervalContext.intervalText} +
+ ) + } + } + const { getByTestId } = render( + + + + {({ getRootProps }) => { + return
Left
+ }} +
+ + + +
+
+ ) + expect(getByTestId('interval-a')).toBeInTheDocument() + }) }) -function dateHeaderComponent({ labelFormat, unit, props, className, style, handleTimeChange } = {}) { - +function dateHeaderComponent({ + labelFormat, + unit, + props, + className, + style, + showPeriod +} = {}) { return ( - - @@ -311,91 +436,45 @@ function dateHeaderComponent({ labelFormat, unit, props, className, style, handl return
Left
}}
- + { - return
- {intervalContext.intervalText} - -
+ intervalRenderer={({ getIntervalProps, intervalContext, data }) => { + 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 index b15c13a3b..6775547c4 100644 --- a/__tests__/components/Headers/SideBarHeader.test.js +++ b/__tests__/components/Headers/SideBarHeader.test.js @@ -1,164 +1,159 @@ import React from 'react' -import { render, cleanup, within } from 'react-testing-library' -import Timeline from 'lib/Timeline' +import { render, cleanup } from 'react-testing-library' import DateHeader from 'lib/headers/DateHeader' import SidebarHeader from 'lib/headers/SidebarHeader' import TimelineHeaders from 'lib/headers/TimelineHeaders' import 'jest-dom/extend-expect' +import { RenderHeadersWrapper } from '../../test-utility/header-renderer' +import { + renderSidebarHeaderWithCustomValues, + renderTwoSidebarHeadersWithCustomValues +} from '../../test-utility/headerRenderers' -import moment from 'moment' +describe('Testing SidebarHeader Component', () => { + afterEach(cleanup) -import { items, groups } from '../../../__fixtures__/itemsAndGroups' + //TODO: rename test + it('Given sidebarHeader When pass style with overridden (width) Then it should not override the default values', () => { + const { getByTestId, debug } = renderSidebarHeaderWithCustomValues({ + props: { style: { width: 250 } } + }) + const { width } = getComputedStyle(getByTestId('sidebarHeader')) + expect(width).not.toBe('250px') + }) + it('Given sidebarHeader When pass style Then it show on the sidebar', () => { + const { getByTestId } = renderSidebarHeaderWithCustomValues({ + props: { style: { color: 'white' } } + }) + const { color } = getComputedStyle(getByTestId('sidebarHeader')) + expect(color).toBe('white') + }) -const defaultProps = { - groups, - items, - defaultTimeStart: moment('1995-12-25').add(-12, 'hour'), - defaultTimeEnd: moment('1995-12-25').add(12, 'hour') -} + it('Given SidebarHeader When a render function Then it will be rendered', () => { + const renderer = jest.fn(({ getRootProps }) => { + return ( +
+ Left +
+ ) + }) + const { getByTestId } = render( + + + {renderer} + + + + + ) + expect(renderer).toHaveBeenCalled() + expect(getByTestId('leftSidebarHeader')).toBeInTheDocument() + }) + + it('Given SidebarHeader When passing props to SidebarHeader it should be passed to the renderProp', () => { + const renderer = jest.fn(({ getRootProps }) => { + return ( +
+ Left +
+ ) + }) + const extraProps = { + someData: 'data' + } + const { getByTestId } = render( + + + {renderer} + + + + + ) + expect(renderer).toHaveBeenCalled() + expect(renderer.mock.calls[0][0].data).toBe(extraProps) + }) -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( - + it('Given SidebarHeader When rendered Then it should shown correctly in the timeline', () => { + const { getByTestId } = render( + {({ getRootProps }) => { - return
Left
+ return ( +
+ Left +
+ ) }}
{({ getRootProps }) => { - return
Right
+ 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') + expect(getByTestId('leftSidebarHeader').nextElementSibling).toHaveAttribute( + 'data-testid', + 'headerContainer' + ) + expect( + getByTestId('rightSidebarHeader').previousElementSibling + ).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 passing a react stateless component as a child Then it should render', () => { + const Renderer = ({ getRootProps }) => { + return ( +
+ Left +
+ ) + } + const { getByText } = render( + + + {Renderer} + + + + + ) + expect(getByText('Left')).toBeInTheDocument() }) - - 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') + it('Given SideBarHeader When passing a react stateful component as a child Then it should render', () => { + class Renderer extends React.Component { + render() { + const { getRootProps } = this.props + return ( +
+ Left +
+ ) + } + } + const { getByText } = render( + + + {Renderer} + + + + + ) + expect(getByText('Left')).toBeInTheDocument() }) - }) - - -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 index 96ab239f0..cb66c7f5e 100644 --- a/__tests__/components/Headers/TimelineHeader.test.js +++ b/__tests__/components/Headers/TimelineHeader.test.js @@ -1,118 +1,82 @@ -import { render, cleanup } from 'react-testing-library' -import Timeline from 'lib/Timeline' +import { render } from 'react-testing-library' 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-testing-library/cleanup-after-each' import React from 'react' -import moment from 'moment' -import { items, groups } from '../../../__fixtures__/itemsAndGroups' +import { RenderHeadersWrapper } from '../../test-utility/header-renderer' 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') -} + renderSidebarHeaderWithCustomValues, + renderTimelineWithVariantSidebar, + renderTimelineWithLeftAndRightSidebar +} from '../../test-utility/headerRenderers' describe('TimelineHeader', () => { - beforeEach(() => { - Element.prototype.getBoundingClientRect = jest.fn(() => { - return { - width: 1000, - height: 120, - top: 0, - left: 0, - bottom: 0, - right: 0, - } - }); + 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() }) - 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 pass calendarHeaderStyle with overridden (overflow, width) Then it should not override the default values', () => { + const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ + calendarHeaderStyle: { overflow: 'unset', width: 0 } }) - 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) + const headerContainer = getByTestId('headerContainer') + const { width, overflow } = getComputedStyle(headerContainer) + expect(overflow).not.toBe('unset') + expect(width).not.toBe('0px') + }) + it('Given TimelineHeader When pass calendarHeaderStyle Then it be added to styles', () => { + const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ + calendarHeaderStyle: { color: 'white', background: 'black' } }) + const headerContainer = getByTestId('headerContainer') + const { color, background } = getComputedStyle(headerContainer) - 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(); + expect(color).toBe('white') + expect(background).toBe('black') + }) + it('Given TimelineHeader When pass style with overridden (display, width) Then it should not override the default values', () => { + const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ + style: { display: 'none', width: 0 } }) + const rootDiv = getByTestId('headerRootDiv') + const { width, display } = getComputedStyle(rootDiv) - 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") - + expect(display).not.toBe('none') + expect(width).not.toBe('0px') + }) + it('Given TimelineHeader When pass style Then it should it be added to root`s styles', () => { + const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ + style: { color: 'white', background: 'black' } }) - 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") + const rootDiv = getByTestId('headerRootDiv') + const { color, background } = getComputedStyle(rootDiv) + expect(color).toBe('white') + expect(background).toBe('black') + }) + it('Given TimelineHeader When pass calendarHeaderClassName Then it should be applied to the date header container', () => { + const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ + calendarHeaderClassName: 'testClassName' }) - 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") + expect(getByTestId('headerContainer')).toHaveClass('testClassName') + }) + + it('Given TimelineHeader When pass className Then it should be applied to the root header container', () => { + const { getByTestId } = renderTimelineWithLeftAndRightSidebar({ + className: 'testClassName' }) + expect(getByTestId('headerRootDiv')).toHaveClass('testClassName') }) it('Given TimelineHeader When rendered Then it should render the default styles of the date header container', () => { - const { getByTestId } = renderTimelineWithLeftAndRightSidebar(); + const { getByTestId } = renderTimelineWithLeftAndRightSidebar() const headerContainer = getByTestId('headerContainer') const { overflow } = getComputedStyle(headerContainer) expect(overflow).toBe('hidden') @@ -120,12 +84,56 @@ describe('TimelineHeader', () => { }) it('Given TimelineHeader When rendered Then it should render the default styles of the rootStyle', () => { - const { getByTestId } = renderTimelineWithLeftAndRightSidebar(); + const { getByTestId } = renderTimelineWithLeftAndRightSidebar() const rootDiv = getByTestId('headerRootDiv') const { width, display } = getComputedStyle(rootDiv) expect(display).toBe('flex') - expect(width).toBe("100%") + expect(width).toBe('100%') + }) + + 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' + ) }) /** @@ -133,12 +141,7 @@ describe('TimelineHeader', () => { */ it('Given TimelineHeader When pass a headers as children Then it should render them correctly', () => { const { getByText, rerender, queryByText } = render( - + {({ getRootProps }) => { @@ -156,17 +159,12 @@ describe('TimelineHeader', () => { - + ) expect(getByText('Left')).toBeInTheDocument() expect(getByText('Right')).toBeInTheDocument() rerender( - + {({ getRootProps }) => { @@ -175,65 +173,8 @@ describe('TimelineHeader', () => { - + ) 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__/components/Headers/defaultHeaders.js b/__tests__/components/Headers/defaultHeaders.js new file mode 100644 index 000000000..dbd7722fe --- /dev/null +++ b/__tests__/components/Headers/defaultHeaders.js @@ -0,0 +1,41 @@ +import React from 'react' +import { render } from 'react-testing-library' +import { items, groups } from '../../../__fixtures__/itemsAndGroups' +import { + props as defaultProps +} from '../../../__fixtures__/stateAndProps' +import 'react-testing-library/cleanup-after-each' +import Timeline from 'lib/Timeline' +import 'jest-dom/extend-expect' + +/** + * Testing The Default Functionality + */ +describe('Renders default headers correctly', () => { + it('Given Timeline When not using TimelineHeaders then it should render 2 DateHeaders and a left sidebar header by default ', () => { + const { getAllByTestId, getByTestId } = renderDefaultTimeline(); + expect(getAllByTestId('dateHeader')).toHaveLength(2); + expect(getByTestId('headerContainer').children).toHaveLength(2); + expect(getByTestId('sidebarHeader')).toBeInTheDocument(); + }); + it('Given TimelineHeader When pass a rightSidebarWidthWidth 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'); + }); +}); + +export function renderDefaultTimeline(props = {}) { + const timelineProps = { + ...defaultProps, + items, + groups, + ...props + } + return render() +} diff --git a/__tests__/components/Markers/TimelineMarkers.test.js b/__tests__/components/Markers/TimelineMarkers.test.js index da00906b0..8d03a5361 100644 --- a/__tests__/components/Markers/TimelineMarkers.test.js +++ b/__tests__/components/Markers/TimelineMarkers.test.js @@ -3,6 +3,7 @@ import { render } from 'react-testing-library' import 'jest-dom/extend-expect' import TimelineMarkers from 'lib/markers/public/TimelineMarkers' import TodayMarker from 'lib/markers/public/TodayMarker' +import CustomMarker from 'lib/markers/public/CustomMarker' import { RenderWrapper } from 'test-utility/marker-renderer' describe('TimelineMarkers', () => { @@ -23,4 +24,27 @@ describe('TimelineMarkers', () => { ) }) + + it('is unsubscribed on unmounting after passing new date then hide it', ()=>{ + const defaultCustomMarkerTestId = 'default-customer-marker-id' + const { queryByTestId, rerender } = render( + + + + + ) + + rerender( + + + + ) + + rerender( + + + ) + + expect(queryByTestId(defaultCustomMarkerTestId)).not.toBeInTheDocument() + }) }) diff --git a/__tests__/index.js b/__tests__/index.js index b93609565..fcb776487 100644 --- a/__tests__/index.js +++ b/__tests__/index.js @@ -149,7 +149,6 @@ xdescribe('Timeline', () => { expect(typeof pluginProps.visibleTimeStart).toBe('number') expect(typeof pluginProps.visibleTimeEnd).toBe('number') expect(typeof pluginProps.height).toBe('number') - expect(typeof pluginProps.headerHeight).toBe('number') expect(typeof pluginProps.minUnit).toBe('string') diff --git a/__tests__/test-utility/header-renderer.js b/__tests__/test-utility/header-renderer.js new file mode 100644 index 000000000..645fb8ee0 --- /dev/null +++ b/__tests__/test-utility/header-renderer.js @@ -0,0 +1,53 @@ +import React from 'react' +import TimelineMarkersRenderer from 'lib/markers/TimelineMarkersRenderer' +import { TimelineMarkersProvider } from 'lib/markers/TimelineMarkersContext' +import { TimelineStateProvider } from 'lib/timeline/TimelineStateContext' +import { state } from '../../__fixtures__/stateAndProps' +import jest from 'jest' +import { defaultTimeSteps } from '../../src/lib/default-config' +import { TimelineHeadersProvider } from '../../src/lib/headers/HeadersContext' + +// eslint-disable-next-line +export const RenderHeadersWrapper = ({ + children, + timelineState = {}, + headersState = {}, + showPeriod = () => {}, + registerScroll = () => {} +}) => { + const defaultTimelineState = { + visibleTimeStart: state.visibleTimeStart, + visibleTimeEnd: state.visibleTimeEnd, + canvasTimeStart: state.canvasTimeStart, + canvasTimeEnd: state.canvasTimeEnd, + canvasWidth: 2000, + showPeriod: showPeriod, + timelineUnit: 'day', + timelineWidth: 1000 + } + + const timelineStateProps = { + ...defaultTimelineState, + ...timelineState + } + + const headersStateProps = { + registerScroll: registerScroll, + timeSteps: defaultTimeSteps, + leftSidebarWidth: 150, + rightSidebarWidth: 0, + ...headersState + } + + return ( +
+ +
+ + {children} + +
+
+
+ ) +} diff --git a/__tests__/test-utility/headerRenderers.js b/__tests__/test-utility/headerRenderers.js new file mode 100644 index 000000000..9fa305052 --- /dev/null +++ b/__tests__/test-utility/headerRenderers.js @@ -0,0 +1,164 @@ +import React from 'react'; +import { render } from 'react-testing-library'; +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 { RenderHeadersWrapper } from './header-renderer'; +export function renderSidebarHeaderWithCustomValues({ variant = undefined, props, timelineState, headersState, extraProps } = {}) { + return render( + + + {({ getRootProps }) => { + return (
+ SidebarHeader +
Should Be Rendred
+
); + }} +
+ + +
+
); +} +export function renderTwoSidebarHeadersWithCustomValues({ props, timelineState, headersState } = {}) { + return render( + + + {({ getRootProps }) => { + return (
+ LeftSideBar +
Should Be Rendred
+
); + }} +
+ + {({ getRootProps, data }) => { + return
RightSideBar
; + }} +
+ + +
+
); +} + +export function renderTimelineWithLeftAndRightSidebar({ + calendarHeaderClassName, + calendarHeaderStyle, + style, + className, + timelineState, + headersState +} = {}) { + return render( + + + + {({ getRootProps }) => { + return ( +
+ Right +
+ ) + }} +
+ + {({ getRootProps }) => { + return ( +
+ Left +
+ ) + }} +
+
+
+ ) +} + +export function renderTimelineWithVariantSidebar({ + variant, + timelineState, + headersState +} = {}) { + return render( + + + + {({ getRootProps }) => { + return ( +
+ Header +
+ ) + }} +
+
+
+ ) +} + +export function getCustomHeadersInTimeline({ + unit, + props, + intervalStyle, + timelineState, + headersState +} = {}) { + return ( + + + + {( + { + headerContext: { intervals }, + getRootProps, + getIntervalProps, + showPeriod, + data = { style: { height: 30 } } + }, + ) => { + return ( +
+ {intervals.map(interval => { + return ( +
{ + showPeriod(interval.startTime, interval.endTime) + }} + {...getIntervalProps({ + interval, + style: intervalStyle + })} + > +
+ {interval.startTime.format('DD/MM/YYYY')} +
+
+ ) + })} +
+ ) + }} +
+
+
+ ) +} diff --git a/__tests__/test-utility/index.js b/__tests__/test-utility/index.js index 3cbac0bfb..0f5bb12a3 100644 --- a/__tests__/test-utility/index.js +++ b/__tests__/test-utility/index.js @@ -9,3 +9,7 @@ export function sel(selectorString) { } export function noop() {} + +export function parsePxToNumbers(value) { + return +value.replace('px', '') +} \ No newline at end of file diff --git a/__tests__/test-utility/parse-px-to-numbers.js b/__tests__/test-utility/parse-px-to-numbers.js new file mode 100644 index 000000000..e69de29bb diff --git a/demo/app/demo-headers/index.js b/demo/app/demo-headers/index.js index 9f8fc2498..efeff753e 100644 --- a/demo/app/demo-headers/index.js +++ b/demo/app/demo-headers/index.js @@ -203,59 +203,28 @@ export default class App extends Component { stackItems="space" > - - {({ getRootProps }) => { - return
Left
- }} -
- - {({ getRootProps }) => { - return
Right
- }} -
- { - return ( -
- {item.title} -
- ) - }} - /> - - + - - + + {( { headerContext: { intervals }, getRootProps, getIntervalProps, - showPeriod + showPeriod, + data, }, - props + ) => { + console.log('props', data) return ( -
+
{intervals.map(interval => { const intervalStyle = { - // height: 30, lineHeight: '30px', textAlign: 'center', borderLeft: '1px solid black', @@ -291,10 +260,9 @@ export default class App extends Component { showPeriod }) => { return ( -
+
{intervals.map(interval => { const intervalStyle = { - // height: 30, lineHeight: '30px', textAlign: 'center', borderLeft: '1px solid black', @@ -330,7 +298,7 @@ export default class App extends Component { showPeriod }) => { return ( -
+
{intervals.map(interval => { const intervalStyle = { lineHeight: '30px', @@ -359,12 +327,13 @@ export default class App extends Component { { + console.log('intervalRenderer props', data) return (
{intervalContext.intervalText} @@ -376,9 +345,9 @@ export default class App extends Component { ? [ , - + ] : null} diff --git a/demo/app/demo-sticky-header/index.js b/demo/app/demo-sticky-header/index.js deleted file mode 100644 index d0685849e..000000000 --- a/demo/app/demo-sticky-header/index.js +++ /dev/null @@ -1,126 +0,0 @@ -import React, { Component } from 'react' -import moment from 'moment' - -import Timeline from 'react-calendar-timeline' -import containerResizeDetector from '../../../src/resize-detector/container' - -// you would use this in real life: -// import containerResizeDetector from 'react-calendar-timeline/lib/resize-detector/container' - -import generateFakeData from '../generate-fake-data' - -var keys = { - groupIdKey: 'id', - groupTitleKey: 'title', - groupRightTitleKey: 'rightTitle', - itemIdKey: 'id', - itemTitleKey: 'title', - itemDivTitleKey: 'title', - itemGroupKey: 'group', - itemTimeStartKey: 'start', - itemTimeEndKey: 'end' -} - -export default class App extends Component { - constructor(props) { - super(props) - - const { groups, items } = generateFakeData() - const defaultTimeStart = moment() - .startOf('day') - .toDate() - const defaultTimeEnd = moment() - .startOf('day') - .add(1, 'day') - .toDate() - const width = 80 - - this.state = { - groups, - items, - defaultTimeStart, - defaultTimeEnd, - width - } - } - - render() { - const { groups, items, defaultTimeStart, defaultTimeEnd } = this.state - - return ( -
- In this example we have a lot of random content above the timeline.
- Try scrolling and see how the timeline header sticks to the screen.
-
- Above The Left
} - rightSidebarWidth={150} - rightSidebarContent={
Above The Right
} - canMove - canResize="right" - canSelect - itemsSorted - itemTouchSendsClick={false} - stackItems="space" - itemHeightRatio={0.75} - resizeDetector={containerResizeDetector} - defaultTimeStart={defaultTimeStart} - defaultTimeEnd={defaultTimeEnd} - /> -
- There is also a lot of stuff below the timeline. Watch the header fix - itself to the bottom of the component. -
-
- bla bla bla -
-
- Here are random pictures of Tom Selleck: -
-
- -
-
- -
-
- Here is another calendar, but this one has stickyOffset set - to 100, meaning that the header will stick 100px from the - top. This is useful for example if you already have a sticky navbar. -
-
- Above The Left
} - rightSidebarWidth={150} - rightSidebarContent={
Above The Right
} - canMove - canResize="right" - canSelect - itemsSorted - itemTouchSendsClick={false} - stackItems="space" - itemHeightRatio={0.75} - resizeDetector={containerResizeDetector} - defaultTimeStart={defaultTimeStart} - defaultTimeEnd={defaultTimeEnd} - /> -
-
- ) - } -} diff --git a/demo/app/index.js b/demo/app/index.js index fbe44aeab..f853ad51c 100644 --- a/demo/app/index.js +++ b/demo/app/index.js @@ -11,7 +11,6 @@ const demos = { treeGroups: require('./demo-tree-groups').default, linkedTimelines: require('./demo-linked-timelines').default, elementResize: require('./demo-element-resize').default, - stickyHeader: require('./demo-sticky-header').default, renderers: require('./demo-renderers').default, verticalClasses: require('./demo-vertical-classes').default, customItems: require('./demo-custom-items').default, diff --git a/examples/README.md b/examples/README.md index 13bdcad66..77b3b0ea8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -40,7 +40,9 @@ You can restrict the moving and resizing of items at the item level by providing ## Right Sidebar -Adding a right sidebar is as easy as passing in a couple of props `rightSidebarWidth` and `rightSidebarContent`. Content in the right column is populated from the `rightTitle` property on the group. +Adding a right sidebar is as easy as passing `rightSidebarWidth`. Content in the right column is populated from the `rightTitle` property on the group. + +Note: If you are using Custom Headers then you need to add them with `SidebarHeader` with variant "right" [Example Codesandbox](https://codesandbox.io/s/j3wrw6rl4v) @@ -63,3 +65,9 @@ Note that this is the user code manipulating groups to achieve tree group functi Using `scrollRef` you can trigger scrolling and create an animation. This is an alternative to setting `visibleStartTime` and `visibleEndTime`. [Example Codesandbox](https://codesandbox.io/s/3kq2503y8p) + +## Sticky header + +Using `Timeline Header` you can make the header stick to the top of the page while scrolling down + +[Example Codesandbox](https://codesandbox.io/s/w6xvqzno4w) diff --git a/package.json b/package.json index ea8764ba9..cd4f933d0 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "react": "^16.2.0", "react-dom": "^16.2.0", "react-router-dom": "^4.1.1", - "react-testing-library": "^5.1.0", + "react-testing-library": "^6.0.3", "rimraf": "^2.6.2", "sass-loader": "^7.0.3", "style-loader": "~0.13.0", diff --git a/src/lib/Timeline.js b/src/lib/Timeline.js index e059451e4..88304e797 100644 --- a/src/lib/Timeline.js +++ b/src/lib/Timeline.js @@ -43,11 +43,8 @@ export default class ReactCalendarTimeline extends Component { rightSidebarWidth: PropTypes.number, dragSnap: PropTypes.number, minResizeWidth: PropTypes.number, - stickyOffset: PropTypes.number, stickyHeader: PropTypes.bool, lineHeight: PropTypes.number, - headerLabelGroupHeight: PropTypes.number, - headerLabelHeight: PropTypes.number, itemHeightRatio: PropTypes.number, minZoom: PropTypes.number, @@ -170,11 +167,8 @@ export default class ReactCalendarTimeline extends Component { rightSidebarWidth: 0, dragSnap: 1000 * 60 * 15, // 15min minResizeWidth: 20, - stickyOffset: 0, stickyHeader: true, lineHeight: 30, - headerLabelGroupHeight: 30, - headerLabelHeight: 30, itemHeightRatio: 0.65, minZoom: 60 * 60 * 1000, // 1 hour @@ -644,6 +638,7 @@ export default class ReactCalendarTimeline extends Component { let time = calculateTimeForXPosition( canvasTimeStart, + canvasTimeEnd, getCanvasWidth(width), offsetX @@ -897,7 +892,6 @@ export default class ReactCalendarTimeline extends Component { groupHeights, groupTops, height, - headerHeight, visibleTimeStart, visibleTimeEnd, minUnit, @@ -926,7 +920,6 @@ export default class ReactCalendarTimeline extends Component { groupTops: groupTops, selected: this.getSelected(), height: height, - headerHeight: headerHeight, minUnit: minUnit, timeSteps: timeSteps } @@ -955,7 +948,7 @@ export default class ReactCalendarTimeline extends Component { return ( - + {this.props.rightSidebarWidth ? : null} @@ -986,8 +979,6 @@ export default class ReactCalendarTimeline extends Component { const { items, groups, - headerLabelGroupHeight, - headerLabelHeight, sidebarWidth, rightSidebarWidth, timeSteps, @@ -1007,7 +998,6 @@ export default class ReactCalendarTimeline extends Component { const zoom = visibleTimeEnd - visibleTimeStart const canvasWidth = getCanvasWidth(width) const minUnit = getMinUnit(zoom, width, timeSteps) - const headerHeight = headerLabelGroupHeight + headerLabelHeight const isInteractingWithItem = !!draggingItem || !!resizingItem @@ -1095,7 +1085,6 @@ export default class ReactCalendarTimeline extends Component { minUnit, timeSteps, height, - headerHeight )} {this.rows(canvasWidth, groupHeights, groups)} {this.infoLabel()} @@ -1107,7 +1096,6 @@ export default class ReactCalendarTimeline extends Component { groupHeights, groupTops, height, - headerHeight, visibleTimeStart, visibleTimeEnd, minUnit, diff --git a/src/lib/Timeline.scss b/src/lib/Timeline.scss index 7fd88e0f5..93a8d58ab 100644 --- a/src/lib/Timeline.scss +++ b/src/lib/Timeline.scss @@ -13,10 +13,6 @@ $border-width: 1px; $thick-border-width: 2px; $sidebar-color: #ffffff; $sidebar-background-color: #c52020; -$header-color: #ffffff; -$header-background-color: #c52020; -$lower-header-color: #333333; -$lower-header-background-color: #f0f0f0; $list-item-padding: 0 4px; $weekend: rgba(250, 246, 225, 0.5); @@ -60,17 +56,6 @@ $weekend: rgba(250, 246, 225, 0.5); } } - .rct-header-container { - z-index: 90; - display: flex; - overflow: hidden; - - &.header-sticky { - position: sticky; - position: -webkit-sticky; - } - } - .rct-sidebar { overflow: hidden; white-space: normal; // was set to nowrap in .rct-outer @@ -149,8 +134,39 @@ $weekend: rgba(250, 246, 225, 0.5); background: rgba(0, 0, 0, 0.5); color: white; padding: 10px; + font-size: 20px; border-radius: 5px; z-index: 85; } + + .rct-dateHeader { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + border-bottom: 1px solid #bbb; + cursor: pointer; + font-size: 14px; + background-color: rgb(240, 240, 240); + border-left: 2px solid #bbb; + } + + .rct-dateHeader-primary { + background-color: initial; + border-left: 1px solid #bbb; + border-right: 1px solid #bbb; + color: #fff + } + + .rct-header-root { + background: #c52020; + border-bottom: 1px solid #bbb; + } + + .rct-calendar-header { + border: 1px solid #bbb, + + } + } diff --git a/src/lib/headers/CustomHeader.js b/src/lib/headers/CustomHeader.js index b1cc46103..a07bfc8c5 100644 --- a/src/lib/headers/CustomHeader.js +++ b/src/lib/headers/CustomHeader.js @@ -9,16 +9,17 @@ export class CustomHeader extends React.Component { //component props children: PropTypes.func.isRequired, unit: PropTypes.string.isRequired, - timeSteps: PropTypes.object.isRequired, //Timeline context + timeSteps: PropTypes.object.isRequired, visibleTimeStart: PropTypes.number.isRequired, visibleTimeEnd: PropTypes.number.isRequired, canvasTimeStart: PropTypes.number.isRequired, canvasTimeEnd: PropTypes.number.isRequired, canvasWidth: PropTypes.number.isRequired, showPeriod: PropTypes.func.isRequired, - props: PropTypes.object, + headerData: PropTypes.object, getLeftOffsetFromDate: PropTypes.func.isRequired, + height: PropTypes.number.isRequired, } constructor(props) { super(props) @@ -29,13 +30,9 @@ export class CustomHeader extends React.Component { unit, timeSteps, showPeriod, - getLeftOffsetFromDate, + getLeftOffsetFromDate } = props - const ratio = this.calculateRatio( - canvasWidth, - canvasTimeEnd, - canvasTimeStart - ) + const intervals = this.getHeaderIntervals({ canvasTimeStart, canvasTimeEnd, @@ -43,12 +40,11 @@ export class CustomHeader extends React.Component { unit, timeSteps, showPeriod, - getLeftOffsetFromDate, + getLeftOffsetFromDate }) this.state = { - intervals, - ratio + intervals } } @@ -60,7 +56,8 @@ export class CustomHeader extends React.Component { nextProps.unit !== this.props.unit || nextProps.timeSteps !== this.props.timeSteps || nextProps.showPeriod !== this.props.showPeriod || - nextProps.children !== this.props.children + nextProps.children !== this.props.children || + nextProps.headerData !== this.props.headerData ) { return true } @@ -83,13 +80,9 @@ export class CustomHeader extends React.Component { unit, timeSteps, showPeriod, - getLeftOffsetFromDate, + getLeftOffsetFromDate } = nextProps - const ratio = this.calculateRatio( - canvasWidth, - canvasTimeEnd, - canvasTimeStart - ) + const intervals = this.getHeaderIntervals({ canvasTimeStart, canvasTimeEnd, @@ -97,10 +90,10 @@ export class CustomHeader extends React.Component { unit, timeSteps, showPeriod, - getLeftOffsetFromDate, + getLeftOffsetFromDate }) - this.setState({ intervals, ratio }) + this.setState({ intervals }) } } @@ -109,7 +102,7 @@ export class CustomHeader extends React.Component { canvasTimeEnd, unit, timeSteps, - getLeftOffsetFromDate, + getLeftOffsetFromDate }) => { const intervals = [] iterateTimes( @@ -120,37 +113,33 @@ export class CustomHeader extends React.Component { (startTime, endTime) => { const left = getLeftOffsetFromDate(startTime.valueOf()) const right = getLeftOffsetFromDate(endTime.valueOf()) - const width = right-left + const width = right - left intervals.push({ startTime, endTime, labelWidth: width, - left, + left }) } ) return intervals } - rootProps = { - style: { - position: 'relative' - } - } - getRootProps = (props = {}) => { const { style } = props return { style: Object.assign({}, style ? style : {}, { position: 'relative', - width: this.props.canvasWidth + width: this.props.canvasWidth, + height: this.props.height, }) } } getIntervalProps = (props = {}) => { const { interval, style } = props - if (!interval) throw new Error("you should provide interval to the prop getter") + if (!interval) + throw new Error('you should provide interval to the prop getter') const { startTime, labelWidth, left } = interval return { style: this.getIntervalStyle({ @@ -159,19 +148,13 @@ export class CustomHeader extends React.Component { labelWidth, canvasTimeStart: this.props.canvasTimeStart, unit: this.props.unit, - ratio: this.state.ratio, - left, + left }), key: `label-${startTime.valueOf()}` } } - calculateRatio(canvasWidth, canvasTimeEnd, canvasTimeStart) { - return canvasWidth / (canvasTimeEnd - canvasTimeStart) - } - - getIntervalStyle = ({ startTime, canvasTimeStart, ratio, unit, left,labelWidth, style, }) => { - + getIntervalStyle = ({ left, labelWidth, style }) => { return { ...style, left, @@ -188,7 +171,8 @@ export class CustomHeader extends React.Component { showPeriod, timelineWidth, visibleTimeStart, - visibleTimeEnd + visibleTimeEnd, + headerData, } = this.props //TODO: only evaluate on changing params return { @@ -205,17 +189,19 @@ export class CustomHeader extends React.Component { }, getRootProps: this.getRootProps, getIntervalProps: this.getIntervalProps, - showPeriod + showPeriod, + data: headerData, } } render() { const props = this.getStateAndHelpers() - return this.props.children(props, this.props.props) + const Renderer = this.props.children + return } } -const CustomHeaderWrapper = ({ children, unit, props }) => ( +const CustomHeaderWrapper = ({ children, unit, headerData, height }) => ( {({ getTimelineState, showPeriod, getLeftOffsetFromDate }) => { const timelineState = getTimelineState() @@ -228,8 +214,9 @@ const CustomHeaderWrapper = ({ children, unit, props }) => ( showPeriod={showPeriod} unit={unit ? unit : timelineState.timelineUnit} {...timelineState} - props={props} + headerData={headerData} getLeftOffsetFromDate={getLeftOffsetFromDate} + height={height} /> )} @@ -241,7 +228,12 @@ const CustomHeaderWrapper = ({ children, unit, props }) => ( CustomHeaderWrapper.propTypes = { children: PropTypes.func.isRequired, unit: PropTypes.string, - props: PropTypes.object, + headerData: PropTypes.object, + height: PropTypes.number, +} + +CustomHeaderWrapper.defaultProps = { + height: 30, } export default CustomHeaderWrapper diff --git a/src/lib/headers/DateHeader.js b/src/lib/headers/DateHeader.js index d5a1d3962..43092824f 100644 --- a/src/lib/headers/DateHeader.js +++ b/src/lib/headers/DateHeader.js @@ -8,8 +8,6 @@ import Interval from './Interval' class DateHeader extends React.PureComponent { static propTypes = { - primaryHeader: PropTypes.bool, - secondaryHeader: PropTypes.bool, unit: PropTypes.string, style: PropTypes.object, className: PropTypes.string, @@ -20,34 +18,55 @@ class DateHeader extends React.PureComponent { PropTypes.string ]).isRequired, intervalRenderer: PropTypes.func, - props: PropTypes.object, + headerData: PropTypes.object, + height: PropTypes.number, } getHeaderUnit = () => { - if (this.props.unit) { - return this.props.unit - } else if (this.props.primaryHeader) { + if (this.props.unit === 'primaryHeader') { return getNextUnit(this.props.timelineUnit) + } else if (this.props.unit) { + return this.props.unit } return this.props.timelineUnit } + getRootStyle = () => { + return { + height: 30, + ...this.props.style + } + } + + getLabelFormat(interval, unit, labelWidth) { + const { labelFormat } = this.props + if (typeof labelFormat === 'string') { + const startTime = interval[0] + return startTime.format(labelFormat) + } else if (typeof labelFormat === 'function') { + return labelFormat(interval, unit, labelWidth) + } else { + throw new Error('labelFormat should be function or string') + } + } + render() { const unit = this.getHeaderUnit() - const {props} = this.props; + const { headerData, height } = this.props return ( - + {({ headerContext: { intervals }, getRootProps, getIntervalProps, - showPeriod - }, props) => { + showPeriod, + data + }) => { const unit = this.getHeaderUnit() return (
@@ -64,11 +83,10 @@ class DateHeader extends React.PureComponent { interval={interval} showPeriod={showPeriod} intervalText={intervalText} - primaryHeader={!!this.props.primaryHeader} - secondaryHeader={!!this.props.secondaryHeader} + primaryHeader={this.props.unit === 'primaryHeader'} getIntervalProps={getIntervalProps} intervalRenderer={this.props.intervalRenderer} - props={props} + headerData={data} /> ) })} @@ -78,38 +96,16 @@ class DateHeader extends React.PureComponent { ) } - - getRootStyle = () => { - return { - height: 30, - ...this.props.style - } - } - - getLabelFormat(interval, unit, labelWidth) { - const { labelFormat } = this.props - if (typeof labelFormat === 'string') { - const startTime = interval[0] - return startTime.format(labelFormat) - } else if (typeof labelFormat === 'object') { - return formatLabel(interval, unit, labelWidth, labelFormat) - } else if (typeof labelFormat === 'function') { - return labelFormat(interval, unit, labelWidth) - } else { - throw new Error('labelFormat should be function, object or string') - } - } } const DateHeaderWrapper = ({ - primaryHeader, - secondaryHeader, unit, labelFormat, style, className, intervalRenderer, - props, + headerData, + height, }) => ( {({ getTimelineState }) => { @@ -117,14 +113,13 @@ const DateHeaderWrapper = ({ return ( ) }} @@ -134,8 +129,6 @@ const DateHeaderWrapper = ({ DateHeaderWrapper.propTypes = { style: PropTypes.object, className: PropTypes.string, - primaryHeader: PropTypes.bool, - secondaryHeader: PropTypes.bool, unit: PropTypes.string, labelFormat: PropTypes.oneOfType([ PropTypes.func, @@ -143,11 +136,11 @@ DateHeaderWrapper.propTypes = { PropTypes.string ]), intervalRenderer: PropTypes.func, - props: PropTypes.object, + headerData: PropTypes.object, + height: PropTypes.number, } DateHeaderWrapper.defaultProps = { - secondaryHeader: true, labelFormat: formatLabel } diff --git a/src/lib/headers/Interval.js b/src/lib/headers/Interval.js index 7da76f3a7..25abef2ea 100644 --- a/src/lib/headers/Interval.js +++ b/src/lib/headers/Interval.js @@ -11,30 +11,8 @@ class Interval extends React.PureComponent { showPeriod: PropTypes.func.isRequired, intervalText: PropTypes.string.isRequired, primaryHeader: PropTypes.bool.isRequired, - secondaryHeader: PropTypes.bool.isRequired, getIntervalProps: PropTypes.func.isRequired, - props: PropTypes.object - } - - getIntervalStyle = () => { - return { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: - this.props.secondaryHeader && !this.props.primaryHeader - ? 'rgb(240, 240, 240)' - : 'initial', - height: '100%', - borderLeft: this.props.primaryHeader - ? '1px solid #bbb' - : '2px solid #bbb', - borderRight: this.props.primaryHeader ? '1px solid #bbb' : 'none', - borderBottom: '1px solid #bbb', - color: this.props.primaryHeader ? '#fff' : 'initial', - cursor: 'pointer', - fontSize: '14px' - } + headerData: PropTypes.object } onIntervalClick = () => { @@ -49,7 +27,7 @@ class Interval extends React.PureComponent { } } - getIntervalProps = (props={}) => { + getIntervalProps = (props = {}) => { return { ...this.props.getIntervalProps({ interval: this.props.interval, @@ -60,20 +38,27 @@ class Interval extends React.PureComponent { } render() { - const { intervalText, interval, intervalRenderer, props } = this.props - if (intervalRenderer) - return intervalRenderer({ - getIntervalProps: this.getIntervalProps, - intervalContext: { - interval, - intervalText - } - }, props) + const { intervalText, interval, intervalRenderer, headerData } = this.props + const Renderer = intervalRenderer + if (Renderer) { + return ( + + ) + } + return (
{intervalText}
diff --git a/src/lib/headers/SidebarHeader.js b/src/lib/headers/SidebarHeader.js index 3ad4aa603..8f0f79b5e 100644 --- a/src/lib/headers/SidebarHeader.js +++ b/src/lib/headers/SidebarHeader.js @@ -9,7 +9,7 @@ class SidebarHeader extends React.PureComponent { rightSidebarWidth: PropTypes.number, leftSidebarWidth: PropTypes.number.isRequired, variant: PropTypes.string, - props: PropTypes.object + headerData: PropTypes.object } getRootProps = (props = {}) => { @@ -20,25 +20,27 @@ class SidebarHeader extends React.PureComponent { : this.props.leftSidebarWidth return { style: { + ...style, width, - ...style } } } getStateAndHelpers = () => { return { - getRootProps: this.getRootProps + getRootProps: this.getRootProps, + data: this.props.headerData, } } render() { const props = this.getStateAndHelpers() - return this.props.children(props, this.props.props) + const Renderer = this.props.children + return } } -const SidebarWrapper = ({ children, variant, props }) => ( +const SidebarWrapper = ({ children, variant, headerData }) => ( {({ leftSidebarWidth, rightSidebarWidth }) => { return ( @@ -47,7 +49,7 @@ const SidebarWrapper = ({ children, variant, props }) => ( rightSidebarWidth={rightSidebarWidth} children={children} variant={variant} - props={props} + headerData={headerData} /> ) }} @@ -57,7 +59,7 @@ const SidebarWrapper = ({ children, variant, props }) => ( SidebarWrapper.propTypes = { children: PropTypes.func.isRequired, variant: PropTypes.string, - props: PropTypes.object + headerData: PropTypes.object } SidebarWrapper.defaultProps = { diff --git a/src/lib/headers/TimelineHeaders.js b/src/lib/headers/TimelineHeaders.js index 080513253..05f0124ce 100644 --- a/src/lib/headers/TimelineHeaders.js +++ b/src/lib/headers/TimelineHeaders.js @@ -13,7 +13,8 @@ class TimelineHeaders extends React.PureComponent { className: PropTypes.string, calendarHeaderStyle: PropTypes.object, calendarHeaderClassName: PropTypes.string, - width: PropTypes.number.isRequired + width: PropTypes.number.isRequired, + headerRef: PropTypes.func, } constructor(props) { @@ -22,8 +23,6 @@ class TimelineHeaders extends React.PureComponent { getRootStyle = () => { return { - background: '#c52020', - borderBottom: '1px solid #bbb', ...this.props.style, display: 'flex', width: 'max-content' @@ -37,13 +36,18 @@ class TimelineHeaders extends React.PureComponent { calendarHeaderStyle } = this.props return { - border: '1px solid #bbb', ...calendarHeaderStyle, overflow: 'hidden', width: this.props.width } } + handleRootRef = (element) => { + if(this.props.headerRef){ + this.props.headerRef(element) + } + } + render() { let rightSidebarHeader let leftSidebarHeader @@ -52,27 +56,28 @@ class TimelineHeaders extends React.PureComponent { ? this.props.children.filter(c => c) : [this.props.children] React.Children.map(children, child => { - if ( - child.type === SidebarHeader && - child.props.variant === RIGHT_VARIANT - ) { - rightSidebarHeader = child - } else if ( - child.type === SidebarHeader && - child.props.variant === LEFT_VARIANT - ) { - leftSidebarHeader = child + if (child.type === SidebarHeader) { + if (child.props.variant === RIGHT_VARIANT) { + rightSidebarHeader = child + } else { + leftSidebarHeader = child + } } else { calendarHeaders.push(child) } }) return ( -
+
{leftSidebarHeader}
{calendarHeaders} @@ -120,7 +125,8 @@ TimelineHeadersWrapper.propTypes = { style: PropTypes.object, className: PropTypes.string, calendarHeaderStyle: PropTypes.object, - calendarHeaderClassName: PropTypes.string + calendarHeaderClassName: PropTypes.string, + headerRef: PropTypes.func, } export default TimelineHeadersWrapper diff --git a/src/lib/markers/TimelineMarkersContext.js b/src/lib/markers/TimelineMarkersContext.js index f71e5bd1c..474a36e28 100644 --- a/src/lib/markers/TimelineMarkersContext.js +++ b/src/lib/markers/TimelineMarkersContext.js @@ -42,7 +42,7 @@ export class TimelineMarkersProvider extends React.Component { unsubscribe: () => { this.setState(state => { return { - markers: state.markers.filter(marker => marker !== newMarker) + markers: state.markers.filter(marker => marker.id !== newMarker.id) } }) }, diff --git a/yarn.lock b/yarn.lock index 2ce970fe8..d2f8d167f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,10 +10,35 @@ esutils "^2.0.2" js-tokens "^3.0.0" +"@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.2.tgz#f5ab6897320f16decd855eed70b705908a313fe8" + dependencies: + regenerator-runtime "^0.13.2" + +"@jest/types@^24.5.0": + version "24.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.5.0.tgz#feee214a4d0167b0ca447284e95a57aa10b3ee95" + dependencies: + "@types/istanbul-lib-coverage" "^1.1.0" + "@types/yargs" "^12.0.9" + +"@sheerun/mutationobserver-shim@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" + +"@types/istanbul-lib-coverage@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a" + "@types/node@*": version "9.3.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.3.0.tgz#3a129cda7c4e5df2409702626892cb4b96546dd5" +"@types/yargs@^12.0.9": + version "12.0.10" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.10.tgz#17a8ec65cd8e88f51b418ceb271af18d3137df67" + "@webassemblyjs/ast@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" @@ -267,6 +292,10 @@ ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" +ansi-regex@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -685,12 +714,12 @@ babel-jest@^22.0.6: babel-plugin-istanbul "^4.1.5" babel-preset-jest "^22.0.6" -babel-jest@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.0.1.tgz#bbad3bf523fb202da05ed0a6540b48c84eed13a6" +babel-jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.6.0.tgz#a644232366557a2240a0c083da6b25786185a2f1" dependencies: babel-plugin-istanbul "^4.1.6" - babel-preset-jest "^23.0.1" + babel-preset-jest "^23.2.0" babel-loader@^7.1.5: version "7.1.5" @@ -733,9 +762,9 @@ babel-plugin-jest-hoist@^22.0.6: version "22.0.6" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.0.6.tgz#551269ded350a15d6585da35d16d449df30d66c4" -babel-plugin-jest-hoist@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.0.1.tgz#eaa11c964563aea9c21becef2bdf7853f7f3c148" +babel-plugin-jest-hoist@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167" babel-plugin-react-remove-properties@^0.2.5: version "0.2.5" @@ -1073,11 +1102,11 @@ babel-preset-jest@^22.0.6: babel-plugin-jest-hoist "^22.0.6" babel-plugin-syntax-object-rest-spread "^6.13.0" -babel-preset-jest@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.0.1.tgz#631cc545c6cf021943013bcaf22f45d87fe62198" +babel-preset-jest@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46" dependencies: - babel-plugin-jest-hoist "^23.0.1" + babel-plugin-jest-hoist "^23.2.0" babel-plugin-syntax-object-rest-spread "^6.13.0" babel-preset-react@^6.5.0: @@ -1120,7 +1149,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: babylon "^6.18.0" lodash "^4.17.4" -babel-traverse@^6.18.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0: +babel-traverse@^6.0.0, babel-traverse@^6.18.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: @@ -1134,7 +1163,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-tr invariant "^2.2.2" lodash "^4.17.4" -babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.26.0: +babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" dependencies: @@ -1297,9 +1326,9 @@ browser-process-hrtime@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" -browser-resolve@^1.11.2: - version "1.11.2" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" dependencies: resolve "1.1.7" @@ -2302,13 +2331,14 @@ dom-serializer@0, dom-serializer@~0.1.0: domelementtype "~1.1.1" entities "~1.1.1" -dom-testing-library@^3.1.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.6.0.tgz#e5598fae9477f3918e22aee44f2262727203b5bd" +dom-testing-library@^3.18.2: + version "3.18.2" + resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.18.2.tgz#07d65166743ad3299b7bee5b488e9622c31241bc" dependencies: - mutationobserver-shim "^0.3.2" - pretty-format "^22.4.3" - wait-for-expect "^1.0.0" + "@babel/runtime" "^7.3.4" + "@sheerun/mutationobserver-shim" "^0.3.2" + pretty-format "^24.5.0" + wait-for-expect "^1.1.0" domain-browser@^1.1.1: version "1.1.7" @@ -2758,16 +2788,16 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expect@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-23.1.0.tgz#bfdfd57a2a20170d875999ee9787cc71f01c205f" +expect@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98" dependencies: ansi-styles "^3.2.0" - jest-diff "^23.0.1" + jest-diff "^23.6.0" jest-get-type "^22.1.0" - jest-matcher-utils "^23.0.1" - jest-message-util "^23.1.0" - jest-regex-util "^23.0.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" express@^4.16.2: version "4.16.3" @@ -3721,7 +3751,7 @@ invariant@^2.2.1: dependencies: loose-envify "^1.0.0" -invariant@^2.2.2: +invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -4112,15 +4142,15 @@ istanbul-reports@^1.3.0: dependencies: handlebars "^4.0.3" -jest-changed-files@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.0.1.tgz#f79572d0720844ea5df84c2a448e862c2254f60c" +jest-changed-files@^23.4.2: + version "23.4.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83" dependencies: throat "^4.0.0" -jest-cli@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.1.0.tgz#eb8bdd4ce0d15250892e31ad9b69bc99d2a8f6bf" +jest-cli@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.6.0.tgz#61ab917744338f443ef2baa282ddffdd658a5da4" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" @@ -4133,23 +4163,24 @@ jest-cli@^23.1.0: istanbul-lib-coverage "^1.2.0" istanbul-lib-instrument "^1.10.1" istanbul-lib-source-maps "^1.2.4" - jest-changed-files "^23.0.1" - jest-config "^23.1.0" - jest-environment-jsdom "^23.1.0" + jest-changed-files "^23.4.2" + jest-config "^23.6.0" + jest-environment-jsdom "^23.4.0" jest-get-type "^22.1.0" - jest-haste-map "^23.1.0" - jest-message-util "^23.1.0" - jest-regex-util "^23.0.0" - jest-resolve-dependencies "^23.0.1" - jest-runner "^23.1.0" - jest-runtime "^23.1.0" - jest-snapshot "^23.0.1" - jest-util "^23.1.0" - jest-validate "^23.0.1" - jest-watcher "^23.1.0" - jest-worker "^23.0.1" + jest-haste-map "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + jest-resolve-dependencies "^23.6.0" + jest-runner "^23.6.0" + jest-runtime "^23.6.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + jest-watcher "^23.4.0" + jest-worker "^23.2.0" micromatch "^2.3.11" node-notifier "^5.2.1" + prompts "^0.1.9" realpath-native "^1.0.0" rimraf "^2.5.4" slash "^1.0.0" @@ -4158,23 +4189,24 @@ jest-cli@^23.1.0: which "^1.2.12" yargs "^11.0.0" -jest-config@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.1.0.tgz#708ca0f431d356ee424fb4895d3308006bdd8241" +jest-config@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.6.0.tgz#f82546a90ade2d8c7026fbf6ac5207fc22f8eb1d" dependencies: babel-core "^6.0.0" - babel-jest "^23.0.1" + babel-jest "^23.6.0" chalk "^2.0.1" glob "^7.1.1" - jest-environment-jsdom "^23.1.0" - jest-environment-node "^23.1.0" + jest-environment-jsdom "^23.4.0" + jest-environment-node "^23.4.0" jest-get-type "^22.1.0" - jest-jasmine2 "^23.1.0" - jest-regex-util "^23.0.0" - jest-resolve "^23.1.0" - jest-util "^23.1.0" - jest-validate "^23.0.1" - pretty-format "^23.0.1" + jest-jasmine2 "^23.6.0" + jest-regex-util "^23.3.0" + jest-resolve "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + micromatch "^2.3.11" + pretty-format "^23.6.0" jest-diff@^22.4.3: version "22.4.3" @@ -4185,22 +4217,22 @@ jest-diff@^22.4.3: jest-get-type "^22.4.3" pretty-format "^22.4.3" -jest-diff@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.0.1.tgz#3d49137cee12c320a4b4d2b4a6fa6e82d491a16a" +jest-diff@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d" dependencies: chalk "^2.0.1" diff "^3.2.0" jest-get-type "^22.1.0" - pretty-format "^23.0.1" + pretty-format "^23.6.0" jest-docblock@^21.0.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" -jest-docblock@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.0.1.tgz#deddd18333be5dc2415260a04ef3fce9276b5725" +jest-docblock@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.2.0.tgz#f085e1f18548d99fdd69b20207e6fd55d91383a7" dependencies: detect-newline "^2.1.0" @@ -4215,65 +4247,67 @@ jest-dom@^1.12.1: pretty-format "^23.0.1" redent "^2.0.0" -jest-each@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.1.0.tgz#16146b592c354867a5ae5e13cdf15c6c65b696c6" +jest-each@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.6.0.tgz#ba0c3a82a8054387016139c733a05242d3d71575" dependencies: chalk "^2.0.1" - pretty-format "^23.0.1" + pretty-format "^23.6.0" -jest-environment-jsdom@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.1.0.tgz#85929914e23bed3577dac9755f4106d0697c479c" +jest-environment-jsdom@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023" dependencies: - jest-mock "^23.1.0" - jest-util "^23.1.0" + jest-mock "^23.2.0" + jest-util "^23.4.0" jsdom "^11.5.1" -jest-environment-node@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.1.0.tgz#452c0bf949cfcbbacda1e1762eeed70bc784c7d5" +jest-environment-node@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10" dependencies: - jest-mock "^23.1.0" - jest-util "^23.1.0" + jest-mock "^23.2.0" + jest-util "^23.4.0" jest-get-type@^22.1.0, jest-get-type@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" -jest-haste-map@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.1.0.tgz#18e6c7d5a8d27136f91b7d9852f85de0c7074c49" +jest-haste-map@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.6.0.tgz#2e3eb997814ca696d62afdb3f2529f5bbc935e16" dependencies: fb-watchman "^2.0.0" graceful-fs "^4.1.11" - jest-docblock "^23.0.1" + invariant "^2.2.4" + jest-docblock "^23.2.0" jest-serializer "^23.0.1" - jest-worker "^23.0.1" + jest-worker "^23.2.0" micromatch "^2.3.11" sane "^2.0.0" -jest-jasmine2@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.1.0.tgz#4afab31729b654ddcd2b074add849396f13b30b8" +jest-jasmine2@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz#840e937f848a6c8638df24360ab869cc718592e0" dependencies: + babel-traverse "^6.0.0" chalk "^2.0.1" co "^4.6.0" - expect "^23.1.0" + expect "^23.6.0" is-generator-fn "^1.0.0" - jest-diff "^23.0.1" - jest-each "^23.1.0" - jest-matcher-utils "^23.0.1" - jest-message-util "^23.1.0" - jest-snapshot "^23.0.1" - jest-util "^23.1.0" - pretty-format "^23.0.1" + jest-diff "^23.6.0" + jest-each "^23.6.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + pretty-format "^23.6.0" -jest-leak-detector@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.0.1.tgz#9dba07505ac3495c39d3ec09ac1e564599e861a0" +jest-leak-detector@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz#e4230fd42cf381a1a1971237ad56897de7e171de" dependencies: - pretty-format "^23.0.1" + pretty-format "^23.6.0" jest-matcher-utils@^22.4.3: version "22.4.3" @@ -4283,17 +4317,17 @@ jest-matcher-utils@^22.4.3: jest-get-type "^22.4.3" pretty-format "^22.4.3" -jest-matcher-utils@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.0.1.tgz#0c6c0daedf9833c2a7f36236069efecb4c3f6e5f" +jest-matcher-utils@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz#726bcea0c5294261a7417afb6da3186b4b8cac80" dependencies: chalk "^2.0.1" jest-get-type "^22.1.0" - pretty-format "^23.0.1" + pretty-format "^23.6.0" -jest-message-util@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.1.0.tgz#9a809ba487ecac5ce511d4e698ee3b5ee2461ea9" +jest-message-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f" dependencies: "@babel/code-frame" "^7.0.0-beta.35" chalk "^2.0.1" @@ -4301,50 +4335,50 @@ jest-message-util@^23.1.0: slash "^1.0.0" stack-utils "^1.0.1" -jest-mock@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.1.0.tgz#a381c31b121ab1f60c462a2dadb7b86dcccac487" +jest-mock@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134" -jest-regex-util@^23.0.0: - version "23.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.0.0.tgz#dd5c1fde0c46f4371314cf10f7a751a23f4e8f76" +jest-regex-util@^23.3.0: + version "23.3.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5" -jest-resolve-dependencies@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.0.1.tgz#d01a10ddad9152c4cecdf5eac2b88571c4b6a64d" +jest-resolve-dependencies@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz#b4526af24c8540d9a3fab102c15081cf509b723d" dependencies: - jest-regex-util "^23.0.0" - jest-snapshot "^23.0.1" + jest-regex-util "^23.3.0" + jest-snapshot "^23.6.0" -jest-resolve@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.1.0.tgz#b9e316eecebd6f00bc50a3960d1527bae65792d2" +jest-resolve@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.6.0.tgz#cf1d1a24ce7ee7b23d661c33ba2150f3aebfa0ae" dependencies: - browser-resolve "^1.11.2" + browser-resolve "^1.11.3" chalk "^2.0.1" realpath-native "^1.0.0" -jest-runner@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.1.0.tgz#fa20a933fff731a5432b3561e7f6426594fa29b5" +jest-runner@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.6.0.tgz#3894bd219ffc3f3cb94dc48a4170a2e6f23a5a38" dependencies: exit "^0.1.2" graceful-fs "^4.1.11" - jest-config "^23.1.0" - jest-docblock "^23.0.1" - jest-haste-map "^23.1.0" - jest-jasmine2 "^23.1.0" - jest-leak-detector "^23.0.1" - jest-message-util "^23.1.0" - jest-runtime "^23.1.0" - jest-util "^23.1.0" - jest-worker "^23.0.1" + jest-config "^23.6.0" + jest-docblock "^23.2.0" + jest-haste-map "^23.6.0" + jest-jasmine2 "^23.6.0" + jest-leak-detector "^23.6.0" + jest-message-util "^23.4.0" + jest-runtime "^23.6.0" + jest-util "^23.4.0" + jest-worker "^23.2.0" source-map-support "^0.5.6" throat "^4.0.0" -jest-runtime@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.1.0.tgz#b4ae0e87259ecacfd4a884b639db07cf4dd620af" +jest-runtime@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.6.0.tgz#059e58c8ab445917cd0e0d84ac2ba68de8f23082" dependencies: babel-core "^6.0.0" babel-plugin-istanbul "^4.1.6" @@ -4353,14 +4387,14 @@ jest-runtime@^23.1.0: exit "^0.1.2" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.1.11" - jest-config "^23.1.0" - jest-haste-map "^23.1.0" - jest-message-util "^23.1.0" - jest-regex-util "^23.0.0" - jest-resolve "^23.1.0" - jest-snapshot "^23.0.1" - jest-util "^23.1.0" - jest-validate "^23.0.1" + jest-config "^23.6.0" + jest-haste-map "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + jest-resolve "^23.6.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" micromatch "^2.3.11" realpath-native "^1.0.0" slash "^1.0.0" @@ -4372,38 +4406,42 @@ jest-serializer@^23.0.1: version "23.0.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165" -jest-snapshot@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.0.1.tgz#6674fa19b9eb69a99cabecd415bddc42d6af3e7e" +jest-snapshot@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.6.0.tgz#f9c2625d1b18acda01ec2d2b826c0ce58a5aa17a" dependencies: + babel-types "^6.0.0" chalk "^2.0.1" - jest-diff "^23.0.1" - jest-matcher-utils "^23.0.1" + jest-diff "^23.6.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-resolve "^23.6.0" mkdirp "^0.5.1" natural-compare "^1.4.0" - pretty-format "^23.0.1" + pretty-format "^23.6.0" + semver "^5.5.0" -jest-util@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.1.0.tgz#c0251baf34644c6dd2fea78a962f4263ac55772d" +jest-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561" dependencies: callsites "^2.0.0" chalk "^2.0.1" graceful-fs "^4.1.11" is-ci "^1.0.10" - jest-message-util "^23.1.0" + jest-message-util "^23.4.0" mkdirp "^0.5.1" slash "^1.0.0" source-map "^0.6.0" -jest-validate@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.0.1.tgz#cd9f01a89d26bb885f12a8667715e9c865a5754f" +jest-validate@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474" dependencies: chalk "^2.0.1" jest-get-type "^22.1.0" leven "^2.1.0" - pretty-format "^23.0.1" + pretty-format "^23.6.0" jest-watch-typeahead@^0.1.0: version "0.1.0" @@ -4416,26 +4454,26 @@ jest-watch-typeahead@^0.1.0: string-length "^2.0.0" strip-ansi "^4.0.0" -jest-watcher@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.1.0.tgz#a8d5842e38d9fb4afff823df6abb42a58ae6cdbd" +jest-watcher@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" string-length "^2.0.0" -jest-worker@^23.0.1: - version "23.0.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-23.0.1.tgz#9e649dd963ff4046026f91c4017f039a6aa4a7bc" +jest-worker@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-23.2.0.tgz#faf706a8da36fae60eb26957257fa7b5d8ea02b9" dependencies: merge-stream "^1.0.1" -jest@^23.1.0: - version "23.1.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-23.1.0.tgz#bbb7f893100a11a742dd8bd0d047a54b0968ad1a" +jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d" dependencies: import-local "^1.0.0" - jest-cli "^23.1.0" + jest-cli "^23.6.0" js-base64@^2.1.8: version "2.4.8" @@ -4589,6 +4627,10 @@ kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" +kleur@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -5082,10 +5124,6 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" -mutationobserver-shim@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#f4d5dae7a4971a2207914fb5a90ebd514b65acca" - mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -6080,6 +6118,22 @@ pretty-format@^23.0.1: ansi-regex "^3.0.0" ansi-styles "^3.2.0" +pretty-format@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + +pretty-format@^24.5.0: + version "24.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.5.0.tgz#cc69a0281a62cd7242633fc135d6930cd889822d" + dependencies: + "@jest/types" "^24.5.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + private@^0.1.6, private@^0.1.7, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -6110,6 +6164,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +prompts@^0.1.9: + version "0.1.14" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2" + dependencies: + kleur "^2.0.1" + sisteransi "^0.1.1" + prop-types@^15.5.4, prop-types@^15.6.0: version "15.6.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" @@ -6305,6 +6366,10 @@ react-is@^16.4.1: version "16.4.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e" +react-is@^16.8.4: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + react-reconciler@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.7.0.tgz#9614894103e5f138deeeb5eabaf3ee80eb1d026d" @@ -6346,12 +6411,12 @@ react-test-renderer@^16.0.0-0: prop-types "^15.6.0" react-is "^16.4.1" -react-testing-library@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-5.1.0.tgz#ae208d9e3b7faae233409d205e287b304ec8ffdc" +react-testing-library@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-6.0.3.tgz#8b5d276a353c17ce4f7486015bb7a1c8827c442c" dependencies: - dom-testing-library "^3.1.0" - wait-for-expect "^1.0.0" + "@babel/runtime" "^7.4.2" + dom-testing-library "^3.18.2" react@^16.2.0: version "16.4.1" @@ -6456,6 +6521,10 @@ regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" @@ -6943,6 +7012,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +sisteransi@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -7780,9 +7853,9 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -wait-for-expect@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.0.1.tgz#73ab346ed56ed2ef66c380a59fd623755ceac0ce" +wait-for-expect@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.1.1.tgz#9cd10e07d52810af9e0aaf509872e38f3c3d81ae" walker@~1.0.5: version "1.0.7"