Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/** @type { import('@storybook/svelte-vite').StorybookConfig } */
const config = {
stories: ["../src/**/*.stories.@(js|svelte)", "../src/**/*.stories.mdx"],
addons: ["@storybook/addon-essentials"],
framework: {
name: "@storybook/svelte-vite",
options: {}
},
staticDirs: ["../dist"],
};

export default config;
11 changes: 11 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import "../dist/global.css";

/** @type { import('@storybook/svelte').Preview } */
const preview = {
parameters: {
controls: { expanded: true },
actions: { argTypesRegex: "^on[A-Z].*" }
}
};

export default preview;
23,541 changes: 21,212 additions & 2,329 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"dev": "rollup -c rollup.config.dev.js -w",
"start": "sirv dist",
"test": "echo \"No test specified\"",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"deploy": "gh-pages -d dist"
},
"repository": {
Expand All @@ -31,18 +33,22 @@
"url": "https://github.com/onsdigital/svelte-maps/issues"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.6.17",
"@storybook/svelte-vite": "^7.6.17",
"@rollup/plugin-commonjs": "^11.0.0",
"@rollup/plugin-dsv": "^2.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"d3-dsv": "^3.0.1",
"gh-pages": "^6.2.0",
"maplibre-gl": "^3.3.1",
"rollup": "^2.38.5",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.1.0",
"rollup-plugin-terser": "^7.0.2",
"sirv-cli": "^1.0.0",
"storybook": "^7.6.17",
"topojson-client": "^3.1.0"
},
"peerDependencies": {
Expand Down
29 changes: 29 additions & 0 deletions src/stories/Map.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import MapExample from "./examples/MapExample.svelte";

export default {
title: "Components/Map",
component: MapExample,
parameters: {
docs: {
description: {
component:
"Map renders a MapLibre GL JS map. This story mirrors the base-map example in App.svelte using the sample style JSON from /data/style-ons-light.json.\n\nNesting: Map is the top-level container. Place MapSource and MapLayer components as children inside Map. Child layers render after the map has loaded.\n\nKey props: id, style (URL or JSON), location (lng/lat/zoom or bounds), controls, minzoom, maxzoom, interactive, attribution, tabbable, mapDescription, css, options.\nBindings: map, zoom, center, pitch, bearing.\nEvents: load, style."
}
}
}
};

export const BaseMap = {
name: "Base map",
render: () => ({
Component: MapExample
}),
parameters: {
docs: {
description: {
story:
"Uses style JSON in /data/style-ons-light.json and demonstrates map bindings for zoom and center."
}
}
}
};
29 changes: 29 additions & 0 deletions src/stories/MapLayer.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import MapLayerExample from "./examples/MapLayerExample.svelte";

export default {
title: "Components/MapLayer",
component: MapLayerExample,
parameters: {
docs: {
description: {
component:
"MapLayer renders a MapLibre layer from a parent MapSource. This story mirrors the hover/select example in App.svelte with both a fill and line layer.\n\nNesting: MapLayer must be a child of MapSource. It can also contain MapTooltip as a child when hover is enabled. Multiple MapLayer instances can share the same MapSource.\n\nKey props: id, type, paint, layout, data, order, visible, minzoom, maxzoom, sourceLayer, filter.\nInteractive props: hover, select, hover/selected bindings, clickIgnore, clickCenter, hovered, selected.\nEvents: hover, select."
}
}
}
};

export const HoverAndSelect = {
name: "Hover and select",
render: () => ({
Component: MapLayerExample
}),
parameters: {
docs: {
description: {
story:
"Uses feature-state colors derived from the CSV in /data/salary-pcon10.csv and shows hover/selected IDs."
}
}
}
};
29 changes: 29 additions & 0 deletions src/stories/MapSource.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import MapSourceExample from "./examples/MapSourceExample.svelte";

export default {
title: "Components/MapSource",
component: MapSourceExample,
parameters: {
docs: {
description: {
component:
"MapSource registers a data source on the map and provides it to child layers. This story uses the same geojson source and salary CSV from App.svelte (pcon10-bounds.json + salary-pcon10.csv).\n\nNesting: MapSource must be a direct child of Map. MapLayer components must be nested inside MapSource so they can reference its source.\n\nKey props: id, type (geojson|vector), data (geojson) or url (vector tiles), layer (vector tiles), promoteId, minzoom, maxzoom, tilesize.\nSlots: child layers render after the source is ready."
}
}
}
};

export const GeoJsonSource = {
name: "GeoJSON source",
render: () => ({
Component: MapSourceExample
}),
parameters: {
docs: {
description: {
story:
"Loads geojson boundaries from /data/pcon10-bounds.json and joins CSV values to set feature-state colors."
}
}
}
};
29 changes: 29 additions & 0 deletions src/stories/MapTooltip.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import MapTooltipExample from "./examples/MapTooltipExample.svelte";

export default {
title: "Components/MapTooltip",
component: MapTooltipExample,
parameters: {
docs: {
description: {
component:
"MapTooltip renders a MapLibre popup anchored to the hovered feature. It must be used inside a MapLayer with hover enabled and a bound hovered ID.\n\nNesting: MapTooltip must be a child of MapLayer. MapLayer must be nested inside MapSource, which sits inside Map.\n\nKey prop: content (string or HTML)."
}
}
}
};

export const HoverTooltip = {
name: "Hover tooltip",
render: () => ({
Component: MapTooltipExample
}),
parameters: {
docs: {
description: {
story:
"Hover a constituency to see the tooltip content rendered from the hovered feature ID."
}
}
}
};
77 changes: 77 additions & 0 deletions src/stories/examples/MapExample.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script>
import Map from "../../Map.svelte";

const baseMapStyle = "/data/style-ons-light.json";

let map;
let zoom;
let center = {};

const mapExample = `&lt;Map\n id=&quot;map-story-base&quot;\n style=&quot;/data/style-ons-light.json&quot;\n bind:map={map}\n bind:zoom={zoom}\n bind:center={center}\n/&gt;`;
</script>

<div class="story">
<div class="story-docs">
<h3>Map</h3>
<p>
Top-level map container that initializes MapLibre. Place MapSource and MapLayer
components inside Map so they render after the map loads.
</p>
<ul>
<li>Key props: id, style, location, controls, minzoom, maxzoom, interactive, attribution</li>
<li>Accessibility: tabbable, mapDescription, css</li>
<li>Bindings: map, zoom, center, pitch, bearing</li>
<li>Events: load, style</li>
</ul>
<pre><code>{@html mapExample}</code></pre>
</div>
<div class="map">
<Map
id="map-story-base"
style={baseMapStyle}
bind:map={map}
bind:zoom={zoom}
bind:center={center}
/>
</div>
<div class="meta">
<span>zoom: {zoom ? zoom.toFixed(1) : ""}</span>
<span>lon: {center?.lng ? center.lng.toFixed(2) : ""}</span>
<span>lat: {center?.lat ? center.lat.toFixed(2) : ""}</span>
</div>
</div>

<style>
.story {
display: grid;
gap: 12px;
}
.story-docs {
font-size: 13px;
color: #222;
}
.story-docs h3 {
margin: 0 0 6px 0;
font-size: 14px;
}
.story-docs ul {
margin: 6px 0 0 16px;
}
pre {
margin: 8px 0 0;
padding: 10px;
background: #f6f8fa;
border-radius: 6px;
overflow: auto;
font-size: 12px;
}
.map {
height: 320px;
}
.meta {
display: flex;
gap: 12px;
font-size: 12px;
color: #444;
}
</style>
Loading