From ba86bb5d06ee1aba75ae847b5d99ec8d839e3834 Mon Sep 17 00:00:00 2001 From: Edoardo Dusi Date: Tue, 14 Jan 2025 17:56:43 +0100 Subject: [PATCH] feat: refactoring the main library exports --- cypress.config.ts | 6 +- cypress/component/page.cy.ts | 2 +- package.json | 7 +- playground/sveltekit/src/lib/Page.svelte | 24 +- .../sveltekit/src/routes/+layout.svelte | 5 +- playground/sveltekit/src/routes/+layout.ts | 4 +- .../src/routes/[...slug]/+page.svelte | 2 +- pnpm-lock.yaml | 296 +++++++++++++++++- src/demo.spec.ts | 7 - src/lib/StoryblokComponent.svelte | 19 +- src/lib/index.ts | 76 +---- src/lib/storyblok.ts | 63 ++++ src/lib/storyblokStore.ts | 72 +++++ src/tests/storyblok.init.spec.ts | 21 ++ src/tests/storyblok.spec.ts | 60 ++++ src/tests/storyblokStore.spec.ts | 102 ++++++ vite.config.ts | 3 +- 17 files changed, 646 insertions(+), 123 deletions(-) delete mode 100644 src/demo.spec.ts create mode 100644 src/lib/storyblok.ts create mode 100644 src/lib/storyblokStore.ts create mode 100644 src/tests/storyblok.init.spec.ts create mode 100644 src/tests/storyblok.spec.ts create mode 100644 src/tests/storyblokStore.spec.ts diff --git a/cypress.config.ts b/cypress.config.ts index f5e67ba7..afd236f8 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from 'cypress'; -import { svelte } from '@sveltejs/vite-plugin-svelte' +import { svelte } from '@sveltejs/vite-plugin-svelte'; export default defineConfig({ e2e: { @@ -12,8 +12,8 @@ export default defineConfig({ bundler: 'vite', viteConfig: () => { return { - plugins: [svelte()] - } + plugins: [svelte()], + }; }, }, }, diff --git a/cypress/component/page.cy.ts b/cypress/component/page.cy.ts index e68d1b24..75b588b2 100644 --- a/cypress/component/page.cy.ts +++ b/cypress/component/page.cy.ts @@ -1,7 +1,7 @@ /** Cypress is not working well with Svelte 5, so for now no StoryblokComponent tests are available. @todo Fix Cypress to work with Svelte 5 -*/ + */ describe('Basic Test', () => { it('can find an element', () => { // Create a basic element diff --git a/package.json b/package.json index 0b82a2a3..7839f623 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "cypress": "^13.17.0", "eslint": "^9.18.0", "eslint-plugin-svelte": "^2.36.0", + "jsdom": "^26.0.0", "publint": "^0.2.0", "simple-git-hooks": "^2.11.1", "start-server-and-test": "^2.0.10", @@ -88,7 +89,11 @@ "@commitlint/config-conventional" ], "rules": { - "body-max-line-length": [2, "always", 200] + "body-max-line-length": [ + 2, + "always", + 200 + ] } }, "release": { diff --git a/playground/sveltekit/src/lib/Page.svelte b/playground/sveltekit/src/lib/Page.svelte index d77b0e80..c3b09bb8 100644 --- a/playground/sveltekit/src/lib/Page.svelte +++ b/playground/sveltekit/src/lib/Page.svelte @@ -1,13 +1,19 @@ - -
- {#each blok.body as item} -
- -
- {/each} -
+{#key blok} +
+ {#each blok.body as bodyBlok} + + {/each} +
{@html resolvedRichText}
+
+{/key} diff --git a/playground/sveltekit/src/routes/+layout.svelte b/playground/sveltekit/src/routes/+layout.svelte index e52c8ac2..da879e26 100644 --- a/playground/sveltekit/src/routes/+layout.svelte +++ b/playground/sveltekit/src/routes/+layout.svelte @@ -1,8 +1,7 @@ -{@render children()} +{@render children?.()} diff --git a/playground/sveltekit/src/routes/+layout.ts b/playground/sveltekit/src/routes/+layout.ts index d700ca05..0a19e0f4 100644 --- a/playground/sveltekit/src/routes/+layout.ts +++ b/playground/sveltekit/src/routes/+layout.ts @@ -1,5 +1,5 @@ import type { LayoutLoad } from './$types'; -import { storyblokInit, apiPlugin, useStoryblokApi } from "@storyblok/svelte"; +import { storyblokInit, apiPlugin, getStoryblokApi } from "@storyblok/svelte"; import Teaser from '$lib/Teaser.svelte'; import Page from '$lib/Page.svelte'; import Feature from '$lib/Feature.svelte'; @@ -26,6 +26,6 @@ export const load: LayoutLoad = () => { }); return { - storyblokApi: useStoryblokApi() + storyblokApi: getStoryblokApi(), }; }; diff --git a/playground/sveltekit/src/routes/[...slug]/+page.svelte b/playground/sveltekit/src/routes/[...slug]/+page.svelte index 86b3b6c6..13114081 100644 --- a/playground/sveltekit/src/routes/[...slug]/+page.svelte +++ b/playground/sveltekit/src/routes/[...slug]/+page.svelte @@ -3,7 +3,7 @@ import type { PageData } from './$types'; import { onMount } from 'svelte'; - let { data }: { data: PageData } = $props(); + let { data = $bindable() }: { data: PageData } = $props(); let story = $state(data.story); let loaded = $state(false); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 525a7716..31507703 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,7 +29,7 @@ importers: version: 12.1.2(rollup@4.30.1)(tslib@2.8.1)(typescript@5.7.3) '@storyblok/eslint-config': specifier: ^0.3.0 - version: 0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)) + version: 0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0)) '@sveltejs/adapter-auto': specifier: ^3.3.1 version: 3.3.1(@sveltejs/kit@2.15.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.17.4)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0)))(svelte@5.17.4)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0))) @@ -51,6 +51,9 @@ importers: eslint-plugin-svelte: specifier: ^2.36.0 version: 2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4) + jsdom: + specifier: ^26.0.0 + version: 26.0.0 publint: specifier: ^0.2.0 version: 0.2.12 @@ -77,7 +80,7 @@ importers: version: 4.5.0(@types/node@22.10.6)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0)) vitest: specifier: ^2.1.8 - version: 2.1.8(@types/node@22.10.6) + version: 2.1.8(@types/node@22.10.6)(jsdom@26.0.0) playground/sveltekit: dependencies: @@ -165,6 +168,9 @@ packages: '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + '@asamuzakjp/css-color@2.8.2': + resolution: {integrity: sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -267,6 +273,34 @@ packages: resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} engines: {node: '>=v18'} + '@csstools/color-helpers@5.0.1': + resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.1': + resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-color-parser@3.0.7': + resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + engines: {node: '>=18'} + '@cypress/request@3.0.7': resolution: {integrity: sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==} engines: {node: '>= 6'} @@ -1099,6 +1133,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -1429,6 +1467,10 @@ packages: engines: {node: '>=4'} hasBin: true + cssstyle@4.2.1: + resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==} + engines: {node: '>=18'} + cypress@13.17.0: resolution: {integrity: sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -1442,6 +1484,10 @@ packages: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -1465,6 +1511,9 @@ packages: supports-color: optional: true + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} @@ -2057,10 +2106,22 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + http-signature@1.4.0: resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -2069,6 +2130,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2153,6 +2218,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} @@ -2201,6 +2269,15 @@ packages: resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} engines: {node: '>=12.0.0'} + jsdom@26.0.0: + resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -2355,6 +2432,10 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -2594,6 +2675,9 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nwsapi@2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} + object-inspect@1.13.3: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} @@ -2663,6 +2747,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} @@ -2879,6 +2966,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2895,6 +2985,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} @@ -3089,6 +3183,9 @@ packages: resolution: {integrity: sha512-ne4IhhVBwzpUByjo1ocxQnqRoWsRilc9Ry1j+0uPWhHmg4jS/nnlSwYYfx7Ium8okCZ4hYM89rg0B5G0hQzk+g==} engines: {node: '>=18'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.6.2: resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} engines: {node: '>=12.20'} @@ -3159,6 +3256,10 @@ packages: resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==} engines: {node: '>=16'} + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -3399,11 +3500,31 @@ packages: peerDependencies: eslint: '>=6.0.0' + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + wait-on@8.0.2: resolution: {integrity: sha512-qHlU6AawrgAIHlueGQHQ+ETcPLAauXbnoTKl3RKq20W0T8x0DKVAo5xWIYjHSyvHxQlcYbFdR0jp4T9bDVITFA==} engines: {node: '>=12.0.0'} hasBin: true + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} + engines: {node: '>=18'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3429,10 +3550,29 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3485,7 +3625,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@antfu/eslint-config@3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))': + '@antfu/eslint-config@3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))': dependencies: '@antfu/install-pkg': 0.4.1 '@clack/prompts': 0.7.0 @@ -3494,7 +3634,7 @@ snapshots: '@stylistic/eslint-plugin': 2.13.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/eslint-plugin': 8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/parser': 8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3) - '@vitest/eslint-plugin': 1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)) + '@vitest/eslint-plugin': 1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0)) eslint: 9.18.0(jiti@2.4.2) eslint-config-flat-gitignore: 0.3.0(eslint@9.18.0(jiti@2.4.2)) eslint-flat-config-utils: 0.4.0 @@ -3542,6 +3682,14 @@ snapshots: '@antfu/utils@0.7.10': {} + '@asamuzakjp/css-color@2.8.2': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + lru-cache: 11.0.2 + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -3685,6 +3833,26 @@ snapshots: '@types/conventional-commits-parser': 5.0.1 chalk: 5.4.1 + '@csstools/color-helpers@5.0.1': {} + + '@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/color-helpers': 5.0.1 + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-tokenizer@3.0.3': {} + '@cypress/request@3.0.7': dependencies: aws-sign2: 0.7.0 @@ -4138,9 +4306,9 @@ snapshots: '@sideway/pinpoint@2.0.0': {} - '@storyblok/eslint-config@0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))': + '@storyblok/eslint-config@0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))': dependencies: - '@antfu/eslint-config': 3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)) + '@antfu/eslint-config': 3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0)) eslint: 9.18.0(jiti@2.4.2) eslint-plugin-format: 0.1.3(eslint@9.18.0(jiti@2.4.2)) transitivePeerDependencies: @@ -4357,13 +4525,13 @@ snapshots: '@typescript-eslint/types': 8.20.0 eslint-visitor-keys: 4.2.0 - '@vitest/eslint-plugin@1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))': + '@vitest/eslint-plugin@1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))': dependencies: '@typescript-eslint/utils': 8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3) eslint: 9.18.0(jiti@2.4.2) optionalDependencies: typescript: 5.7.3 - vitest: 2.1.8(@types/node@22.10.6) + vitest: 2.1.8(@types/node@22.10.6)(jsdom@26.0.0) '@vitest/expect@2.1.8': dependencies: @@ -4482,6 +4650,8 @@ snapshots: acorn@8.14.0: {} + agent-base@7.1.3: {} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 @@ -4780,6 +4950,11 @@ snapshots: cssesc@3.0.0: {} + cssstyle@4.2.1: + dependencies: + '@asamuzakjp/css-color': 2.8.2 + rrweb-cssom: 0.8.0 + cypress@13.17.0: dependencies: '@cypress/request': 3.0.7 @@ -4832,6 +5007,11 @@ snapshots: dependencies: assert-plus: 1.0.0 + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + dayjs@1.11.13: {} de-indent@1.0.2: {} @@ -4848,6 +5028,8 @@ snapshots: optionalDependencies: supports-color: 8.1.1 + decimal.js@10.4.3: {} + decode-named-character-reference@1.0.2: dependencies: character-entities: 2.0.2 @@ -5583,16 +5765,38 @@ snapshots: hosted-git-info@2.8.9: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + http-signature@1.4.0: dependencies: assert-plus: 1.0.0 jsprim: 2.0.2 sshpk: 1.18.0 + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + human-signals@1.1.1: {} human-signals@2.1.0: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore-walk@5.0.1: @@ -5654,6 +5858,8 @@ snapshots: is-path-inside@3.0.3: {} + is-potential-custom-element-name@1.0.1: {} + is-reference@3.0.3: dependencies: '@types/estree': 1.0.6 @@ -5694,6 +5900,34 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} + jsdom@26.0.0: + dependencies: + cssstyle: 4.2.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.1 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.16 + parse5: 7.2.1 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@0.5.0: {} jsesc@3.1.0: {} @@ -5833,6 +6067,8 @@ snapshots: dependencies: tslib: 2.8.1 + lru-cache@11.0.2: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -6235,6 +6471,8 @@ snapshots: dependencies: boolbase: 1.0.0 + nwsapi@2.2.16: {} + object-inspect@1.13.3: {} once@1.4.0: @@ -6306,6 +6544,10 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5@7.2.1: + dependencies: + entities: 4.5.0 + pascal-case@3.1.2: dependencies: no-case: 3.0.4 @@ -6500,6 +6742,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 + rrweb-cssom@0.8.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -6516,6 +6760,10 @@ snapshots: safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scslre@0.3.0: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -6738,6 +6986,8 @@ snapshots: magic-string: 0.30.17 zimmerframe: 1.1.2 + symbol-tree@3.2.4: {} + synckit@0.6.2: dependencies: tslib: 2.8.1 @@ -6792,6 +7042,10 @@ snapshots: dependencies: tldts: 6.1.71 + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + tree-kill@1.2.2: {} ts-api-utils@2.0.0(typescript@5.7.3): @@ -6949,7 +7203,7 @@ snapshots: optionalDependencies: vite: 6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0) - vitest@2.1.8(@types/node@22.10.6): + vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0): dependencies: '@vitest/expect': 2.1.8 '@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.6)) @@ -6973,6 +7227,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 + jsdom: 26.0.0 transitivePeerDependencies: - less - lightningcss @@ -6999,6 +7254,10 @@ snapshots: transitivePeerDependencies: - supports-color + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + wait-on@8.0.2(debug@4.4.0): dependencies: axios: 1.7.9(debug@4.4.0) @@ -7009,6 +7268,19 @@ snapshots: transitivePeerDependencies: - debug + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.1.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -7034,8 +7306,14 @@ snapshots: wrappy@1.0.2: {} + ws@8.18.0: {} + xml-name-validator@4.0.0: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + y18n@5.0.8: {} yallist@4.0.0: {} diff --git a/src/demo.spec.ts b/src/demo.spec.ts deleted file mode 100644 index aa934263..00000000 --- a/src/demo.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -describe('sum test', () => { - it('adds 1 + 2 to equal 3', () => { - expect(1 + 2).toBe(3); - }); -}); diff --git a/src/lib/StoryblokComponent.svelte b/src/lib/StoryblokComponent.svelte index a2ac1407..153b2ac2 100644 --- a/src/lib/StoryblokComponent.svelte +++ b/src/lib/StoryblokComponent.svelte @@ -1,19 +1,16 @@ - +{#if component} + +{/if} diff --git a/src/lib/index.ts b/src/lib/index.ts index 65053fcd..fd97ad74 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,79 +1,5 @@ -import { - storyblokEditable as sbEdit, - storyblokInit as sbInit, -} from '@storyblok/js'; - -import type { - SbBlokData, - SbSvelteComponentsMap, - SbSvelteSDKOptions, - StoryblokClient, -} from './types'; - +export * from './storyblok'; export { default as StoryblokComponent } from './StoryblokComponent.svelte'; - -export const storyblokEditable = (node: HTMLElement, value: SbBlokData) => { - const updateDom = (value: SbBlokData) => { - const options = sbEdit(value); - if (options['data-blok-c']) { - node.setAttribute('data-blok-c', options['data-blok-c']); - node.setAttribute('data-blok-uid', options['data-blok-uid']); - node.classList.add('storyblok__outline'); - } - }; - - updateDom(value); // when is mounted - - return { - update(newValue: SbBlokData) { - // when value changes - updateDom(newValue); - }, - }; -}; - -let storyblokApiInstance: StoryblokClient; -export const useStoryblokApi = (): StoryblokClient => { - if (!storyblokApiInstance) { - // eslint-disable-next-line no-console - console.log( - `You can't use getStoryblokApi if you're not loading apiPlugin.`, - ); - } - return storyblokApiInstance; -}; - -export { useStoryblokApi as getStoryblokApi }; - -let componentsMap: SbSvelteComponentsMap | CallableFunction = {}; - -export const storyblokInit = (options: SbSvelteSDKOptions) => { - const { storyblokApi } = sbInit(options); - storyblokApiInstance = storyblokApi ?? storyblokApiInstance; - componentsMap = options.components || {}; -}; - -export const getComponent = (componentName: string) => { - let component = null; - component - = typeof componentsMap === 'function' - ? componentsMap()[componentName] - : componentsMap[componentName]; - - if (!component) { - console.error(`You didn't load the ${componentName} component. Please load it in storyblokInit. For example: -storyblokInit({ - accessToken: "", - components: { - "teaser": Teaser - } -}) -`); - } - - return component; -}; - export * from './types'; export { apiPlugin, diff --git a/src/lib/storyblok.ts b/src/lib/storyblok.ts new file mode 100644 index 00000000..6ef1bc39 --- /dev/null +++ b/src/lib/storyblok.ts @@ -0,0 +1,63 @@ +import { + storyblokEditable as sbEdit, +} from '@storyblok/js'; + +import type { + SbBlokData, +} from './types'; + +/** + * Creates options object for Storyblok editable elements + */ +const createEditableOptions = (value: SbBlokData) => { + const options = sbEdit(value); + return options['data-blok-c'] + ? { + 'data-blok-c': options['data-blok-c'], + 'data-blok-uid': options['data-blok-uid'], + 'className': 'storyblok__outline', + } + : null; +}; + +/** + * Applies Storyblok editable attributes to a DOM node + */ +const applyEditableOptions = (node: HTMLElement, options: ReturnType) => { + if (!options) { + return; + } + + node.setAttribute('data-blok-c', options['data-blok-c']); + node.setAttribute('data-blok-uid', options['data-blok-uid']); + node.classList.add(options.className); +}; + +/** + * Svelte action to make a component editable in Storyblok + */ +export const storyblokEditable = (node: HTMLElement, value: SbBlokData) => { + const updateDom = (value: SbBlokData) => { + const options = createEditableOptions(value); + if (options) { + applyEditableOptions(node, options); + } + }; + + updateDom(value); + + return { + update: updateDom, + }; +}; + +export { + getComponent, + getStoryblokApi, + storyblokInit, +} from './storyblokStore'; + +/** + * @deprecated Use getStoryblokApi() instead. This will be removed in a next major version. + */ +export { getStoryblokApi as useStoryblokApi } from './storyblokStore'; diff --git a/src/lib/storyblokStore.ts b/src/lib/storyblokStore.ts new file mode 100644 index 00000000..982a4a81 --- /dev/null +++ b/src/lib/storyblokStore.ts @@ -0,0 +1,72 @@ +import { storyblokInit as sbInit } from '@storyblok/js'; +import type { + SbSvelteComponentsMap, + SbSvelteSDKOptions, + StoryblokClient, +} from './types'; + +/** + * Creates a singleton store for managing Storyblok state + */ +const createStoryblokStore = () => { + let api: StoryblokClient | null = null; + let components: SbSvelteComponentsMap | CallableFunction = {}; + + /** + * Retrieves the Storyblok API instance + */ + const getApi = (): StoryblokClient => { + if (!api) { + console.error('Storyblok API not initialized. Make sure to load apiPlugin.'); + } + return api!; + }; + + /** + * Initializes the Storyblok SDK with provided options + */ + const init = (options: SbSvelteSDKOptions) => { + const { storyblokApi } = sbInit(options); + api = storyblokApi ?? api; + components = options.components ?? {}; + }; + + /** + * Retrieves a component from the registered components map + */ + const getComponent = (componentName: string) => { + const resolvedComponent = typeof components === 'function' + ? components()[componentName] + : components[componentName]; + + if (!resolvedComponent) { + console.error( + `Component "${componentName}" not found. Please register it in storyblokInit:\n` + + `storyblokInit({ + accessToken: "", + components: { + "${componentName}": YourComponent + } + })`, + ); + } + + return resolvedComponent; + }; + + return { + init, + getApi, + getComponent, + }; +}; + +// Create singleton instance +const storyblokStore = createStoryblokStore(); + +// Export store methods +export const { + init: storyblokInit, + getApi: getStoryblokApi, + getComponent, +} = storyblokStore; diff --git a/src/tests/storyblok.init.spec.ts b/src/tests/storyblok.init.spec.ts new file mode 100644 index 00000000..4bee33bd --- /dev/null +++ b/src/tests/storyblok.init.spec.ts @@ -0,0 +1,21 @@ +import { describe, expect, it, vi } from 'vitest'; +import { getStoryblokApi } from '../lib/storyblokStore'; + +// We simulate an uninitialized API by returning null +vi.mock('@storyblok/js', () => ({ + storyblokInit: vi.fn(() => ({ + storyblokApi: null, + })), +})); + +describe('storyblokStore initialization', () => { + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}); + + it('should log error if API is not initialized', () => { + // API is not initialized + getStoryblokApi(); + expect(consoleError).toHaveBeenCalledWith( + 'Storyblok API not initialized. Make sure to load apiPlugin.', + ); + }); +}); diff --git a/src/tests/storyblok.spec.ts b/src/tests/storyblok.spec.ts new file mode 100644 index 00000000..0000a0a2 --- /dev/null +++ b/src/tests/storyblok.spec.ts @@ -0,0 +1,60 @@ +import { describe, expect, it, vi } from 'vitest'; +import { storyblokEditable } from '../lib/storyblok'; +import type { SbBlokData } from '../lib/types'; + +// Mock @storyblok/js +vi.mock('@storyblok/js', () => ({ + storyblokEditable: (blok: SbBlokData) => { + if (blok._editable) { + return { + 'data-blok-c': '1234', + 'data-blok-uid': '123-456', + 'class': 'storyblok__outline', + }; + } + return {}; + }, +})); + +describe('storyblok utilities', () => { + describe('storyblokEditable', () => { + it('should apply editable attributes to DOM node', () => { + // Create a test DOM node + const node = document.createElement('div'); + + // Mock Storyblok blok data + const mockBlok: SbBlokData = { + _uid: '123-456', + component: 'test-component', + _editable: '', + }; + + // Apply storyblokEditable action + storyblokEditable(node, mockBlok); + + // Assert that the correct attributes were applied + expect(node.hasAttribute('data-blok-c')).toBe(true); + expect(node.hasAttribute('data-blok-uid')).toBe(true); + expect(node.classList.contains('storyblok__outline')).toBe(true); + }); + + it('should not apply attributes when _editable is not present', () => { + // Create a test DOM node + const node = document.createElement('div'); + + // Mock Storyblok blok data without _editable + const mockBlok: SbBlokData = { + _uid: '123-456', + component: 'test-component', + }; + + // Apply storyblokEditable action + storyblokEditable(node, mockBlok); + + // Assert that no attributes were applied + expect(node.getAttribute('data-blok-c')).toBe(null); + expect(node.getAttribute('data-blok-uid')).toBe(null); + expect(node.classList.contains('storyblok__outline')).toBe(false); + }); + }); +}); diff --git a/src/tests/storyblokStore.spec.ts b/src/tests/storyblokStore.spec.ts new file mode 100644 index 00000000..91f1a3ea --- /dev/null +++ b/src/tests/storyblokStore.spec.ts @@ -0,0 +1,102 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { getComponent, getStoryblokApi, storyblokInit } from '../lib/storyblokStore'; +import type { SbSvelteSDKOptions } from '../lib/types'; + +// Mock @storyblok/js +vi.mock('@storyblok/js', () => ({ + storyblokInit: vi.fn(() => ({ + storyblokApi: { test: 'api' }, + })), +})); + +describe('storyblokStore', () => { + // Mock console.error to prevent noise in tests + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}); + + beforeEach(() => { + // Clear console.error mock calls between tests + consoleError.mockClear(); + }); + + describe('storyblokInit', () => { + it('should initialize with components', () => { + const TestComponent = class {}; + const options: SbSvelteSDKOptions = { + accessToken: 'test-token', + components: { + 'test-component': TestComponent, + }, + }; + + storyblokInit(options); + + // Verify component is registered by trying to retrieve it + const component = getComponent('test-component'); + expect(component).toBe(TestComponent); + }); + + it('should initialize with function components', () => { + const TestComponent = class {}; + const options: SbSvelteSDKOptions = { + accessToken: 'test-token', + components: () => ({ + 'test-component': TestComponent, + }), + }; + + storyblokInit(options); + + // Verify component is registered by trying to retrieve it + const component = getComponent('test-component'); + expect(component).toBe(TestComponent); + }); + + it('should initialize without components', () => { + const options: SbSvelteSDKOptions = { + accessToken: 'test-token', + }; + + storyblokInit(options); + + // Trying to get a non-existent component should trigger error + getComponent('non-existent'); + expect(consoleError).toHaveBeenCalled(); + }); + }); + + describe('getComponent', () => { + it('should return registered component', () => { + const TestComponent = class {}; + storyblokInit({ + accessToken: 'test-token', + components: { + 'test-component': TestComponent, + }, + }); + + const component = getComponent('test-component'); + expect(component).toBe(TestComponent); + }); + + it('should return undefined and log error for non-existent component', () => { + const component = getComponent('non-existent'); + + expect(component).toBeUndefined(); + expect(consoleError).toHaveBeenCalledWith( + expect.stringContaining('Component "non-existent" not found'), + ); + }); + }); + + describe('getStoryblokApi', () => { + it('should return initialized API', () => { + storyblokInit({ + accessToken: 'test-token', + }); + + // API is initialized + const api = getStoryblokApi(); + expect(api).toEqual({ test: 'api' }); + }); + }); +}); diff --git a/vite.config.ts b/vite.config.ts index 755809d3..174019ad 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ plugins: [dts({ insertTypesEntry: true }), sveltekit()] as Plugin[], test: { - include: ['./src/__tests__/**/*', './src/**/*.spec.ts'], + environment: 'jsdom', + include: ['./src/tests/**/*', './src/**/*.spec.ts'], }, });