Royal Navy Design Systemv2.10.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
May 2020
June 2020
30/03
06/04
13/04
20/04
27/04
04/05
11/05
18/05
25/05
01/06
08/06
15/06
22/06
29/06
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
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
31
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
Event 1
Event 2

Installation

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

Command Line
// npm
npm install @royalnavy/css-framework @royalnavy/react-component-library

// yarn
yarn add @royalnavy/css-framework @royalnavy/react-component-library

Options

startDateRequired

Date
Default Value
-
Description

A month will display either side of this start date.

endDate

Date
Default Value
-
Description

Bound the timeline by the specified start and end dates.

today

Date
Default Value
new Date()
Description

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

range

Number
Default Value
3
Description

The number of months to display at any one time.

dayWidth

Number
Default Value
30
Description

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

TimelineExample.js
import React from 'react'

import { 
  Timeline, 
  TimelineTodayMarker,
  TimelineSide,
  TimelineMonths, 
  TimelineWeeks, 
  TimelineDays, 
  TimelineRows, 
  TimelineRow, 
  TimelineEvents, 
  TimelineEvent 
} from '@royalnavy/react-component-library'

const ExampleTimeline = () => {
  return (
    <Timeline 
      startDate={new Date(2020, 4, 0)} 
      today={new Date(2020, 3, 15)} 
      range={3}
      dayWidth={30}
    >
      <TimelineTodayMarker />
      <TimelineSide/>
      <TimelineMonths />
      <TimelineWeeks />
      <TimelineDays />
      <TimelineRows>
        <TimelineRow name="Row 1">
          <TimelineEvents>
            <TimelineEvent
              startDate={new Date(2020, 3, 14)}
              endDate={new Date(2020, 3, 18)}
            >
              Event 1
            </TimelineEvent>
          </TimelineEvents>
        </TimelineRow>
        <TimelineRow name="Row 2">
          <TimelineEvents>
            <TimelineEvent
              startDate={new Date(2020, 3, 3)}
              endDate={new Date(2020, 3, 8)}
            >
              Event 2
            </TimelineEvent>
          </TimelineEvents>
        </TimelineRow>
      </TimelineRows>
    </Timeline>
  )
}

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 specialization.

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 persentation layers to our compound components by exposing any relevant internal state. See the example usage for the TimelineMonths component.

TimelineExample.js
import React from 'react'

import { 
  Timeline, 
  TimelineMonths, 
  TimelineRows 
} from '@royalnavy/react-component-library'

const CustomTimelineMonth = (
  index,
  dayWidth,
  daysTotal,
  startDate
) => {
  return (
    <span
      style={{
        display: 'inline-block',
        width: `${dayWidth * daysTotal}px`,
        // ...
      }}
    >
      {startDate}
    </span>
  )
}

const ExampleTimeline = () => {
  return (
    <Timeline>
      <TimelineMonths render={CustomTimelineMonth} />
      <TimelineRows>{}</TimelineRows>
    </Timeline>
  )
}

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
import React, { useContext } from 'react' 

import { 
  Timeline, 
  TimelineRows 
  TimelineContext, 
  TIMELINE_ACTIONS 
} from '@royalnavy/react-component-library'

const CustomTimelineComponent = () => {
  const { state: { months, weeks, days, options }, dispatch } = useContext(TimelineContext)

  return (
    <div>
      <button onClick={_ => dispatch({ type: TIMELINE_ACTIONS.GET_PREV })}>Previous</button>
      <button onClick={_ => dispatch({ type: TIMELINE_ACTIONS.GET_NEXT })}>Next</button>
    </div>
  )
}

const ExampleTimeline = () => {
  return (
    <Timeline>
      <CustomTimelineComponent />
      <TimelineRows>{}</TimelineRows>
    </Timeline>
  )
}

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
import React from 'react'

import { useTimelinePosition } from '@royalnavy/react-component-library'

const CustomTimelineComponent = ({
  startDate,
  endDate
}) => {
  const {
    width,
    offset,
    isBeforeStart,
    isAfterEnd
  } = useTimelinePosition(startDate, endDate)

  if (isBeforeStart || isAfterEnd) return null

  return (
    <div style={{
        position: 'absolute',
        display: 'inline-block',
        width,
        left: offset
        // ...
      }} 
    />
  )
}

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:

Hook APIs

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

useTimelinePosition

startDateRequired

Date
Default Value
-
Description

The start date of the event.

endDate

Date | Null
Default Value
Null
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
-
Description

A month will display either side of this start date.

endDate

Date
Default Value
-
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.

dayWidth

Number
Default Value
30
Description

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

TimelineTodayMarker

render

Func
Default Value
Function
  • todayDate
  • offsetstring
Description

Supply a custom presentation layer.

TimelineSide

render

Func
Default Value
Function
Description

Supply a custom presentation layer.

TimelineMonths

render

Func
Default Value
Function
  • indexnumber
  • 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.

TimelineRows

render

Func
Default Value
Function
  • indexnumber
  • isOddNumberboolean
  • offsetPxstring
  • widthPxstring
Description

Supply a custom presentation layer.

childrenRequired

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

Supply children to be rendered.

TimelineRow

nameRequired

string
Default Value
-
Description

A descriptive identifier for the row.

childrenRequired

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

Supply children to be rendered.

TimelineEvents

childrenRequired

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

Supply children to be rendered.

TimelineEvent

startDateRequired

Date
Default Value
-
Description

The start date of the event.

endDateRequired

Date
Default Value
-
Description

The end date of the event.

render

Func
Default Value
Function
  • startDateDate
  • endDateDate
  • widthPxstring
  • offsetPxstring
Description

Supply a custom presentation layer.

childrenRequired

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

Supply children to be rendered.

Roadmap

  • Migrate to independent package within monorepo
  • Adopt styled-components for default presentation
  • Expose TimelineSide render prop
  • Add demos to sandbox
  • Iterate upon default presentation (user research and design)
  • Investigate support for advanced features:
    • Infinite scroll
    • Lazy loading
    • View scaling (micro to macro / hours to years)
    • 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.

License

The Royal Navy Design System is licensed under the Apache License 2.0.