Skip to content

Commit ada188e

Browse files
authored
Merge pull request #59 from conveyal/add-storybook
Add storybook for improved testing/debugging
2 parents 6c17b8d + 867f653 commit ada188e

16 files changed

+11231
-1983
lines changed

.babelrc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env", {
5+
"useBuiltIns": "entry",
6+
"corejs": 2
7+
}
8+
],
9+
"@babel/preset-react"
10+
]
11+
}

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ lib-cov
44
*.csv
55
*.dat
66
*.out
7+
out*
78
*.pid
89
*.gz
910

.storybook/main.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const path = require('path')
2+
// your app's webpack.config.js
3+
const custom = require('./webpack.config.js');
4+
5+
module.exports = {
6+
stories: ['../stories/*.stories.js'],
7+
addons: [
8+
'@storybook/addon-links',
9+
'@storybook/addon-essentials',
10+
'@storybook/preset-create-react-app'
11+
],
12+
// Ensures that, when running storybook, webpack rules (loaders for css, file,
13+
// etc.) from the custom config are used.
14+
webpackFinal: (config) => {
15+
return { ...config, module: { ...config.module, rules: custom.module.rules } };
16+
}
17+
}

.storybook/webpack.config.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const path = require('path');
2+
const webpack = require('webpack');
3+
4+
/**
5+
* This is a (mostly) auto-generated webpack config that storybook created when
6+
* initializing the storybook.
7+
*/
8+
9+
const TerserPlugin = require('terser-webpack-plugin');
10+
11+
module.exports = {
12+
mode: 'development',
13+
entry: '../lib/transitive.js',
14+
15+
output: {
16+
path: path.resolve(__dirname, 'build'),
17+
},
18+
19+
plugins: [new webpack.ProgressPlugin()],
20+
21+
module: {
22+
rules: [
23+
{
24+
test: /\.(js|jsx)$/,
25+
include: [path.resolve(__dirname, '../lib'), path.resolve(__dirname, '../stories')],
26+
loader: 'babel-loader',
27+
},
28+
{
29+
test: /\.(png|jpe?g|gif)$/i,
30+
use: [
31+
{
32+
loader: 'file-loader',
33+
},
34+
],
35+
},
36+
{
37+
test: /.css$/,
38+
39+
use: [
40+
{
41+
loader: 'style-loader',
42+
},
43+
{
44+
loader: 'css-loader',
45+
46+
options: {
47+
sourceMap: true,
48+
},
49+
},
50+
],
51+
},
52+
],
53+
},
54+
55+
optimization: {
56+
minimizer: [new TerserPlugin()],
57+
58+
splitChunks: {
59+
cacheGroups: {
60+
vendors: {
61+
priority: -10,
62+
test: /[\\/]node_modules[\\/]/,
63+
},
64+
},
65+
66+
chunks: 'async',
67+
minChunks: 1,
68+
minSize: 30000,
69+
name: false,
70+
},
71+
},
72+
};

.travis.yml

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ notifications:
66
email: false
77
node_js:
88
- '10'
9+
# Install AWS CLI for uploading storybook to S3.
10+
before_install:
11+
- pyenv global 3.7.1
12+
- pip install -U pip
13+
- pip install awscli
14+
# add aws 'default' profile for uploading storybook
15+
- mkdir ~/.aws && printf '%s\n' '[default]' "aws_access_key_id=${AWS_ACCESS_KEY_ID}" "aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}" 'region=us-east-1' > ~/.aws/config
916
install:
1017
- yarn
1118
script:
@@ -14,6 +21,9 @@ script:
1421
after_success:
1522
- bash <(curl -s https://codecov.io/bash)
1623
- semantic-release
24+
# Deploy storybook demo to S3 if handling a non-PR build on the main branch
25+
# https://s3.amazonaws.com/transitive.js/index.html
26+
- if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then yarn deploy-storybook; else echo 'skipping deploy to s3'; fi
1727
branches:
1828
except:
1929
- /^v\d+\.\d+\.\d+$/

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,40 @@ A Transitive map can be embedded as a freestanding web element or overlaid onto
1212

1313
Transitive is supported by the [Mobility Lab](http://mobilitylab.org/) [Transit Tech Initiative](http://mobilitylab.org/tech/transit-tech-initiative/). Read more in [this Mobility Lab article](http://mobilitylab.org/2014/04/16/the-technology-behind-a-new-kind-of-travel-planning/).
1414

15+
## Storybook
16+
17+
To view samples of Transitive in action, [check out the live Storybook](https://s3.amazonaws.com/transitive.js/index.html).
18+
19+
You can also run this locally with:
20+
21+
```bash
22+
git clone https://github.com/conveyal/transitive.js
23+
cd transitive.js
24+
yarn start
25+
# Go to http://localhost:5555 to view the storybook (the web page should open automatically)
26+
```
27+
28+
### Usage of otp-ui
29+
30+
These stories rely on the [otp-ui](https://github.com/opentripplanner/otp-ui)
31+
project to render transitive data on a Leaflet map. Specifically, it uses:
32+
- @opentripplanner/base-map - renders a Leaflet base map
33+
- @opentripplanner/core-utils - converts an OpenTripPlanner itinerary object into
34+
data that Transitive can read.
35+
- @opentripplanner/transitive-overlay - copied from the otp-ui project and
36+
replaces the Transitive import with the local copy (useful for testing local
37+
changes)
38+
39+
### BYOD - Bring your own data
40+
41+
To test out how your own data (e.g., an OpenTripPlanner itinerary) would appear in
42+
Transitive, try replacing the `itinerary` prop in the [Itinerary](https://s3.amazonaws.com/transitive.js/index.html?path=/story/example-transitive--itinerary) (for OpenTripPlanner itineraries) story (or `transitiveData`
43+
in the [Profile](https://s3.amazonaws.com/transitive.js/index.html?path=/story/example-transitive--profile)
44+
story).
45+
46+
You can also override the default style by adding a `style` prop. See a style
47+
example here: https://github.com/conveyal/transitive-demo/blob/master/styles.js
48+
1549
## Demo
1650

1751
* [Demo of a freestanding Transitive map](http://conveyal.github.io/transitive.js)

lib/styler/styles.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const stopsMerged = {
103103

104104
'marker-padding': 3,
105105

106-
visibility: function (display, data) {
106+
visibility: (display, data) => {
107107
if (!data.owner.containsSegmentEndPoint()) return 'hidden'
108108
}
109109
}
@@ -112,18 +112,18 @@ const stopsMerged = {
112112
* Stops Along a Pattern
113113
*/
114114

115-
const stopsPattern = exports.stops_pattern = {
115+
const stopsPattern = {
116116
cx: 0,
117117
cy: 0,
118118
r: [
119119
4,
120-
function (display, data, index, utils) {
120+
(display, data, index, utils) => {
121121
return utils.pixels(display.scale, 1, 2, 4)
122122
},
123-
function (display, data, index, utils) {
123+
(display, data, index, utils) => {
124124
var point = data.owner
125125
var busOnly = true
126-
point.getPatterns().forEach(function (pattern) {
126+
point.getPatterns().forEach(pattern => {
127127
if (pattern.route && pattern.route.route_type !== 3) busOnly = false
128128
})
129129
if (busOnly && !point.containsSegmentEndPoint()) {
@@ -132,7 +132,7 @@ const stopsPattern = exports.stops_pattern = {
132132
}
133133
],
134134
stroke: 'none',
135-
visibility: function (display, data) {
135+
visibility: (display, data) => {
136136
if (display.scale < 1.5) return 'hidden'
137137
if (data.owner.containsSegmentEndPoint()) return 'hidden'
138138
}

package.json

+26-5
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,40 @@
2626
"sphericalmercator": "^1.0.5",
2727
"svg.js": "^2.6.5"
2828
},
29-
"peerDependencies": {
30-
"svg.js": "^2.6.4"
31-
},
3229
"scripts": {
3330
"prepublish": "mastarm prepublish lib:build",
3431
"cover": "npm test -- --coverage",
3532
"lint": "mastarm lint lib",
3633
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
37-
"test": "mastarm test"
34+
"start": "start-storybook -p 5555",
35+
"deploy-storybook": "storybook-to-aws-s3 --bucket-path=transitive.js --s3-sync-options=--acl=public-read"
3836
},
3937
"devDependencies": {
38+
"@babel/core": "^7.12.3",
39+
"@babel/preset-env": "^7.12.1",
40+
"@opentripplanner/base-map": "^1.0.4",
41+
"@opentripplanner/core-utils": "^3.0.2",
42+
"@storybook/addon-essentials": "^6.1.0-beta.6",
43+
"@storybook/addon-info": "5.3.21",
44+
"@storybook/addon-links": "^6.1.0-beta.6",
45+
"@storybook/preset-create-react-app": "^3.1.5",
46+
"@storybook/react": "^6.1.0-beta.6",
47+
"@storybook/storybook-deployer": "^2.8.7",
48+
"@webpack-cli/init": "^1.0.3",
49+
"babel-loader": "^8.1.0",
50+
"core-js": "2",
51+
"css-loader": "^5.0.1",
52+
"file-loader": "^6.2.0",
53+
"leaflet": "^1.7.1",
54+
"lodash.isequal": "^4.5.0",
4055
"mastarm": "^5.0.1",
41-
"semantic-release": "^15.13.16"
56+
"react-leaflet": "^2.6.1",
57+
"semantic-release": "^15.13.16",
58+
"style-loader": "^2.0.0",
59+
"styled-components": "^5.2.1",
60+
"terser-webpack-plugin": "^5.0.3",
61+
"webpack": "^5.4.0",
62+
"webpack-cli": "^4.2.0"
4263
},
4364
"standard": {
4465
"parser": "babel-eslint"

stories/Transitive.stories.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {TransitiveMap} from './TransitiveMap'
2+
3+
const companies = [
4+
{
5+
id: 'RAZOR',
6+
label: 'Razor',
7+
modes: 'MICROMOBILITY_RENT'
8+
},
9+
{
10+
id: 'SHARED',
11+
label: 'Shared',
12+
modes: 'MICROMOBILITY_RENT'
13+
}
14+
]
15+
16+
export default {
17+
title: 'Example/Transitive'
18+
}
19+
20+
const Template = (args) => TransitiveMap(args)
21+
22+
export const Itinerary = Template.bind({})
23+
Itinerary.args = {
24+
companies,
25+
itinerary: require('./data/walk-interlined-transit-walk-itinerary.json'),
26+
styles: undefined
27+
}
28+
29+
export const Profile = Template.bind({})
30+
Profile.args = {
31+
center: [38.885, -77.0369],
32+
styles: undefined,
33+
transitiveData: require('./data/profile.json'),
34+
zoom: 14
35+
}

stories/TransitiveMap.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import BaseMap from '@opentripplanner/base-map'
2+
import {itineraryToTransitive} from '@opentripplanner/core-utils/lib/map'
3+
import React from 'react'
4+
import styled from 'styled-components'
5+
6+
import TransitiveOverlay from './transitive-overlay'
7+
8+
import '../node_modules/leaflet/dist/leaflet.css'
9+
10+
require('./leaflet-canvas-layer')
11+
12+
const MapContainer = styled.div`
13+
height: 800px;
14+
width: 100%;
15+
16+
.map {
17+
height: 800px;
18+
width: 100%;
19+
}
20+
21+
* {
22+
box-sizing: unset;
23+
}
24+
`
25+
26+
/**
27+
* Primary UI component for user interaction
28+
*/
29+
export const TransitiveMap = ({
30+
center = [45.506, -122.68302],
31+
companies = [],
32+
itinerary,
33+
styles,
34+
// If no transitiveData is provided, default to generating from itinerary.
35+
transitiveData = itineraryToTransitive(itinerary, companies),
36+
zoom = 15
37+
}) => {
38+
return (
39+
<MapContainer>
40+
<BaseMap
41+
// TODO: Determine center/zoom based on input data?
42+
center={center}
43+
zoom={zoom}
44+
>
45+
<TransitiveOverlay
46+
transitiveData={transitiveData}
47+
visible
48+
/>
49+
</BaseMap>
50+
</MapContainer>
51+
)
52+
}

stories/data/profile.json

+1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)