diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 900d44e..9868be1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,9 +1,9 @@ version: 2 updates: - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: npm + directory: / schedule: - interval: "daily" + interval: daily commit-message: prefix: fix prefix-development: chore @@ -11,18 +11,18 @@ updates: groups: linting-dx-tools: patterns: - - "*lint*" # eslint, eslint-*, vue-eslint-parser, lint-staged, @commitlint/*, @typescript-eslint/* - - "prettier" - - "husky" - - "@vue/tsconfig" - - "*babel*" # babel-*, @vue/babel-preset-app, @babel/* + - '*lint*' # eslint, eslint-*, vue-eslint-parser, lint-staged, @commitlint/*, @typescript-eslint/* + - prettier + - husky + - '@vue/tsconfig' + - '*babel*' # babel-*, @vue/babel-preset-app, @babel/* update-types: - - "minor" - - "patch" + - minor + - patch testing-tools: patterns: - - "@vue/test-utils" - - "vitest" + - '@vue/test-utils' + - vitest update-types: - - "minor" - - "patch" \ No newline at end of file + - minor + - patch diff --git a/README.md b/README.md index f88f08f..58b84dc 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,10 @@
- ## 🚀 Usage > If you are first-time user of the Storyblok, read the [Getting Started](https://www.storyblok.com/docs/guide/getting-started?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-richtext) guide to get a project ready in less than 5 minutes. - ### Installation ```bash @@ -56,17 +54,17 @@ pnpm add @storyblok/richtext ### Basic ```ts -import { richTextResolver } from '@storyblok/richtext' +import { richTextResolver } from '@storyblok/richtext'; -const { render } = richTextResolver() +const { render } = richTextResolver(); -const html = render(doc) +const html = render(doc); document.querySelector- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 | 1x -1x -1x -33x -31x -31x -31x -31x -31x -31x -15x -9x -15x -6x -6x -15x -31x -33x -30x -4x -4x -30x -26x -26x -30x -4x -4x -30x -26x -26x -30x -30x -30x -30x -30x -18x -18x -18x -18x -5x -5x -18x -5x -5x -18x -5x -5x -18x -18x -18x -18x -18x -30x -30x -30x -3x -9x -6x -6x -9x -3x -3x -3x -3x -3x -30x -30x -30x -2x -2x -30x -31x -31x -31x -31x -33x -2x -2x -33x -8x -8x -31x -31x -31x -31x -31x -31x | import { ImageOptimizationOptions } from "./types"; - -export function optimizeImage(src: string, options?: boolean | Partial<ImageOptimizationOptions>): { src: string, attrs: Record<string, any>} { - if (!options) return {src, attrs: {}}; - let w = 0; - let h = 0; - const attrs: Record<string, unknown> = {}; - const filterParams: string[] = []; - - function validateAndPushFilterParam(value: number, min: number, max: number, filter: string, filterParams: string[]) { - if (typeof value !== 'number' || value <= min || value >= max) { - console.warn(`[StoryblokRichText] - ${filter.charAt(0).toUpperCase() + filter.slice(1)} value must be a number between ${min} and ${max} (inclusive)`); - } else { - filterParams.push(`${filter}(${value})`); - } - } - - if(typeof options === 'object') { - if (typeof options.width === 'number' && options.width > 0) { - attrs.width = options.width; - w = options.width; - } else { - console.warn("[StoryblokRichText] - Width value must be a number greater than 0"); - } - if (options.height && typeof options.height === 'number' && options.height > 0) { - attrs.height = options.height; - h = options.height; - } else { - console.warn("[StoryblokRichText] - Height value must be a number greater than 0"); - } - if(options.loading && ['lazy', 'eager'].includes(options.loading)) attrs.loading = options.loading; - if(options.class) attrs.class = options.class; - - - if(options.filters) { - const { filters } = options || {}; - const { blur, brightness, fill, format, grayscale, quality, rotate } = filters || {}; - - if (blur) { - validateAndPushFilterParam(blur, 0, 100, 'blur', filterParams); - } - if (quality) { - validateAndPushFilterParam(quality, 0, 100, 'quality', filterParams); - } - if (brightness) { - validateAndPushFilterParam(brightness, 0, 100, 'brightness', filterParams); - } - if (fill) filterParams.push(`fill(${fill})`); - if (grayscale) filterParams.push(`grayscale()`); - if (rotate && [0, 90, 180, 270].includes(options.filters.rotate)) filterParams.push(`rotate(${rotate})`); - if (format && ['webp', 'png', 'jpeg'].includes(format)) filterParams.push(`format(${format})`); - } - - // Construct srcset attribute - if (options.srcset) { - attrs.srcset = options.srcset.map((entry) => { - if (typeof entry === 'number') { - return `${src}/m/${entry}x0/${filterParams.length > 0 ? 'filters:' + filterParams.join(':') : ''} ${entry}w`; - } - if (Array.isArray(entry) && entry.length === 2) { - const [entryWidth, entryHeight] = entry; - return `${src}/m/${entryWidth}x${entryHeight}/${filterParams.length > 0 ? 'filters:' + filterParams.join(':') : ''} ${entryWidth}w`; - } - }).join(', '); - } - - // Construct sizes attribute - if (options.sizes) { - attrs.sizes = options.sizes.join(', '); - } - } - - // server-side WebP support detection https://www.storyblok.com/docs/image-service/#optimize - // https://a.storyblok.com/f/39898/3310x2192/e4ec08624e/demo-image.jpeg/m/ - let resultSrc = `${src}/m/`; - if(w > 0 && h > 0) { - resultSrc = `${resultSrc}${w}x${h}/`; - } - if(filterParams.length > 0) { - resultSrc = `${resultSrc}filters:${filterParams.join(':')}`; - } - - return { - src: resultSrc, - attrs, - }; -} |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
---|---|---|---|---|---|---|---|---|---|
images-optimization.ts | -
-
- |
- 100% | -87/87 | -91.11% | -41/45 | -100% | -2/2 | -100% | -87/87 | -
Hello, world!
') - }) - + }; + const html = render(paragraph as NodeHello, world!
'); + }); + it('should render a heading 1', async () => { - const { render } = richTextResolver({}) + const { render } = richTextResolver({}); const heading = { type: 'heading', attrs: { @@ -36,13 +35,13 @@ describe('richtext', () => { type: 'text', }, ], - } - const html = render(heading as Nodeconsole.log("Hello, world!")
')
- })
-
+ };
+ const html = render(code as Nodeconsole.log("Hello, world!")
');
+ });
+
it('should render a horizontal rule', async () => {
- const { render } = richTextResolver({})
+ const { render } = richTextResolver({});
const hr = {
type: 'horizontal_rule',
- }
- const html = render(hr as Node') - }) - }) + }; + const html = render(quote as NodeQuote
'); + }); + }); - describe('TextTypes & MarksTypes', () => { + describe('textTypes & MarksTypes', () => { it('should render text with marks', async () => { - const { render } = richTextResolver({}) + const { render } = richTextResolver({}); const text = { type: 'paragraph', content: [ @@ -219,13 +220,13 @@ describe('richtext', () => { marks: [{ type: 'bold' }, { type: 'italic' }], }, ], - } - const html = render(text as NodeQuote
Bold and italic
') - }) - + }; + const html = render(text as NodeBold and italic
'); + }); + it('should render text with styled marks', async () => { - const { render } = richTextResolver({}) + const { render } = richTextResolver({}); const text = { type: 'paragraph', content: [ @@ -235,14 +236,14 @@ describe('richtext', () => { marks: [{ type: 'styled', attrs: { color: 'red' } }, { type: 'styled', attrs: { color: 'blue' } }], }, ], - } - const html = render(text as NodeBold and italic
') - }) - + expect(html).toBe('Bold and italic
'); + }); + it('should render an external link', async () => { - const { render } = richTextResolver({}) + const { render } = richTextResolver({}); const link = { text: 'External link', type: 'text', @@ -256,13 +257,13 @@ describe('richtext', () => { }, }, ], - } - const html = render(link as NodeCode
')
- })
-
+ };
+ const html = render(code as NodeCode
');
+ });
+
it('should render a superscript text', async () => {
- const { render } = richTextResolver({})
+ const { render } = richTextResolver({});
const superscript = {
text: 'Superscript',
type: 'text',
marks: [{ type: 'superscript' }],
- }
- const html = render(superscript as Node- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
---|---|---|---|---|---|---|---|---|---|
src | -
-
- |
- 98.07% | -918/936 | -90.96% | -151/166 | -95.65% | -22/23 | -98.07% | -918/936 | -
src/types | -
-
- |
- 100% | -38/38 | -100% | -5/5 | -100% | -0/0 | -100% | -38/38 | -
File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
---|---|---|---|---|---|---|---|---|---|
src | +
+
+
+
+
+ |
+ 98.07% | +918/936 | +90.96% | +151/166 | +95.65% | +22/23 | +98.07% | +918/936 | +
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-1
2
3
@@ -791,21 +784,22 @@ All files / src images- }) }) |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-1
2
3
@@ -272,7 +265,7 @@ All files / src images-
if(options.filters) {
const { filters } = options || {};
const { blur, brightness, fill, format, grayscale, quality, rotate } = filters || {};
-
+
if (blur) {
validateAndPushFilterParam(blur, 0, 100, 'blur', filterParams);
}
@@ -306,8 +299,8 @@ All files / src images- if(filterParams.length > 0) { resultSrc = `${resultSrc}filters:${filterParams.join(':')}`; } - + return { src: resultSrc, attrs, }; } |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
---|---|---|---|---|---|---|---|---|---|
images-optimization.test.ts | -
-
- |
- 100% | -209/209 | -100% | -34/34 | -100% | -0/0 | -100% | -209/209 | -
images-optimization.ts | -
-
- |
- 100% | -72/72 | -89.13% | -41/46 | -100% | -2/2 | -100% | -72/72 | -
File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
---|---|---|---|---|---|---|---|---|---|
+ images-optimization.test.ts + | +
+
+
+
+
+ |
+ 100% | +209/209 | +100% | +34/34 | +100% | +0/0 | +100% | +209/209 | +
richtext.test.ts | -
-
- |
- 100% | -480/480 | -100% | -35/35 | -100% | -2/2 | -100% | -480/480 | -
+ images-optimization.ts + | +
+
+
+
+
+ |
+ 100% | +72/72 | +89.13% | +41/46 | +100% | +2/2 | +100% | +72/72 | +
richtext.ts | -
-
- |
- 89.71% | -157/175 | -80% | -40/50 | -94.44% | -17/18 | -89.71% | -157/175 | -
+ richtext.test.ts + | +
+
+
+
+
+ |
+ 100% | +480/480 | +100% | +35/35 | +100% | +2/2 | +100% | +480/480 | +
vite-env.d.ts | -
-
- |
- 0% | -0/0 | -0% | -1/1 | -0% | -1/1 | -0% | -0/0 | -
richtext.ts | +
+
+
+
+
+ |
+ 89.71% | +157/175 | +80% | +40/50 | +94.44% | +17/18 | +89.71% | +157/175 | +
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-1
2
3
@@ -1105,7 +1098,7 @@ All files / src richtex
const html = render(paragraph as Node<string>)
expect(html).toBe('<p key="p-2">Hello, world!</p>')
})
-
+
it('should render a heading 1', async () => {
const { render } = richTextResolver({})
const heading = {
@@ -1123,7 +1116,7 @@ All files / src richtex
const html = render(list as Node<string>)
expect(html).toBe('<ul key="ul-5"><li key="li-3">Item 1</li><li key="li-5">Item 2</li></ul>')
})
-
+
it('should render an ordered list', async () => {
const { render } = richTextResolver({})
const list = {
@@ -1182,7 +1175,7 @@ All files / src richtex
const html = render(image as Node<string>)
expect(html).toBe('<img src="https://example.com/image.jpg" alt="An image" key="img-1"></img>')
})
-
+
it('should render an emoji', async () => {
const { render } = richTextResolver({})
const emoji = {
@@ -1207,7 +1200,7 @@ All files / src richtex
const html = render(code as Node<string>)
expect(html).toBe('<pre key="code-2"><code key="code-2">console.log("Hello, world!")</code></pre>')
})
-
+
it('should render a horizontal rule', async () => {
const { render } = richTextResolver({})
const hr = {
@@ -1231,7 +1224,7 @@ All files / src richtex
const html = render(br as Node<string>)
expect(html).toBe('<br key="br-1"></br>')
})
-
+
it('should render a quote' , async () => {
const { render } = richTextResolver({})
const quote = {
@@ -1278,7 +1271,7 @@ All files / src richtex
// Update the expected HTML to reflect the styles
expect(html).toBe('<p key="p-5"><span style="color: blue" key="span-5"><span style="color: red" key="span-4">Bold and italic</span></span></p>')
})
-
+
it('should render an external link', async () => {
const { render } = richTextResolver({})
const link = {
@@ -1315,7 +1308,7 @@ All files / src richtex
const html = render(link as Node<string>)
expect(html).toBe('<a target="_self" href="#anchor" key="a-3">Anchor link</a>')
})
-
+
it('should render an email link', async () => {
const { render } = richTextResolver({})
const link = {
@@ -1354,7 +1347,7 @@ All files / src richtex
const html = render(link as Node<string>)
expect(html).toBe('<a uuid="2bbf3ee7-acbe-401c-ade5-cf33e6e0babb" target="_blank" href="/" key="a-3">Internal Link</a>')
})
-
+
it('should render an asset link', async () => {
const { render } = richTextResolver({})
const link = {
@@ -1395,7 +1388,7 @@ All files / src richtex
const html = render(bold as Node<string>)
expect(html).toBe('<strong key="strong-3">Bold</strong>')
})
-
+
it('should render an italic text', async () => {
const { render } = richTextResolver({})
const italic = {
@@ -1417,7 +1410,7 @@ All files / src richtex
const html = render(underline as Node<string>)
expect(html).toBe('<u key="u-3">Underline</u>')
})
-
+
it('should render a strike text', async () => {
const { render } = richTextResolver({})
const strike = {
@@ -1439,7 +1432,7 @@ All files / src richtex
const html = render(code as Node<string>)
expect(html).toBe('<code key="code-3">Code</code>')
})
-
+
it('should render a superscript text', async () => {
const { render } = richTextResolver({})
const superscript = {
@@ -1461,7 +1454,7 @@ All files / src richtex
const html = render(subscript as Node<string>)
expect(html).toBe('<sub key="sub-3">Subscript</sub>')
})
-
+
it('should render a highlight text', async () => {
const { render } = richTextResolver({})
const highlight = {
@@ -1592,21 +1585,22 @@ |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-1
2
3
@@ -698,21 +691,21 @@ All files / src richtex
* Converts an object of attributes to a string.
*
* @param {Record<string, string>} [attrs={}]
- *
+ *
* @returns {string} The string representation of the attributes.
- *
+ *
* @example
- *
+ *
* ```typescript
* const attrs = {
* class: 'text-red',
* style: 'color: red',
* }
- *
+ *
* const attrsString = attrsToString(attrs)
- *
+ *
* console.log(attrsString) // 'class="text-red" style="color: red"'
- *
+ *
* ```
*
*/
@@ -724,19 +717,19 @@ All files / src richtex
*
* @param {string} unsafeText
* @return {*} {string}
- *
+ *
* @example
- *
+ *
* ```typescript
* const unsafeText = '<script>alert("Hello")</script>'
- *
+ *
* const safeText = escapeHtml(unsafeText)
- *
+ *
* console.log(safeText) // '<script>alert("Hello")</script>'
* ```
*/
@@ -789,7 +782,7 @@ All files / src richtex
return renderFn('a', { ...rest, href: finalHref, key: `a-${currentKey}` }, node.text as any) as T
}
-
+
const componentResolver: StoryblokRichTextNodeResolver<T> = (node: StoryblokRichTextNode<T>): T => {
console.warn('[StoryblokRichtText] - BLOK resolver is not available for vanilla usage')
return renderFn('span', {
@@ -975,24 +968,24 @@ All files / src richtex } |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
---|---|---|---|---|---|---|---|---|---|
index.ts | +
+
+
+
+
+ |
+ 100% | +38/38 | +100% | +5/5 | +100% | +0/0 | +100% | +38/38 | +
File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
---|---|---|---|---|---|---|---|---|---|
index.ts | -
-
- |
- 100% | -38/38 | -100% | -5/5 | -100% | -0/0 | -100% | -38/38 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-1
2
3
@@ -730,9 +723,9 @@ All files / src/typesAll files / src/typesAll files / src/typesAll files / src/typesAll files / src/typesAll files / src/typesAll files / src/typesAll files / src/types |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the + previous block.
-1 2 | /// <reference types="vite/client" /> |