Royal Navy Design Systemv0.1.0
Framework

Compound Timeline

A collection of composable and presentation agnostic Compound Components, Hooks and a Context Provider, to help aid in the creation of scheduling based user-interfaces.

Motivation

Identify commonality across applications utilising scheduling related patterns, with the aim to abstract out a library that helps to ensure the integrity and future maintainability of applications with disparate sets of scheduling related use cases.

We identified commonality across applications in two key areas:

How something "looks and feels"

  • Somewhat but nuanced based on problem domain

How something works "under the hood"

  • DateTime manipulation

  • Positioning and sizing arbitrary components across a timeline

  • Generated data structures and state

  • Common but extensible interfaces

Live Example

Interact with the Live Example, or view more stories in our Storybook.

Live Example
April 2020
30/03
06/04
13/04
20/04
27/04
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Row 1
Event 1
Row 2
Event 2

Installation

You can use either yarn or npm to install the framework.

Command Line
1// npm
2npm install @royalnavy/css-framework @royalnavy/react-component-library
3
4// yarn
5yarn add @royalnavy/css-framework @royalnavy/react-component-library

Compound Components & Composition

In React, composition is a natural pattern of the component model. It's how we build components from other components, of varying complexity and specialisation.

The consumer can pick and choose what functionality to include in their Timeline via the declarative JSX API.

Custom Component Presentation

We aim to empower the consumer by enabling them to override the presentation of the exposed compound components:

  • Full control over look and feel (no opinion about markup or styles)

  • Consistent underlying implementation across applications

  • Single set of robust automated tests

Render Props

Render props allow us to provide custom presentation layers to our compound components by exposing any relevant internal state. See the example usage for the TimelineMonths component.

TimelineExample.js
1import React from 'react'
2
3import {
4 Timeline,
5 TimelineMonths,
6 TimelineRows
7} from '@royalnavy/react-component-library'
8
9const CustomTimelineMonth = (
10 index,
11 dayWidth,
12 daysTotal,
13 startDate
14) => {
15 return (
16 <span
17 style={{
18 display: 'inline-block',
19 width: `${dayWidth * daysTotal}px`,
20 // ...
21 }}
22 >
23 {startDate}
24 </span>
25 )
26}
27
28const ExampleTimeline = () => {
29 return (
30 <Timeline>
31 <TimelineMonths render={CustomTimelineMonth} />
32 <TimelineRows>{}</TimelineRows>
33 </Timeline>
34 )
35}

Context Provider

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

We expose the TimelineContext provider so that a consumer can create their own application specific components. The context provider exposes Timeline state and a dispatch function for dispatching reducer actions against the store.

State & Action dispatcher

In this example we have created a custom component that consumes Timeline related state and dispatches reducer actions when buttons are clicked.

ContextExample.js
1import React, { useContext } from 'react'
2
3import {
4 Timeline,
5 TimelineRows
6 TimelineContext,
7 TIMELINE_ACTIONS
8} from '@royalnavy/react-component-library'
9
10const CustomTimelineComponent = () => {
11 const { state: { months, weeks, days, options }, dispatch } = useContext(TimelineContext)
12
13 return (
14 <div>
15 <button onClick={_ => dispatch({ type: TIMELINE_ACTIONS.GET_PREV })}>Previous</button>
16 <button onClick={_ => dispatch({ type: TIMELINE_ACTIONS.GET_NEXT })}>Next</button>
17 </div>
18 )
19}
20
21const ExampleTimeline = () => {
22 return (
23 <Timeline>
24 <CustomTimelineComponent />
25 <TimelineRows>{}</TimelineRows>
26 </Timeline>
27 )
28}

Hooks

We expose some hooks in order to aid in the creation of your own custom Timeline components.

useTimelinePosition

This hook takes a startDate and optional endDate and in return exposes the width and position (in the form of an offset) of an item relative to the date range currently displayed by the Timeline.

HooksExample.js
1import React from 'react'
2
3import { useTimelinePosition } from '@royalnavy/react-component-library'
4
5const CustomTimelineComponent = ({
6 startDate,
7 endDate
8}) => {
9 const {
10 width,
11 offset,
12 startsBeforeStart,
13 startsAfterEnd
14 } = useTimelinePosition(startDate, endDate)
15
16 if (startsBeforeStart || startsAfterEnd) return null
17
18 return (
19 <div style={{
20 position: 'absolute',
21 display: 'inline-block',
22 width,
23 left: offset
24 // ...
25 }}
26 />
27 )
28}

Advanced Custom Layouts

Through the use of clever composition and custom styling, it's possible to create layouts that are either nuanced or high in complexity. Here is an example of a custom layout that adds groupings to rows:

Advanced Custom Layout - Example

Hook APIs

Here you will find comprehensive API documentation for the Timeline Hooks.

useTimelinePosition

startDateRequired

Date
Default Value
new Date()
Description

The start date of the event.

endDate

Date
Default Value
undefined
Description

The end date of the event.

Component APIs

Here you will find comprehensive API documentation for the Timeline Components.

Timeline

startDateRequired

Date
Default Value
undefined
Description

A month will display either side of this start date.

endDate

Date
Default Value
undefined
Description

Bound the timeline by the specified start and end dates.

today

Date
Default Value
new Date()
Description

Today's current date - default is the system date time.

range

Number
Default Value
3
Description

The number of months to display at any one time.

unitWidth

Number
Default Value
30
Description

The fixed width value of a single day (in pixels).

hasSide

Boolean
Default Value
false
Description

Specify whether or not to output sidebar headings.

hideScaling

Boolean
Default Value
false
Description

Specify whether to hide the scaling buttons.

hideToolbar

Boolean
Default Value
false
Description

Specify whether to hide the toolbar.

className

String
Default Value
undefined
Description

Custom CSS class to add to the component.

TimelineTodayMarker

render

Func
Default Value
Function
    todaydate
    offsetstring
Description

Supply a custom presentation layer.

TimelineSide (Deprecated)

render

Func
Default Value
undefined
Description

Supply a custom presentation layer.

TimelineMonths

render

Func
Default Value
Function
    index number
    dayWidthnumber
    daysTotalnumber
    startDatedate
Description

Supply a custom presentation layer.

TimelineWeeks

render

Func
Default Value
Function
    indexnumber
    isOddNumberboolean
    offsetPxstring
    widthPxstring
    dayWidthnumber
    daysTotalnumber
    startDatedate
Description

Supply a custom presentation layer.

TimelineDays

render

Func
Default Value
Function
    indexnumber
    dayWidthnumber
    datedate
Description

Supply a custom presentation layer.

TimelineHours

Hour blocks will not be visible at the default scale level and will be visible when scaling in from the week level.

blockSize

Number
Default Value
6
Description

Number of hours per block in a day.

render

Func
Default Value
Function
    widthnumber
    timestring
Description

Supply a custom presentation layer.

TimelineRows

childrenRequired

React.ReactNode | React.ReactNode[]
Default Value
undefined
Description

Supply children to be rendered.

render

Func
Default Value
Function
    indexnumber
    isOddNumberboolean
    offsetPxstring
    widthPxstring
Description

Supply a custom presentation layer.

TimelineRow

childrenRequired

React.ReactNode | React.ReactNode[]
Default Value
undefined
Description

Supply children to be rendered.

nameRequired

String
Default Value
undefined

css

CSSProp
Default Value
undefined
Description

A styled-components css`` value to modify the CSS.

contentProps

{ css?: CSSProp, 'data-testid'?: string }
Default Value
undefined
Description

Ability to pass props to the content div of the row.

headerProps

{ css?: CSSProp, 'data-testid'?: string }
Default Value
undefined
Description

Ability to pass props to the header div of the row.

TimelineEvents

childrenRequired

React.ReactNode | React.ReactNode[]
Default Value
undefined
Description

Supply children to be rendered.

TimelineEvent

startDateRequired

Date
Default Value
undefined
Description

The start date of the event.

endDateRequired

Date
Default Value
undefined
Description

The end date of the event.

childrenRequired

React.ReactNode | React.ReactNode[]
Default Value
undefined
Description

Supply children to be rendered.

render

Func
Default Value
Function
    startDatedate
    endDatedate
    widthPxstring
    offsetPxstring
Description

Supply a custom presentation layer.

barColor

String
Default Value
ColorSuccess500
Description

The colour of the bar.

Roadmap

  • Iterate upon default presentation (user research and design)

  • Investigate support for advanced features:

    • Infinite scroll

    • Lazy loading

    • Drag and drop

Contributing

The contributing guide resource presents information about our development process.

Changelog

If you have recently updated then read the release notes.