@abcnews/components-storylab 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,27 @@
2
2
 
3
3
  A live repo containing reusable components and snippts by Story Lab. This repo is subject to change.
4
4
 
5
+ ## Installation
6
+
7
+ 1. Install the package:
8
+ ```sh
9
+ npm i @abcnews/components-storylab
10
+ ```
11
+ 2. Add the package to your `aunty` config's `includedDependencies` array in `package.json` so it gets built properly:
12
+ ```js
13
+ "aunty": {
14
+ "type": "svelte",
15
+ "build": {
16
+ "includedDependencies": [
17
+ "@abcnews/components-storylab"
18
+ ]
19
+ }
20
+ }
21
+ ```
22
+
5
23
  ## Components
6
24
 
7
- ### initDarkModeIframe
25
+ ### 🌃 initDarkModeIframe
8
26
 
9
27
  Initializes dark mode inside an iframe, synchronizing with the parent site or system preferences.
10
28
 
@@ -16,8 +34,47 @@ import { initDarkModeIframe } from "@abcnews/components-storylab";
16
34
  initDarkModeIframe();
17
35
  ```
18
36
 
37
+ ### 🌏 MapLibre
38
+
39
+ Tools and components for working with MapLibre. The `utils` export gives you the
40
+ tools to do this yourself, whereas the MapLibreLoader component handles it for
41
+ you in Svelte. Import these from `@abcnews/components-storylab/mapLibre`.
42
+
43
+ #### MapLibreLoader
44
+
45
+ Call back when MapLibre is loaded. Passes the `maplibre` namespace and a root
46
+ node for you to use.
47
+
48
+ ```ts
49
+ import type { maplibregl } from "@abcnews/components-storylab/mapLibre";
50
+ import { MapLibreLoader, utils } from "@abcnews/components-storylab/mapLibre";
51
+
52
+ // Load MapLibre and use it in your component
53
+ const { loadMapLibre, STYLE_BRIGHT, STYLE_LIGHT } = utils;
54
+
55
+ // Example of using MapLibreLoader in a Svelte component
56
+ <MapLibreLoader
57
+ onLoad={({ rootNode, maplibregl }) => {
58
+ const map = new maplibregl.Map({
59
+ container: rootNode,
60
+ style: STYLE_BRIGHT,
61
+ });
62
+ return map;
63
+ }}
64
+ />
65
+ ```
66
+
67
+ If you return the Map from `onLoad`, you can componentise your viz and access it
68
+ from child components with:
69
+
70
+ ```ts
71
+ let { map } = getContext<Map>("mapInstance");
72
+ ```
73
+
74
+ This component will attempt to keep the map stable, so you can not change
75
+ props after the fact. If you want to destroy the map and create a new one
76
+ when props change, wrap it in a {#key}{/key} block/
77
+
19
78
  ## Developing
20
79
 
21
80
  See [DEVELOPMENT.md](DEVELOPMENT.md)
22
-
23
- Note: I haven't been able to `npm link` svelte components into an Aunty project. If you work this out please let me know.
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  // Reexport your entry components here
2
+ // iframes
2
3
  export { default as initDarkModeIframe } from "./utils/initDarkModeIframe/initDarkModeIframe.js";
@@ -0,0 +1,58 @@
1
+ <script module>
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import MapLibreLoader from "./MapLibreLoader.svelte";
4
+
5
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
6
+ const { Story } = defineMeta({
7
+ title: "MapLibre/MapLibreLoader",
8
+ component: MapLibreLoader,
9
+ tags: ["autodocs"],
10
+ });
11
+ </script>
12
+
13
+ <Story
14
+ name="Bright style"
15
+ args={{
16
+ rootElStyle: "width:100%;height:calc(100dvh - 2rem);",
17
+ onLoad: async ({ rootNode, maplibregl }) => {
18
+ const map = new maplibregl.Map({
19
+ zoom: 3,
20
+ minZoom: 2,
21
+ maxZoom: 22,
22
+ attributionControl: false,
23
+ dragRotate: false,
24
+ doubleClickZoom: false,
25
+ style:
26
+ "https://www.abc.net.au/res/sites/news-projects/map-vector-style-bright/style.json",
27
+ container: rootNode,
28
+ interactive: true,
29
+ cooperativeGestures: true,
30
+ center: [133.28, -28.15],
31
+ });
32
+ return map;
33
+ },
34
+ }}
35
+ ></Story>
36
+ <Story
37
+ name="Light style"
38
+ args={{
39
+ rootElStyle: "width:100%;height:calc(100dvh - 2rem);",
40
+ onLoad: async ({ rootNode, maplibregl }) => {
41
+ const map = new maplibregl.Map({
42
+ zoom: 3,
43
+ minZoom: 2,
44
+ maxZoom: 22,
45
+ attributionControl: false,
46
+ dragRotate: false,
47
+ doubleClickZoom: false,
48
+ style:
49
+ "https://www.abc.net.au/res/sites/news-projects/map-vector-style-light/style.json",
50
+ container: rootNode,
51
+ interactive: true,
52
+ cooperativeGestures: true,
53
+ center: [133.28, -28.15],
54
+ });
55
+ return map;
56
+ },
57
+ }}
58
+ ></Story>
@@ -0,0 +1,27 @@
1
+ export default MapLibreLoader;
2
+ type MapLibreLoader = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const MapLibreLoader: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ import MapLibreLoader from "./MapLibreLoader.svelte";
15
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
16
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
17
+ $$bindings?: Bindings;
18
+ } & Exports;
19
+ (internal: unknown, props: {
20
+ $$events?: Events;
21
+ $$slots?: Slots;
22
+ }): Exports & {
23
+ $set?: any;
24
+ $on?: any;
25
+ };
26
+ z_$$bindings?: Bindings;
27
+ }
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import { onMount, setContext, untrack } from "svelte";
3
+ import { loadMapLibre } from "../utils.js";
4
+ import type { maplibregl } from "../maplibre.d.js";
5
+ import type { Snippet } from "svelte";
6
+ type Props = {
7
+ rootElStyle?: string;
8
+ onLoad: ({}: {
9
+ rootNode: HTMLDivElement;
10
+ maplibregl: typeof maplibregl;
11
+ }) => maplibregl.Map | Promise<maplibregl.Map> | void | Promise<void>;
12
+ onTeardown?: () => void | Promise<void>;
13
+ children?: Snippet;
14
+ };
15
+ const {
16
+ rootElStyle = "width:100%;height:100%;",
17
+ onLoad,
18
+ children,
19
+ }: Props = $props();
20
+ let rootNode = $state<HTMLDivElement>();
21
+ let status = $state<"loading" | "loaded">("loading");
22
+ let mapInstance = $state<{ map: maplibregl.Map | void }>({ map: undefined });
23
+ setContext("mapInstance", mapInstance);
24
+
25
+ onMount(async () => {
26
+ if (!rootNode) {
27
+ return;
28
+ }
29
+ await loadMapLibre();
30
+ const newMapInstance = await onLoad({
31
+ rootNode,
32
+ maplibregl: window.maplibregl,
33
+ });
34
+ mapInstance.map = newMapInstance;
35
+ status = "loaded";
36
+ });
37
+ </script>
38
+
39
+ <div class="maplibre" bind:this={rootNode} style={rootElStyle}>
40
+ {@render children?.()}
41
+ </div>
42
+
43
+ <style>
44
+ .maplibre {
45
+ width: 100%;
46
+ height: 100%;
47
+ }
48
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { maplibregl } from "../maplibre.d.ts";
2
+ import type { Snippet } from "svelte";
3
+ type Props = {
4
+ rootElStyle?: string;
5
+ onLoad: ({}: {
6
+ rootNode: HTMLDivElement;
7
+ maplibregl: typeof maplibregl;
8
+ }) => maplibregl.Map | Promise<maplibregl.Map> | void | Promise<void>;
9
+ onTeardown?: () => void | Promise<void>;
10
+ children?: Snippet;
11
+ };
12
+ declare const MapLibreLoader: import("svelte").Component<Props, {}, "">;
13
+ type MapLibreLoader = ReturnType<typeof MapLibreLoader>;
14
+ export default MapLibreLoader;
@@ -0,0 +1,3 @@
1
+ export { default as MapLibreLoader } from "./MapLibreLoader/MapLibreLoader.svelte";
2
+ export * as utils from "./utils.ts";
3
+ export type { maplibregl } from "./maplibre.d.ts";
@@ -0,0 +1,3 @@
1
+ // MapLibre
2
+ export { default as MapLibreLoader } from "./MapLibreLoader/MapLibreLoader.svelte";
3
+ export * as utils from "./utils.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * We only use the npm version of MapLibre for its types, and keep the version
3
+ * synced to the shared version in news-projects.
4
+ */
5
+ import type * as maplibregl from "maplibre-gl/dist/maplibre-gl.d.ts";
6
+ export type { maplibregl };
@@ -0,0 +1,20 @@
1
+ import type { maplibregl } from "./maplibre.d.ts";
2
+ export declare const MAPLIBRE_JS_URL = "https://www.abc.net.au/res/sites/news-projects/maplibre/v5.x.x-latest/maplibre-gl.js";
3
+ export declare const MAPLIBRE_CSS_URL = "https://www.abc.net.au/res/sites/news-projects/maplibre/v5.x.x-latest/maplibre-gl.css";
4
+ /** Colourful style */
5
+ export declare const STYLE_BRIGHT = "https://www.abc.net.au/res/sites/news-projects/map-vector-style-bright/style.json";
6
+ /** Grey style */
7
+ export declare const STYLE_LIGHT = "https://www.abc.net.au/res/sites/news-projects/map-vector-style-light/style.json";
8
+ /**
9
+ * Load common MapLibre version from news-projects.
10
+ *
11
+ * To keep bundle sizes, deploys, and end user downloads svelte, we can load
12
+ * MapLibre from news-projects. This version is updated only when the major
13
+ * version changes, which should be safe given semver conventions.
14
+ *
15
+ * - Regular visitors should only need to download & parse MapLibre once, ever,
16
+ * no matter how many different deploys or projects they hit.
17
+ * - Build sizes stay small, preventing duplication & deploy size blowouts
18
+ * - Build times are quicker with MapLibre externalised.
19
+ */
20
+ export declare function loadMapLibre(): Promise<typeof maplibregl>;
@@ -0,0 +1,58 @@
1
+ export const MAPLIBRE_JS_URL = "https://www.abc.net.au/res/sites/news-projects/maplibre/v5.x.x-latest/maplibre-gl.js";
2
+ export const MAPLIBRE_CSS_URL = "https://www.abc.net.au/res/sites/news-projects/maplibre/v5.x.x-latest/maplibre-gl.css";
3
+ /** Colourful style */
4
+ export const STYLE_BRIGHT = "https://www.abc.net.au/res/sites/news-projects/map-vector-style-bright/style.json";
5
+ /** Grey style */
6
+ export const STYLE_LIGHT = "https://www.abc.net.au/res/sites/news-projects/map-vector-style-light/style.json";
7
+ const promises = {};
8
+ function importModule(url) {
9
+ const key = "module" + url;
10
+ const promise = promises[key];
11
+ if (promise) {
12
+ return promise;
13
+ }
14
+ const newPromise = new Promise((resolve, reject) => {
15
+ const s = document.createElement("script");
16
+ s.src = url;
17
+ s.type = "module";
18
+ s.addEventListener("load", () => resolve());
19
+ s.addEventListener("error", reject);
20
+ document.head.appendChild(s);
21
+ });
22
+ promises[key] = newPromise;
23
+ return newPromise;
24
+ }
25
+ function loadCss(url) {
26
+ const key = "css" + url;
27
+ const promise = promises[key];
28
+ if (promise) {
29
+ return promise;
30
+ }
31
+ const newPromise = new Promise((resolve, reject) => {
32
+ const s = document.createElement("link");
33
+ s.rel = "stylesheet";
34
+ s.type = "text/css";
35
+ s.href = url;
36
+ s.addEventListener("load", () => resolve());
37
+ s.addEventListener("error", reject);
38
+ document.head.appendChild(s);
39
+ });
40
+ promises[key] = newPromise;
41
+ return newPromise;
42
+ }
43
+ /**
44
+ * Load common MapLibre version from news-projects.
45
+ *
46
+ * To keep bundle sizes, deploys, and end user downloads svelte, we can load
47
+ * MapLibre from news-projects. This version is updated only when the major
48
+ * version changes, which should be safe given semver conventions.
49
+ *
50
+ * - Regular visitors should only need to download & parse MapLibre once, ever,
51
+ * no matter how many different deploys or projects they hit.
52
+ * - Build sizes stay small, preventing duplication & deploy size blowouts
53
+ * - Build times are quicker with MapLibre externalised.
54
+ */
55
+ export async function loadMapLibre() {
56
+ await Promise.all([importModule(MAPLIBRE_JS_URL), loadCss(MAPLIBRE_CSS_URL)]);
57
+ return window.maplibregl;
58
+ }
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "@abcnews/components-storylab",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "scripts": {
5
- "dev": "vite dev",
6
- "build": "vite build && npm run prepack",
5
+ "dev": "npm run storybook",
6
+ "build": "npm run prepack",
7
7
  "preview": "vite preview",
8
8
  "prepare": "svelte-kit sync || echo ''",
9
9
  "prepack": "svelte-kit sync && svelte-package && publint",
10
10
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
11
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12
+ "test:unit": "vitest",
13
+ "test": "npm run test:unit -- --run",
14
+ "storybook": "export AUNTY_HOST=${AUNTY_HOST:-localhost} && storybook dev -p 6006 --host $AUNTY_HOST --ci",
15
+ "build-storybook": "storybook build"
12
16
  },
13
17
  "files": [
14
18
  "dist",
@@ -25,30 +29,40 @@
25
29
  ".": {
26
30
  "types": "./dist/index.d.ts",
27
31
  "svelte": "./dist/index.js"
32
+ },
33
+ "./mapLibre": {
34
+ "types": "./dist/mapLibre/index.d.ts",
35
+ "default": "./dist/mapLibre/index.js"
28
36
  }
29
37
  },
30
38
  "peerDependencies": {
31
39
  "svelte": "^5.0.0"
32
40
  },
33
41
  "devDependencies": {
42
+ "@storybook/addon-svelte-csf": "^5.0.10",
43
+ "@storybook/sveltekit": "^10.1.11",
34
44
  "@sveltejs/adapter-auto": "^7.0.0",
35
45
  "@sveltejs/kit": "^2.49.1",
36
46
  "@sveltejs/package": "^2.5.7",
37
47
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
48
+ "@types/node": "^24",
49
+ "@vitest/browser-playwright": "^4.0.15",
50
+ "maplibre-gl": "^5.16.0",
51
+ "playwright": "^1.57.0",
38
52
  "publint": "^0.3.15",
53
+ "sass-embedded": "^1.97.2",
54
+ "storybook": "^10.1.11",
39
55
  "svelte": "^5.45.6",
40
56
  "svelte-check": "^4.3.4",
41
57
  "typescript": "^5.9.3",
42
- "vite": "^7.2.6"
58
+ "vite": "^7.2.6",
59
+ "vitest": "^4.0.15",
60
+ "vitest-browser-svelte": "^2.0.1"
43
61
  },
44
62
  "keywords": [
45
63
  "svelte"
46
64
  ],
47
65
  "license": "MIT",
48
66
  "homepage": "https://github.com/abcnews/components-storylab",
49
- "repository": {
50
- "type": "git",
51
- "url": "git+https://github.com/abcnews/components-storylab.git"
52
- },
53
- "private": false
67
+ "repository": "github:abcnews/components-storylab"
54
68
  }