The code behind https://parkingreform.org/parking-lot-map/ and https://parkingreform.org/ct-parking-lots/.
The code is fairly simple. We do not use frameworks like React or Svelte to keep things simple. However, we do use these techniques:
- TypeScript
- Sass and the folder
packages/shared/src/css/theme
, which should stay aligned with https://github.com/ParkingReformNetwork/reform-map - Reactive state management - see State diagram
The repository is organized as a "monorepo":
packages/shared
: the core functionality for the mappackages/primary
: the map at https://parkingreform.org/parking-lot-map/packages/ct
: the map at https://parkingreform.org/ct-parking-lots/ (CT == Connecticut)packages/scripts
: scripts to help with adding and updatings data
All the commands take place in a terminal, a text-based interface for interacting with your computer. On macOS, you can open the "Terminal" app. On Windows, you can use "Git Bash", which will be installed when you install Git below.
You will also need to install:
- Git, which we use for "version control": https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
- NPM, which is how we run JavaScript and our tests. Use the LTS at https://nodejs.dev/en/download/
To run the below commands, open your terminal app. Make sure you have git clone
d the fork of your repository somewhere on your machine. Then, use cd <directory>
to navigate there, e.g. cd code/parking-lot-map
.
You must first install the project's dependencies before running any of the below commands.
❯ corepack enable pnpm
❯ pnpm install
❯ npx playwright install
# Open primary app:
❯ pnpm -F primary start
# Or, open CT app:
❯ pnpm -F ct start
Then open http://127.0.0.1:1234 in a browser. Hit CTRL-C
to stop the server.
When the server is running, you can make any changes you want to the project. Reload the page in the browser to see those changes. (You may need to force reload, e.g. hold the shift key while reloading on macOS.)
❯ pnpm test
You can tests for a specific package with -F
, e.g. pnpm -F shared test
.
❯ pnpm fmt
"Linting" means using tools that check for common issues that may be bugs or low code quality.
❯ pnpm lint
Manually edit the values in the files packages/primary/data/city-stats.json
and packages/ct/data/city-stats.json
.
Start the server to make sure it's what you want.
Then, save your changes in Git (in a new branch) and open a pull request. See the section "Make a contribution" below.
Export the GeoJSON file and save it as the file parking-lots-update.geojson
in the root of this repository. If the file already exists, overwrite it with your new data.
Then, determine the city/state name. This is the same as what we show in the city toggle on the site, e.g. St. Louis, MO
.
Now, run the below but replace the last part with the city/state name (in single quotes!):
❯ pnpm -F scripts update-lots -- 'My City, AZ'
Run the site with npm start
and make sure it's what you want. Also, autoformat the file with pnpm fmt
.
Then, save your changes in Git (in a new branch) and open a pull request. See the section "Make a contribution" below.
Export the GeoJSON file and save it as the file city-update.geojson
in the root of this repository. If the file already exists, overwrite it with your new data.
Then, determine the city/state name. This is the same as what we show in the city toggle on the site, e.g. St. Louis, MO
.
Now, run the below but replace the last part with the city/state name (in single quotes!):
❯ pnpm -F scripts update-city-boundaries -- 'My City, AZ'
Start the server to make sure it's what you want. Also, autoformat the file with pnpm fmt
.
Finally, save your changes in Git (in a new branch) and open a pull request. See the section "Make a contribution" below.
Export the city boundaries' GeoJSON and save it as the file city-update.geojson
in the root of this repository. If the file already exists, overwrite it with your new data.
Also export the city's parking lots GeoJSON and save it as the file parking-lots-update.geojson
.
Then, determine the city/state name, such as St. Louis, MO
.
Now, run the below but replace the last part with the city/state name (in single quotes!):
❯ pnpm -F scripts add-city -- 'My City, AZ'
Next, manually fill in the score card entries in the city-stats.json
for the app you're adding the city to. Search for the city name and update the values.
Start the server to make sure it's what you want. Also, autoformat the file with pnpm fmt
.
Finally, save your changes in Git (in a new branch) and open a pull request. See the section "Make a contribution" below.
We use the typical forking model to make contributions by opening Pull Requests. See https://docs.github.com/en/get-started/quickstart/contributing-to-projects.
You can preview what a build will look like by running pnpm -F primary build
or pnpm -F ct build
. Then use either pnpm -F primary serve-dist
or pnpm -F ct serve-dist
to start the server.
You can also run our integration tests on built dist folder with pnpm -F primary test-dist
and pnpm -F ct test-dist
(make sure the server is not already running).
We use continuous deployment, meaning that we re-deploy the site every time we merge a pull request to staging at https://parkingreform.org/plm-staging/ and https://parkingreform.org/ct-parking-lots-staging. You can check how the site renders about ~1-2 minutes after your change merges.
After you've confirmed staging looks good at https://parkingreform.org/plm-staging/ and https://parkingreform.org/ct-parking-lots-staging, you can manually trigger a deploy.
Go to https://github.com/ParkingReformNetwork/parking-lot-map/actions and open the "Deploy to prod" workflow. Initiate a build.
This shows all possible user interactions on the map, and what triggers what.
graph TD
A[current city]
F[user interaction w/ select element]
C[share link URL]
S[full-screen URL]
B[map data loaded]
D[scorecard entry]
E[map position]
H[user scrolling]
T[user click on city]
U[AND]
G[zoom]
R[zoom buttons]
J[scorecard accordion button]
K[scorecard accordion contents]
L[layer toggle]
M[map layer]
N[about icon]
O[about popup]
P[click outside popup]
Q[about popup close icon]
N -->|toggles| O
P -->|closes| O
Q -->|closes| O
J -->|toggles| K
L -->|controls| M
F -->|controls| A
E -->|changes| B
E -->|can change| A
T --> U
G --> U
U --> |changes| A
A -->|controls| C
A -->|controls| S
A -->|controls| B
A -->|controls| D
A -->|can change| E
A -->|can reset| G
H -->|controls| E
R -->|controls| G
We use reactive programming to manage the application's state. This paradigm separates out state management from how the UI should render. Whenever the state is changed, the relevant UI elements will automatically update. For example:
// Keep track of the value of a counter, starting at 0.
const counterObservable = new Observable<number>(0);
// Get existing DOM elements
const counterDisplay = document.getElementById("counter-display")!;
const incrementButton = document.getElementById("increment-button")!;
// Whenever the count changes values, this will re-render the UI with the
// updated value.
counterObservable.subscribe((count) => {
counterDisplay.textContent = `Count: ${count}`;
});
// This binds the button to the counter so that when you click the button, the
// counter increases its value by 1. The call to .setValue() will then cause
// everything that has called `counterObservable.subscribe` to re-render.
incrementButton.addEventListener("click", () => {
counterObservable.setValue(counterObservable.getValue() + 1);
});
// Initialize the UI
counterObservable.initialize();
Look at Observable.ts
and its usages for how we use the reactive paradigm.