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

Installation

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

Command Line
1// npm
2npm install @royalnavy/react-component-library
3
4// pnpm
5pnpm add @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 = ({ index, dayWidth, daysTotal, startDate }) => {
10 return (
11 <span
12 style={{
13 display: 'inline-block',
14 width: `${dayWidth * daysTotal}px`,
15 // ...
16 }}
17 >
18 {startDate.getMonth() + 1}/{startDate.getFullYear()}
19 </span>
20 )
21}
22
23const ExampleTimeline = () => {
24 return (
25 <Timeline startDate={new Date(2022, 0, 1)}>
26 <TimelineMonths render={CustomTimelineMonth} />
27 <TimelineRows>{}</TimelineRows>
28 </Timeline>
29 )
30}

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 a reducer action when a button is clicked.

ContextExample.js
1import React, { useContext } from 'react'
2
3import {
4 Timeline,
5 TimelineDays,
6 TimelineHours,
7 TimelineRows,
8 TimelineContext,
9 TIMELINE_ACTIONS,
10} from '@royalnavy/react-component-library'
11
12const CustomTimelineComponent = () => {
13 const {
14 state: { months, weeks, days, options, scaleOptions },
15 dispatch,
16 } = useContext(TimelineContext)
17
18 return (
19 <div>
20 <button
21 onClick={(_) =>
22 dispatch({ type: TIMELINE_ACTIONS.SCALE, getScaleIndex: () => 0 })
23 }
24 >
25 Hourly
26 </button>
27 </div>
28 )
29}
30
31const ExampleTimeline = () => {
32 return (
33 <Timeline startDate={new Date(2022, 0, 1)}>
34 <CustomTimelineComponent />
35 <TimelineDays />
36 <TimelineHours />
37 <TimelineRows>{}</TimelineRows>
38 </Timeline>
39 )
40}

Hooks

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

useTimelineFrame

This hook does not take any parameters and in return exposes moveNext and movePrevious which can be used to navigate between frames at the current zoom level.

useTimelinePosition

This hook takes startDate and 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.

useTimelineZoom

This hook does not take any parameters and in return exposes fields for zooming in and out. canZoomIn and canZoomOut are booleans that return whether the Timeline can zoom in or out further. zoomIn and zoomOut are functions which decrease or increase the amount of time visible in a frame of the Timeline.

HooksExample.js
1import React from 'react'
2
3import {
4 useTimelineFrame,
5 useTimelinePosition,
6 useTimelineZoom,
7} from '@royalnavy/react-component-library'
8
9const CustomTimelineFrameComponent = () => {
10 const { moveNext, movePrevious } = useTimelineFrame()
11
12 return (
13 <>
14 <button onClick={movePrevious}>Previous</button>
15 <button onClick={moveNext}>Next</button>
16 </>
17 )
18}
19
20const CustomTimelinePositionComponent = ({ startDate, endDate }) => {
21 const { width, offset, endsBeforeStart, startsAfterEnd } =
22 useTimelinePosition(startDate, endDate)
23
24 if (endsBeforeStart || startsAfterEnd) return null
25
26 return (
27 <div
28 style={{
29 position: 'absolute',
30 display: 'inline-block',
31 width,
32 left: offset,
33 // ...
34 }}
35 />
36 )
37}
38
39const CustomTimelineZoomComponent = () => {
40 const { canZoomIn, canZoomOut, zoomIn, zoomOut } = useTimelineZoom()
41
42 if (!canZoomIn && !canZoomOut) return null
43
44 return (
45 <>
46 <button onClick={zoomIn}>Zoom in</button>
47 <button onClick={zoomOut}>Zoom out</button>
48 </>
49 )
50}
51

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.

useTimelineFrame

Provides the ability to navigate Timeline frames.

Return Object

moveNext

() => void
Default Value
undefined
Description

Move to the next frame of the Timeline.

movePrevious

() => void
Default Value
undefined
Description

Move to the previous frame of the Timeline.

useTimelinePosition

Provides fields to facilitate positioning items in the Timeline.

startDateRequired

Date
Default Value
undefined
Description

The start date of the event.

endDate

Date
Default Value
undefined
Description

The end date of the event.

Return Object

endsAfterEnd

boolean
Default Value
undefined
Description

Whether the element end date is after the end date of the current frame.

endsBeforeStart

boolean
Default Value
undefined
Description

Whether the element end date is before the start date of the current frame.

maxWidth

string
Default Value
undefined
Description

Width of the element formatted as a pixel string taking into consideration the dates being displayed.

offset

string
Default Value
undefined
Description

Distance of the element from the left of the Timeline as a pixel string.

startsAfterEnd

boolean
Default Value
undefined
Description

Whether the element start date is after the end date of the current frame.

startsBeforeStart

boolean
Default Value
undefined
Description

Whether the element start date is before the start date of the current frame.

width

string
Default Value
undefined
Description

Width of the element formatted as a pixel string.

useTimelineZoom

Provides the ability to zoom in and out of the Timeline.

Return Object

canZoomIn

boolean
Default Value
undefined
Description

Whether it is possible to zoom in further.

canZoomOut

boolean
Default Value
undefined
Description

Whether it is possible to zoom out further.

zoomIn

() => {}
Default Value
undefined
Description

View a lesser amount of time in the current frame.

zoomOut

() => void
Default Value
undefined
Description

View a larger amount of time in the current frame.

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
1
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
(props: { today, offset }) => ReactNode
    todaydate
    offsetstring
Description

Supply a custom presentation layer.

TimelineMonths

render

Func
Default Value
(props: { index, dayWidth, daysTotal, startDate }) => ReactElement
    index number
    dayWidthnumber
    daysTotalnumber
    startDatedate
Description

Supply a custom presentation layer.

TimelineWeeks

render

Func
Default Value
(props: { index, isOddNumber, offsetPx, widthPx, startDate }) => ReactElement
    indexnumber
    isOddNumberboolean
    offsetPxstring
    widthPxstring
    startDatedate
Description

Supply a custom presentation layer.

TimelineDays

render

Func
Default Value
(props: { index, dayWidth, date }) => ReactElement
    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
(props: { width, time }) => ReactElement
    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
(props: { index, isOddNumber, offsetPx, widthPx }) => ReactElement
    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.

render

Func
Default Value
(props: { name }) => ReactElement
    namestring?
Description

Supply a custom presentation layer for the row header.

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
(props: { startDate, endDate, widthPx, offsetPx, maxWidthPx, startsBeforeStart, endsAfterEnd }) => ReactNode
    startDatedate
    endDatedate
    widthPxstring
    offsetPxstring
    maxWidthPxstring
    startsBeforeStartboolean
    endsAfterEndboolean
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.