Royal Navy Design Systemv2.44.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
// npm
npm install @royalnavy/css-framework @royalnavy/react-component-library

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

Options

endDate

Date
Default Value
-
Description

Bound the timeline by the specified start and end dates.

hasSide

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

range

Number
Default Value
3
Description

The number of months to display at any one time.

startDateRequired

Date
Default Value
-
Description

A month will display either side of this start date.

today

Date
Default Value
new Date()
Description

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

unitWidth

Number
Default Value
30
Description

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

TimelineExample.js
import React from 'react'

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

const ExampleTimeline = () => {
  return (
    <Timeline
      hasSide
      startDate={new Date(2020, 3, 1)}
      today={new Date(2020, 3, 3)}
    >
      <TimelineTodayMarker />
      <TimelineMonths />
      <TimelineWeeks />
      <TimelineDays />
      <TimelineHours />
      <TimelineRows>
        <TimelineRow name="Row 1">
          <TimelineEvents>
            <TimelineEvent
              startDate={new Date(2020, 2, 14)}
              endDate={new Date(2020, 3, 4)}
            >
              Event 1
            </TimelineEvent>
          </TimelineEvents>
        </TimelineRow>
        <TimelineRow name="Row 2">
          <TimelineEvents>
            <TimelineEvent
              startDate={new Date(2020, 3, 2)}
              endDate={new Date(2020, 3, 5)}
            >
              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,
    startsBeforeStart,
    startsAfterEnd
  } = useTimelinePosition(startDate, endDate)

  if (startsBeforeStart || startsAfterEnd) 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.

unitWidth

Number
Default Value
30
Description

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

hasSide

Boolean
Default Value
-
Description

Specify whether or not to output sidebar headings.

className

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

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

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

childrenRequired

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

Supply children to be rendered.

css

CSSProp
Default Value
-
Description

Pass a styled-components css`` value to modify the CSS.

nameRequired

string
Default Value
-
Description

A descriptive identifier for the row.

contentProps

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

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

headerProps

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

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

TimelineEvents

childrenRequired

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

Supply children to be rendered.

TimelineEvent

barColor

string
Default Value
ColorSuccess500
Description

The colour of the bar.

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:
    • Hours display
    • 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.