├── examples └── .gitkeep ├── src ├── PrismicTable │ ├── index.ts │ ├── PrismicTableRow.vue │ ├── PrismicTableDefaultComponents.ts │ ├── types.ts │ ├── PrismicTable.vue │ └── getTableComponentProps.ts ├── PrismicRichText │ ├── index.ts │ ├── PrismicRichTextSerialize.vue │ ├── getRichTextComponentProps.ts │ ├── types.ts │ ├── PrismicRichTextDefaultComponent.vue │ └── PrismicRichText.vue ├── SliceZone │ ├── index.ts │ ├── TODOSliceComponent.ts │ ├── defineSliceZoneComponents.ts │ ├── SliceZone.vue │ ├── getSliceComponentProps.ts │ └── types.ts ├── lib │ ├── devMsg.ts │ └── isInternalURL.ts ├── index.ts ├── PrismicText.vue ├── types.ts ├── PrismicLink.vue └── PrismicImage.vue ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── release.yml │ ├── prerelease-pr-cleanup.yml │ ├── prerelease-canary.yml │ ├── prerelease-pr.yml │ └── validate.yml ├── tsdown.config.ts ├── .gitattributes ├── .editorconfig ├── test ├── __fixtures__ │ ├── richText.ts │ ├── linkRichText.json │ ├── WrapperComponent.ts │ ├── router.ts │ └── enRichText.json ├── __setup__.ts ├── lib-isInternalURL.test.ts ├── SliceZone.prod.test.ts ├── PrismicText.test.ts ├── PrismicRichText.test.ts ├── SliceZone.test.ts ├── PrismicLink.test.ts ├── PrismicTable.test.ts └── PrismicImage.test.ts ├── vite.config.ts ├── .gitignore ├── tsconfig.json ├── .prettierignore ├── .oxlintrc.json ├── messages ├── prismictext-works-only-with-rich-text-and-title-fields.md ├── alt-must-be-an-empty-string.md └── html-serialization-is-deprecated-with-prismic-rich-text.md ├── .prettierrc ├── package.json ├── CONTRIBUTING.md ├── README.md ├── LICENSE └── CHANGELOG.md /examples/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/PrismicTable/index.ts: -------------------------------------------------------------------------------- 1 | export * as getTableComponentProps from "./getTableComponentProps" 2 | -------------------------------------------------------------------------------- /src/PrismicRichText/index.ts: -------------------------------------------------------------------------------- 1 | export { getRichTextComponentProps } from "./getRichTextComponentProps" 2 | 3 | export type { VueRichTextSerializer, RichTextComponentProps } from "./types" 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 👪 Prismic Community Forum 4 | url: https://community.prismic.io 5 | about: Ask a question about the package or raise an issue directly related to Prismic. You will usually get support there more quickly! 6 | -------------------------------------------------------------------------------- /tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsdown" 2 | 3 | export default defineConfig({ 4 | entry: "./src/index.ts", 5 | format: ["esm", "cjs"], 6 | platform: "neutral", 7 | unbundle: true, 8 | sourcemap: true, 9 | exports: true, 10 | fromVite: true, 11 | dts: { vue: true }, 12 | }) 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # asserts everything is text 2 | * text eol=lf 3 | 4 | # treats lock files as binaries to prevent merge headache 5 | package-lock.json -diff 6 | yarn.lock -diff 7 | 8 | # treats assets as binaries 9 | *.png binary 10 | *.jpg binary 11 | *.jpeg binary 12 | *.gif binary 13 | *.ico binary 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /test/__fixtures__/richText.ts: -------------------------------------------------------------------------------- 1 | import type { RichTextField } from "@prismicio/client" 2 | 3 | import enRichTextJSON from "./enRichText.json" 4 | import linkRichTextJSON from "./linkRichText.json" 5 | 6 | export const richTextFixture = { 7 | en: enRichTextJSON as RichTextField, 8 | link: linkRichTextJSON as RichTextField, 9 | } 10 | -------------------------------------------------------------------------------- /test/__setup__.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach } from "vitest" 2 | 3 | import type { MockFactory } from "@prismicio/mock" 4 | import { createMockFactory } from "@prismicio/mock" 5 | 6 | declare module "vitest" { 7 | export interface TestContext { 8 | mock: MockFactory 9 | } 10 | } 11 | 12 | beforeEach((ctx) => { 13 | ctx.mock = createMockFactory({ seed: ctx.task.name }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/SliceZone/index.ts: -------------------------------------------------------------------------------- 1 | export { TODOSliceComponent } from "./TODOSliceComponent" 2 | export { getSliceComponentProps } from "./getSliceComponentProps" 3 | export { defineSliceZoneComponents } from "./defineSliceZoneComponents" 4 | 5 | export type { 6 | SliceComponentProps, 7 | SliceComponentType, 8 | SliceLike, 9 | SliceLikeGraphQL, 10 | SliceLikeRestV2, 11 | SliceZoneComponents, 12 | SliceZoneLike, 13 | } from "./types" 14 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import vue from "@vitejs/plugin-vue" 3 | import { defineConfig } from "vite" 4 | 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | test: { 8 | setupFiles: ["./test/__setup__"], 9 | typecheck: { 10 | enabled: true, 11 | checker: "vue-tsc", 12 | }, 13 | coverage: { 14 | provider: "v8", 15 | reporter: ["lcovonly", "text"], 16 | include: ["src"], 17 | }, 18 | environment: "jsdom", 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /test/__fixtures__/linkRichText.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "paragraph", 4 | "text": "link", 5 | "spans": [ 6 | { 7 | "start": 0, 8 | "end": 4, 9 | "type": "hyperlink", 10 | "data": { 11 | "id": "XvoFFREAAM0WGBng", 12 | "type": "page", 13 | "tags": ["test"], 14 | "slug": "have-you-heard-of-that-bird-puffin", 15 | "lang": "en-us", 16 | "uid": "home", 17 | "url": "/link", 18 | "link_type": "Document", 19 | "isBroken": false 20 | } 21 | } 22 | ] 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # custom 2 | dist 3 | examples/**/package-lock.json 4 | 5 | # os 6 | .DS_Store 7 | ._* 8 | 9 | # node 10 | logs 11 | *.log 12 | node_modules 13 | 14 | # yarn 15 | yarn-debug.log* 16 | yarn-error.log* 17 | lerna-debug.log* 18 | .yarn-integrity 19 | yarn.lock 20 | 21 | # npm 22 | npm-debug.log* 23 | 24 | # tests 25 | coverage 26 | .eslintcache 27 | .nyc_output 28 | 29 | # .env 30 | .env 31 | .env.test 32 | .env*.local 33 | 34 | # vscode 35 | .vscode/* 36 | !.vscode/tasks.json 37 | !.vscode/launch.json 38 | *.code-workspace 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "skipLibCheck": true, 5 | 6 | "target": "esnext", 7 | "module": "preserve", 8 | "moduleResolution": "bundler", 9 | "declaration": true, 10 | "declarationMap": true, 11 | "resolveJsonModule": true, 12 | "allowSyntheticDefaultImports": true, 13 | "esModuleInterop": true, 14 | 15 | "forceConsistentCasingInFileNames": true, 16 | "jsx": "preserve", 17 | "lib": ["esnext", "dom", "dom.iterable"], 18 | "types": ["node"] 19 | }, 20 | "exclude": ["node_modules", "dist", "examples"] 21 | } 22 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # custom 2 | CHANGELOG.md 3 | 4 | # .gitignore copy 5 | 6 | # custom 7 | dist 8 | examples/**/package-lock.json 9 | 10 | # os 11 | .DS_Store 12 | ._* 13 | 14 | # node 15 | logs 16 | *.log 17 | node_modules 18 | 19 | # yarn 20 | yarn-debug.log* 21 | yarn-error.log* 22 | lerna-debug.log* 23 | .yarn-integrity 24 | yarn.lock 25 | 26 | # npm 27 | npm-debug.log* 28 | 29 | # tests 30 | coverage 31 | .eslintcache 32 | .nyc_output 33 | 34 | # .env 35 | .env 36 | .env.test 37 | .env*.local 38 | 39 | # vscode 40 | .vscode/* 41 | !.vscode/tasks.json 42 | !.vscode/launch.json 43 | *.code-workspace 44 | -------------------------------------------------------------------------------- /src/lib/devMsg.ts: -------------------------------------------------------------------------------- 1 | import { version } from "../../package.json" 2 | 3 | /** 4 | * Returns a `prismic.dev/msg` URL for a given message slug. 5 | * 6 | * @example 7 | * 8 | * ```ts 9 | * devMsg("missing-param") 10 | * // => "https://prismic.dev/msg/svelte/v1.2.3/missing-param.md" 11 | * ``` 12 | * 13 | * @param slug - Slug for the message. This corresponds to a Markdown file in 14 | * the Git repository's `/messages` directory. 15 | * 16 | * @returns The `prismic.dev/msg` URL for the given slug. 17 | */ 18 | export const devMsg = (slug: string): string => { 19 | return `https://prismic.dev/msg/vue/v${version}/${slug}` 20 | } 21 | -------------------------------------------------------------------------------- /test/__fixtures__/WrapperComponent.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentPropsOptions, DefineComponent } from "vue" 2 | import { defineComponent, h } from "vue" 3 | 4 | /** Creates a new component. */ 5 | export const createWrapperComponent = ( 6 | suffix: string | number = "", 7 | props: ComponentPropsOptions = {}, 8 | ): T => 9 | defineComponent({ 10 | name: `WrapperComponent${suffix}`, 11 | props, 12 | setup(_props, { slots }) { 13 | return () => { 14 | return h( 15 | "div", 16 | { 17 | class: `wrapperComponent${suffix}`, 18 | }, 19 | slots.default && slots.default(), 20 | ) 21 | } 22 | }, 23 | }) as unknown as T 24 | 25 | export const WrapperComponent = createWrapperComponent() 26 | -------------------------------------------------------------------------------- /src/lib/isInternalURL.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if a URL is internal or external. 3 | * 4 | * @param url - The URL to check if internal or external 5 | * 6 | * @returns `true` if `url` is internal, `false` otherwise 7 | */ 8 | // TODO: This does not detect all relative URLs as internal, such as `about` or `./about`. This function assumes relative URLs start with a "/"`. 9 | export const isInternalURL = (url: string): boolean => { 10 | /** @see Regex101 expression: {@link https://regex101.com/r/1y7iod/1} */ 11 | const isInternal = /^\/(?!\/)/.test(url) 12 | /** @see Regex101 expression: {@link https://regex101.com/r/RnUseS/1} */ 13 | const isSpecialLink = !isInternal && !/^https?:\/\//i.test(url) 14 | 15 | return isInternal && !isSpecialLink 16 | } 17 | -------------------------------------------------------------------------------- /test/__fixtures__/router.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from "vue-router" 2 | import { createMemoryHistory, createRouter } from "vue-router" 3 | 4 | import { WrapperComponent } from "./WrapperComponent" 5 | 6 | const routes: Array = [ 7 | { 8 | path: "/", 9 | name: "Index", 10 | component: WrapperComponent, 11 | }, 12 | { 13 | path: "/foo", 14 | name: "Foo", 15 | component: WrapperComponent, 16 | }, 17 | { 18 | path: "/bar", 19 | name: "Bar", 20 | component: WrapperComponent, 21 | }, 22 | { 23 | path: "/baz", 24 | name: "Baz", 25 | component: WrapperComponent, 26 | }, 27 | ] 28 | 29 | const router = createRouter({ 30 | history: createMemoryHistory(), 31 | routes, 32 | }) 33 | 34 | export default router 35 | -------------------------------------------------------------------------------- /test/lib-isInternalURL.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest" 2 | 3 | import { isInternalURL } from "../src/lib/isInternalURL" 4 | 5 | it("returns true for internal URLs", () => { 6 | expect(isInternalURL("/")).toBe(true) 7 | expect(isInternalURL("/internal")).toBe(true) 8 | }) 9 | 10 | it("returns false for external URLs", () => { 11 | expect(isInternalURL("//example.com")).toBe(false) 12 | expect(isInternalURL("//example.com/image.png")).toBe(false) 13 | expect(isInternalURL("//example.com#anchor")).toBe(false) 14 | expect(isInternalURL("https://example.com")).toBe(false) 15 | expect(isInternalURL("mailto:example.com")).toBe(false) 16 | expect(isInternalURL("tel:example.com")).toBe(false) 17 | expect(isInternalURL("ftp:example.com")).toBe(false) 18 | }) 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🙋‍♀️ Feature request 3 | about: Suggest an idea or enhancement for the package. 4 | title: "" 5 | labels: "enhancement" 6 | assignees: "" 7 | --- 8 | 9 | 10 | 11 | ### Is your feature request related to a problem? Please describe. 12 | 13 | 14 | 15 | ### Describe the solution you'd like 16 | 17 | 18 | 19 | ### Describe alternatives you've considered 20 | 21 | 22 | 23 | ### Additional context 24 | 25 | 26 | -------------------------------------------------------------------------------- /.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["unicorn", "vue", "typescript", "oxc", "jsdoc"], 3 | "rules": { 4 | "no-console": ["warn", { "allow": ["info", "warn", "error"] }], 5 | "no-debugger": "warn", 6 | "prefer-const": "error", 7 | "padding-line-between-statements": [ 8 | "error", 9 | { "blankLine": "always", "prev": "*", "next": "return" } 10 | ], 11 | "no-unused-vars": [ 12 | "error", 13 | { 14 | "argsIgnorePattern": "^_", 15 | "varsIgnorePattern": "^_" 16 | } 17 | ], 18 | "typescript/explicit-module-boundary-types": "error", 19 | "typescript/consistent-type-imports": "error", 20 | "typescript/no-explicit-any": "error", 21 | "typescript/ban-ts-comment": "error", 22 | "jsdoc/check-tag-names": [ 23 | "warn", 24 | { 25 | "definedTags": ["typeParam", "remarks", "defaultValue", "experimental"] 26 | } 27 | ], 28 | "vue/multi-word-component-names": "off", 29 | "vue/no-v-text-v-html-on-component": "off" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PrismicRichText/PrismicRichTextSerialize.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | permissions: 4 | contents: write 5 | issues: write 6 | pull-requests: write 7 | id-token: write 8 | 9 | on: 10 | push: 11 | branches: 12 | - master 13 | 14 | jobs: 15 | release-please: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: googleapis/release-please-action@v4 19 | id: release 20 | with: 21 | release-type: node 22 | - uses: actions/checkout@v6 23 | if: ${{ steps.release.outputs.release_created }} 24 | - uses: actions/setup-node@v6 25 | if: ${{ steps.release.outputs.release_created }} 26 | with: 27 | node-version: 24 28 | registry-url: "https://registry.npmjs.org" 29 | - run: npm ci 30 | if: ${{ steps.release.outputs.release_created }} 31 | - run: npm publish --provenance 32 | if: ${{ steps.release.outputs.release_created }} 33 | env: 34 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 35 | -------------------------------------------------------------------------------- /.github/workflows/prerelease-pr-cleanup.yml: -------------------------------------------------------------------------------- 1 | name: prerelease-pr-cleanup 2 | 3 | permissions: 4 | contents: read 5 | id-token: write 6 | 7 | on: 8 | pull_request: 9 | types: [closed] 10 | 11 | jobs: 12 | cleanup: 13 | if: github.repository_owner == 'prismicio' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | - uses: actions/setup-node@v6 18 | with: 19 | node-version: 24 20 | registry-url: "https://registry.npmjs.org" 21 | - name: "Deprecate PR prerelease" 22 | run: | 23 | PACKAGE_NAME=$(jq -r ".name" package.json) 24 | TAG="pr-${{ github.event.number }}" 25 | VERSION=$(npm view "$PACKAGE_NAME" dist-tags."$TAG" 2>/dev/null || echo "") 26 | if [ -n "$VERSION" ]; then 27 | npm deprecate "$PACKAGE_NAME@$VERSION" "PR ${{ github.event.number }} was closed" 28 | npm dist-tag rm "$PACKAGE_NAME" "$TAG" 29 | fi 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚨 Bug report 3 | about: Report a bug report to help improve the package. 4 | title: "" 5 | labels: "bug" 6 | assignees: "" 7 | --- 8 | 9 | 16 | 17 | ### Versions 18 | 19 | - @prismicio/vue: 20 | - vue: 21 | - node: 22 | 23 | ### Reproduction 24 | 25 | 26 | 27 |
28 | Additional Details 29 |
30 | 31 |
32 | 33 | ### Steps to reproduce 34 | 35 | ### What is expected? 36 | 37 | ### What is actually happening? 38 | -------------------------------------------------------------------------------- /messages/prismictext-works-only-with-rich-text-and-title-fields.md: -------------------------------------------------------------------------------- 1 | # `` works only with Rich Text and title fields 2 | 3 | `` works only with [Rich Text and title fields][rich-text-title-field]. It renders the field's value as plain text (i.e. with no formatting, paragraphs, or headings). 4 | 5 | ```html 6 | 7 | 8 | ``` 9 | 10 | Other text-based field types, such as [Key Text][key-text-field] and [Select][select-field], cannot be rendered using ``. 11 | 12 | Since Key Text and Select field values are already plain text, you can render them inline without a special component. 13 | 14 | ```html 15 | 16 | {doc.data.keyTextField} 17 | 18 | 19 | {doc.data.selectField} 20 | ``` 21 | 22 | [rich-text-title-field]: https://prismic.io/docs/rich-text 23 | [key-text-field]: https://prismic.io/docs/field#key-text 24 | [select-field]: https://prismic.io/docs/field#select 25 | -------------------------------------------------------------------------------- /test/SliceZone.prod.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it, vi } from "vitest" 2 | 3 | import { mount } from "@vue/test-utils" 4 | 5 | import { createWrapperComponent } from "./__fixtures__/WrapperComponent" 6 | 7 | import type { SliceComponentType } from "../src" 8 | import { 9 | SliceZone, 10 | defineSliceZoneComponents, 11 | getSliceComponentProps, 12 | } from "../src" 13 | 14 | vi.mock("esm-env", async () => ({ DEV: false })) 15 | 16 | it("doesn't render TODO component in production", () => { 17 | const consoleWarnSpy = vi 18 | .spyOn(console, "warn") 19 | .mockImplementation(() => void 0) 20 | 21 | const Foo = createWrapperComponent( 22 | "Foo", 23 | getSliceComponentProps(), 24 | ) 25 | 26 | const wrapper = mount(SliceZone, { 27 | props: { 28 | slices: [ 29 | { id: "1", slice_type: "foo" }, 30 | { id: "2", slice_type: "bar" }, 31 | ], 32 | components: defineSliceZoneComponents({ 33 | foo: Foo, 34 | }), 35 | }, 36 | }) 37 | 38 | expect(wrapper.html()).toBe(`
`) 39 | expect(consoleWarnSpy).not.toHaveBeenCalledWith( 40 | expect.stringMatching(/could not find a component/i), 41 | { id: "2", slice_type: "bar" }, 42 | ) 43 | 44 | consoleWarnSpy.mockRestore() 45 | }) 46 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-jsdoc"], 3 | "jsdocCapitalizeDescription": false, 4 | "jsdocSeparateReturnsFromParam": true, 5 | "jsdocSeparateTagGroups": true, 6 | "jsdocCommentLineStrategy": "singleLine", 7 | "tsdoc": true, 8 | "importOrder": [ 9 | "^vitest(/|$)", 10 | "^node:", 11 | "", 12 | "^(?!.*/src(/|$)).*/__testutils__(/|$)", 13 | "^(?!.*/src(/|$)).*/__fixtures__(/|$)", 14 | "^(?!.*/src(/|$)).*/lib(/|$)", 15 | "^(?!.*/src(/|$)).*/types(/|$)", 16 | "^(?!.*/src(/|$)).*/errors(/|$)", 17 | "^(?!.*/src(/|$))../../../../../", 18 | "^(?!.*/src(/|$))../../../../", 19 | "^(?!.*/src(/|$))../../../", 20 | "^(?!.*/src(/|$))../../", 21 | "^(?!.*/src(/|$))../", 22 | "^(?!.*/src(/|$))./(.*/)+", 23 | "^(?!.*/src(/|$))./", 24 | "/src(/|$)" 25 | ], 26 | "importOrderSeparation": true, 27 | "importOrderSortSpecifiers": true, 28 | "importOrderGroupNamespaceSpecifiers": true, 29 | "printWidth": 80, 30 | "useTabs": true, 31 | "semi": false, 32 | "singleQuote": false, 33 | "quoteProps": "as-needed", 34 | "jsxSingleQuote": false, 35 | "trailingComma": "all", 36 | "bracketSpacing": true, 37 | "bracketSameLine": false, 38 | "arrowParens": "always", 39 | "requirePragma": false, 40 | "insertPragma": false, 41 | "htmlWhitespaceSensitivity": "css", 42 | "endOfLine": "lf" 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/prerelease-canary.yml: -------------------------------------------------------------------------------- 1 | name: prerelease-canary 2 | 3 | permissions: 4 | contents: read 5 | id-token: write 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | publish: 14 | if: github.repository_owner == 'prismicio' 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v6 18 | - uses: actions/setup-node@v6 19 | with: 20 | node-version: 24 21 | registry-url: "https://registry.npmjs.org" 22 | - name: "Deprecate previous canary" 23 | run: | 24 | PACKAGE_NAME=$(jq -r ".name" package.json) 25 | PREVIOUS_VERSION=$(npm view "$PACKAGE_NAME" dist-tags.canary 2>/dev/null) 26 | if [ -n "$PREVIOUS_VERSION" ]; then 27 | npm deprecate "$PACKAGE_NAME@$PREVIOUS_VERSION" "Replaced by newer canary version" 28 | fi 29 | env: 30 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 31 | - run: npm ci 32 | - name: "Generate new version" 33 | run: | 34 | SHORT_SHA=$(git rev-parse --short HEAD) 35 | CURRENT_VERSION=$(jq -r '.version' package.json) 36 | VERSION="${CURRENT_VERSION}-canary.${SHORT_SHA}" 37 | npm version $VERSION --no-git-tag-version 38 | - run: npm publish --provenance --tag canary 39 | env: 40 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 41 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as PrismicRichText } from "./PrismicRichText/PrismicRichText.vue" 2 | export type { PrismicRichTextProps } from "./PrismicRichText/PrismicRichText.vue" 3 | export { getRichTextComponentProps } from "./PrismicRichText" 4 | export type { 5 | VueRichTextSerializer, 6 | RichTextComponentProps, 7 | } from "./PrismicRichText" 8 | 9 | export { default as PrismicTable } from "./PrismicTable/PrismicTable.vue" 10 | export type { PrismicTableProps } from "./PrismicTable/PrismicTable.vue" 11 | export { getTableComponentProps } from "./PrismicTable" 12 | 13 | export { default as PrismicImage } from "./PrismicImage.vue" 14 | export type { PrismicImageProps } from "./PrismicImage.vue" 15 | 16 | export { default as PrismicLink } from "./PrismicLink.vue" 17 | export type { PrismicLinkProps } from "./PrismicLink.vue" 18 | 19 | export { default as PrismicText } from "./PrismicText.vue" 20 | export type { PrismicTextProps } from "./PrismicText.vue" 21 | 22 | export { default as SliceZone } from "./SliceZone/SliceZone.vue" 23 | export type { SliceZoneProps } from "./SliceZone/SliceZone.vue" 24 | export { 25 | TODOSliceComponent, 26 | defineSliceZoneComponents, 27 | getSliceComponentProps, 28 | } from "./SliceZone" 29 | export type { 30 | SliceComponentProps, 31 | SliceComponentType, 32 | SliceLike, 33 | SliceLikeGraphQL, 34 | SliceLikeRestV2, 35 | SliceZoneComponents, 36 | SliceZoneLike, 37 | } from "./SliceZone" 38 | -------------------------------------------------------------------------------- /.github/workflows/prerelease-pr.yml: -------------------------------------------------------------------------------- 1 | name: prerelease-pr 2 | 3 | permissions: 4 | contents: read 5 | id-token: write 6 | 7 | on: 8 | pull_request: 9 | 10 | jobs: 11 | publish: 12 | if: github.repository_owner == 'prismicio' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | - uses: actions/setup-node@v6 17 | with: 18 | node-version: 24 19 | registry-url: "https://registry.npmjs.org" 20 | - name: "Deprecate previous PR prerelease" 21 | run: | 22 | PACKAGE_NAME=$(jq -r ".name" package.json) 23 | TAG="pr-${{ github.event.number }}" 24 | PREVIOUS_VERSION=$(npm view "$PACKAGE_NAME" dist-tags."$TAG" 2>/dev/null) 25 | if [ -n "$PREVIOUS_VERSION" ]; then 26 | npm deprecate "$PACKAGE_NAME@$PREVIOUS_VERSION" "Replaced by newer prerelease version" 27 | fi 28 | env: 29 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 30 | - run: npm ci 31 | - name: "Generate new version" 32 | run: | 33 | SHORT_SHA=$(git rev-parse --short HEAD) 34 | CURRENT_VERSION=$(jq -r '.version' package.json) 35 | VERSION="${CURRENT_VERSION}-pr.${{ github.event.number }}.${SHORT_SHA}" 36 | npm version $VERSION --no-git-tag-version 37 | - run: npm publish --provenance --tag pr-${{ github.event.number }} 38 | env: 39 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 40 | -------------------------------------------------------------------------------- /src/PrismicTable/PrismicTableRow.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 45 | -------------------------------------------------------------------------------- /src/PrismicText.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /src/SliceZone/TODOSliceComponent.ts: -------------------------------------------------------------------------------- 1 | import { DEV } from "esm-env" 2 | import { computed, defineComponent, h, watchEffect } from "vue" 3 | import type { PropType } from "vue" 4 | 5 | import type { SliceComponentType, SliceLike } from "./types" 6 | 7 | /** 8 | * This Slice component can be used as a reminder to provide a proper 9 | * implementation. 10 | * 11 | * This is also the default Vue component rendered when a component mapping 12 | * cannot be found in ``. 13 | */ 14 | // oxlint-disable-next-line explicit-module-boundary-types 15 | export const TODOSliceComponent = DEV 16 | ? /*#__PURE__*/ (defineComponent({ 17 | name: "TODOSliceComponent", 18 | props: { 19 | slice: { 20 | type: Object as PropType, 21 | required: true, 22 | }, 23 | }, 24 | setup(props) { 25 | const type = computed(() => { 26 | return "slice_type" in props.slice 27 | ? props.slice.slice_type 28 | : props.slice.type 29 | }) 30 | 31 | watchEffect(() => { 32 | console.warn( 33 | `[SliceZone] Could not find a component for Slice type "${type.value}"`, 34 | props.slice, 35 | ) 36 | }) 37 | 38 | return () => { 39 | return h( 40 | "section", 41 | { 42 | "data-slice-zone-todo-component": "", 43 | "data-slice-type": type.value, 44 | }, 45 | [`Could not find a component for Slice type "${type.value}"`], 46 | ) 47 | } 48 | }, 49 | }) as SliceComponentType) 50 | : ((() => null) as SliceComponentType) 51 | -------------------------------------------------------------------------------- /src/PrismicTable/PrismicTableDefaultComponents.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, h } from "vue" 2 | 3 | import type { VueTableComponents } from "./types" 4 | 5 | import * as getTableComponentProps from "./getTableComponentProps" 6 | 7 | export const defaultTableComponents: Required = { 8 | table: defineComponent({ 9 | props: getTableComponentProps.table(), 10 | setup(props, { attrs, slots }) { 11 | return () => h("table", attrs, slots.default ? slots.default() : []) 12 | }, 13 | }), 14 | thead: defineComponent({ 15 | props: getTableComponentProps.thead(), 16 | setup(props, { attrs, slots }) { 17 | return () => h("thead", attrs, slots.default ? slots.default() : []) 18 | }, 19 | }), 20 | tbody: defineComponent({ 21 | props: getTableComponentProps.tbody(), 22 | setup(props, { attrs, slots }) { 23 | return () => h("tbody", attrs, slots.default ? slots.default() : []) 24 | }, 25 | }), 26 | tr: defineComponent({ 27 | props: getTableComponentProps.tr(), 28 | setup(props, { attrs, slots }) { 29 | return () => h("tr", attrs, slots.default ? slots.default() : []) 30 | }, 31 | }), 32 | th: defineComponent({ 33 | props: getTableComponentProps.th(), 34 | setup(props, { attrs, slots }) { 35 | return () => h("th", attrs, slots.default ? slots.default() : []) 36 | }, 37 | }), 38 | td: defineComponent({ 39 | props: getTableComponentProps.td(), 40 | setup(props, { attrs, slots }) { 41 | return () => h("td", attrs, slots.default ? slots.default() : []) 42 | }, 43 | }), 44 | } 45 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ConcreteComponent, 3 | DefineComponent, 4 | FunctionalComponent, 5 | Raw, 6 | defineAsyncComponent, 7 | } from "vue" 8 | 9 | export type ComponentOrTagName = 10 | | string 11 | | ConcreteComponent 12 | | Raw 13 | 14 | export type VueComponent = 15 | // For reference within TypeScript files when `*.vue` type cannot be inferred 16 | // eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-explicit-any 17 | | DefineComponent<{}, {}, any> 18 | // Likewise, for reference with TypeScript files. 19 | | ReturnType 20 | | DefineComponent 21 | | FunctionalComponent 22 | 23 | /** 24 | * A shorthand definition for `` and `` 25 | * component types. 26 | */ 27 | export type VueComponentShorthand = { 28 | /** The HTML element type rendered for this node type. */ 29 | as?: string 30 | 31 | /** Other attributes to apply to the element type. */ 32 | [Attribute: string]: string | boolean | null | undefined 33 | } 34 | 35 | export const isVueComponent = ( 36 | component: VueComponent | VueComponentShorthand | undefined, 37 | ): component is VueComponent => { 38 | return ( 39 | !!component && 40 | (typeof component === "function" || 41 | (typeof component === "object" && 42 | (("render" in component && typeof component.render === "function") || 43 | ("setup" in component && typeof component.setup === "function") || 44 | ("__file" in component && !!component.__file) || 45 | ("__name" in component && !!component.__name) || 46 | ("props" in component && typeof component.props === "object")))) 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: validate 2 | 3 | on: push 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v6 10 | - uses: actions/setup-node@v6 11 | with: 12 | node-version: 24 13 | cache: npm 14 | - run: npm ci 15 | - run: npm run lint 16 | 17 | build: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v6 21 | - uses: actions/setup-node@v6 22 | with: 23 | node-version: 24 24 | cache: npm 25 | - run: npm ci 26 | - run: npm run build 27 | 28 | test: 29 | name: test (Node.js ${{ matrix.node }}) 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | node: [20, 22, 24] 34 | fail-fast: false 35 | steps: 36 | - uses: actions/checkout@v6 37 | - uses: actions/setup-node@v6 38 | with: 39 | node-version: ${{ matrix.node }} 40 | cache: npm 41 | - run: npm ci 42 | - run: npm run unit 43 | env: 44 | E2E_PRISMIC_EMAIL: ${{ secrets.E2E_PRISMIC_EMAIL }} 45 | E2E_PRISMIC_PASSWORD: ${{ secrets.E2E_PRISMIC_PASSWORD }} 46 | 47 | types: 48 | name: "types (TypeScript ${{ matrix.typescript }})" 49 | runs-on: ubuntu-latest 50 | strategy: 51 | matrix: 52 | typescript: ["5.6", "5.7", "5.8", "5.9"] 53 | fail-fast: false 54 | steps: 55 | - uses: actions/checkout@v6 56 | - uses: actions/setup-node@v6 57 | with: 58 | node-version: 24 59 | cache: npm 60 | - run: npm ci 61 | - run: npm install --no-save typescript@${{ matrix.typescript }} && npx tsc --version 62 | - run: npm run types 63 | -------------------------------------------------------------------------------- /messages/alt-must-be-an-empty-string.md: -------------------------------------------------------------------------------- 1 | # `alt` must be an empty string 2 | 3 | `` allows the `alt` HTML attribute to be configured using two props: `alt` and `fallbackAlt`. 4 | 5 | Both `alt` and `fallbackAlt` can only be used to [declare an image as decorative][mdn-alt-decorative-image] by pasing an empty string. You may not pass arbitrary alternative text to the `alt` prop. 6 | 7 | ```html 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ``` 17 | 18 | All images should have an alt value. `` will automatically use the image field's `alt` property written in the Prismic Editor. If the image field's `alt` property is empty, the `alt` HTML attribute will not be included _unless_ one of `alt` or `fallbackAlt` is used. 19 | 20 | For more details on decorative images, [see the MDN article on the `` HTML element's `alt` attribute][mdn-alt-decorative-image]. 21 | 22 | ## Deciding between `alt=""` and `fallbackAlt=""` 23 | 24 | `alt=""` will always mark the image as decorative, ignoring the provied image field's `alt` property. Use this when the image is always used for decorative or presentational purposes. 25 | 26 | `fallbackAlt=""` will only mark the image as decorative if the provided image field's `alt` property is empty. Use this when you want to mark the image as decorative _unless_ alternative text is provided in the Prismic Editor. **Generally speaking, this is discouraged**; prefer marking the image as decorative intentionally. 27 | 28 | [mdn-alt-decorative-image]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt#decorative_images 29 | -------------------------------------------------------------------------------- /src/PrismicTable/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | TableField, 3 | TableFieldBody, 4 | TableFieldBodyRow, 5 | TableFieldDataCell, 6 | TableFieldHead, 7 | TableFieldHeadRow, 8 | TableFieldHeaderCell, 9 | } from "@prismicio/client" 10 | 11 | import type { VueComponent, VueComponentShorthand } from "../types" 12 | 13 | /** 14 | * A map of Table block types to Vue Components. It is used to render table 15 | * fields. 16 | * 17 | * @see Templating Table fields from Prismic {@link https://prismic.io/docs/table} 18 | */ 19 | export type VueTableComponents = { 20 | table?: 21 | | VueComponent<{ table: TableField<"filled"> }> 22 | | Omit 23 | thead?: 24 | | VueComponent<{ head: TableFieldHead }> 25 | | Omit 26 | tbody?: 27 | | VueComponent<{ body: TableFieldBody }> 28 | | Omit 29 | tr?: 30 | | VueComponent<{ row: TableFieldBodyRow | TableFieldHeadRow }> 31 | | Omit 32 | th?: 33 | | VueComponent<{ cell: TableFieldHeaderCell }> 34 | | Omit 35 | td?: 36 | | VueComponent<{ cell: TableFieldDataCell }> 37 | | Omit 38 | } 39 | 40 | export type InternalVueTableComponents = { 41 | table: { 42 | is: VueTableComponents["table"] 43 | shorthand?: Omit 44 | } 45 | thead: { 46 | is: VueTableComponents["thead"] 47 | shorthand?: Omit 48 | } 49 | tbody: { 50 | is: VueTableComponents["tbody"] 51 | shorthand?: Omit 52 | } 53 | tr: { 54 | is: VueTableComponents["tr"] 55 | shorthand?: Omit 56 | } 57 | th: { 58 | is: VueTableComponents["th"] 59 | shorthand?: Omit 60 | } 61 | td: { 62 | is: VueTableComponents["td"] 63 | shorthand?: Omit 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /messages/html-serialization-is-deprecated-with-prismic-rich-text.md: -------------------------------------------------------------------------------- 1 | # HTML serialization is deprecated with `` 2 | 3 | As of [`@prismicio/vue`][prismic-vue] v5, `` introduces a new components-based serializer, replacing the previous HTML-based serialization method. This change improves flexibility and security by eliminating the need for the `v-html` directive. 4 | 5 | The HTML-based serializer is still accessible to ease your upgrade path to `@prismicio/vue` v5 but **will be removed in a future major version**. We encourage you to migrate to the new components-based serializer to ensure future compatibility. 6 | 7 | ## Migration example 8 | 9 | Here’s how to transition from the old HTML-based serializer to the new components-based serializer: 10 | 11 | ### Deprecated method (HTML-based Serializer) 12 | 13 | ```html 14 | 20 | ``` 21 | 22 | ### New method (Components-based Serializer) 23 | 24 | ```html 25 | 32 | ``` 33 | 34 | The above snippet relies on an `Heading2.vue` component. Here's an example of this component: 35 | 36 | ```html 37 | 42 | 43 | 46 | ``` 47 | 48 | Learn more about the new component serializer on the [`@prismicio/vue` technical reference][prismic-vue-rich-text]. 49 | 50 | [prismic-vue]: https://prismic.io/docs/technical-reference/prismicio-vue 51 | [prismic-vue-rich-text]: https://prismic.io/docs/technical-reference/prismicio-vue#prismicrichtext 52 | -------------------------------------------------------------------------------- /src/PrismicRichText/getRichTextComponentProps.ts: -------------------------------------------------------------------------------- 1 | import type { RTAnyNode } from "@prismicio/client" 2 | import type { PropType } from "vue" 3 | 4 | import type { RichTextComponentProps } from "./types" 5 | 6 | /** 7 | * Native Vue props for a component rendering nodes from a Prismic rich text 8 | * field using the `` component. 9 | * 10 | * @typeParam TRTNode - The type of rich text node(s) this component handles 11 | */ 12 | type DefineComponentRichTextComponentProps< 13 | TRTNode extends RTAnyNode = RTAnyNode, 14 | > = { 15 | node: { 16 | type: PropType["node"]> 17 | required: true 18 | } 19 | } 20 | 21 | /** 22 | * Gets native Vue props for a component rendering nodes from a Prismic rich 23 | * text field using the `` component. 24 | * 25 | * Props are: `["node"]` 26 | * 27 | * @example 28 | * 29 | * ```javascript 30 | * // Defining a new rich text component 31 | * import { getRichTextComponentProps } from "@prismicio/vue" 32 | * 33 | * defineProps(getRichTextComponentProps()) 34 | * ``` 35 | * 36 | * @example 37 | * 38 | * ```typescript 39 | * // Defining a new rich text component for a specific node type 40 | * import { getRichTextComponentProps } from "@prismicio/vue" 41 | * 42 | * defineProps(getRichTextComponentProps("heading1")) 43 | * ``` 44 | * 45 | * @typeParam TRTType - The type of rich text node(s) this component handles 46 | * 47 | * @param type - The type of rich text node this component handles 48 | * 49 | * @returns Props object to use with `defineProps()` 50 | */ 51 | export const getRichTextComponentProps = ( 52 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 53 | type?: TRTType, 54 | ): DefineComponentRichTextComponentProps< 55 | Extract 56 | > => ({ 57 | node: { 58 | type: Object as PropType< 59 | RichTextComponentProps>["node"] 60 | >, 61 | required: true, 62 | }, 63 | }) 64 | -------------------------------------------------------------------------------- /src/SliceZone/defineSliceZoneComponents.ts: -------------------------------------------------------------------------------- 1 | import { markRaw } from "vue" 2 | 3 | import type { 4 | SliceComponentType, 5 | SliceLike, 6 | SliceZoneComponents, 7 | } from "./types" 8 | 9 | /** 10 | * Gets an optimized record of Slice types mapped to Vue components. Each 11 | * components will be rendered for each instance of their Slice type. 12 | * 13 | * @remarks 14 | * This is essentially an helper function to ensure {@link markRaw} is correctly 15 | * applied on each components, improving performances. 16 | * 17 | * @example 18 | * 19 | * ```javascript 20 | * // Defining a slice components 21 | * import { defineSliceZoneComponents } from "@prismicio/vue"; 22 | * 23 | * export default { 24 | * data() { 25 | * components: defineSliceZoneComponents({ 26 | * foo: Foo, 27 | * bar: defineAsyncComponent( 28 | * () => new Promise((res) => res(Bar)), 29 | * ), 30 | * baz: "Baz", 31 | * }), 32 | * } 33 | * }; 34 | * ``` 35 | * 36 | * @typeParam TSlice - The type(s) of slices in the Slice Zone 37 | * @typeParam TContext - Arbitrary data made available to all Slice components 38 | * 39 | * @param components - {@link SliceZoneComponents} 40 | * 41 | * @returns A new optimized record of {@link SliceZoneComponents} 42 | */ 43 | export const defineSliceZoneComponents = < 44 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 45 | TSlice extends SliceLike = any, 46 | TContext = unknown, 47 | >( 48 | components: SliceZoneComponents, 49 | ): SliceZoneComponents => { 50 | const result = {} as SliceZoneComponents 51 | 52 | let type: keyof typeof components 53 | for (type in components) { 54 | const component = components[type] 55 | result[type] = 56 | typeof component === "string" 57 | ? component 58 | : markRaw( 59 | component as SliceComponentType< 60 | Extract>, 61 | TContext 62 | >, 63 | ) 64 | } 65 | 66 | return result 67 | } 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@prismicio/vue", 3 | "version": "5.3.0", 4 | "description": "Vue plugin, components, and composables to fetch and present Prismic content", 5 | "keywords": [ 6 | "typescript", 7 | "prismic", 8 | "vue", 9 | "plugin" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "ssh://git@github.com/prismicio/prismic-vue.git" 14 | }, 15 | "license": "Apache-2.0", 16 | "author": "Prismic (https://prismic.io)", 17 | "sideEffects": false, 18 | "type": "module", 19 | "exports": { 20 | ".": { 21 | "require": "./dist/index.cjs", 22 | "import": "./dist/index.js" 23 | }, 24 | "./package.json": "./package.json" 25 | }, 26 | "main": "./dist/index.cjs", 27 | "module": "./dist/index.js", 28 | "types": "./dist/index.d.cts", 29 | "files": [ 30 | "./dist", 31 | "./src" 32 | ], 33 | "scripts": { 34 | "build": "tsdown", 35 | "dev": "tsdown --watch", 36 | "format": "prettier --write .", 37 | "prepare": "npm run build", 38 | "lint": "oxlint --deny-warnings", 39 | "types": "vue-tsc --noEmit", 40 | "unit": "vitest run --coverage", 41 | "unit:watch": "vitest watch", 42 | "test": "npm run lint && npm run types && npm run unit && npm run build" 43 | }, 44 | "dependencies": { 45 | "esm-env": "^1.2.2" 46 | }, 47 | "devDependencies": { 48 | "@prismicio/client": "^7.21.0", 49 | "@prismicio/mock": "^0.7.1", 50 | "@trivago/prettier-plugin-sort-imports": "^6.0.0", 51 | "@types/jsdom-global": "^3.0.7", 52 | "@vitejs/plugin-vue": "^6.0.2", 53 | "@vitest/coverage-v8": "^4.0.15", 54 | "@vue/test-utils": "^2.4.6", 55 | "jsdom": "^27.3.0", 56 | "jsdom-global": "^3.0.2", 57 | "oxlint": "1.32.0", 58 | "prettier": "^3.7.4", 59 | "prettier-plugin-jsdoc": "^1.8.0", 60 | "tsdown": "0.17.2", 61 | "typescript": "^5.9.3", 62 | "vite": "^7.2.7", 63 | "vitest": "^4.0.15", 64 | "vue": "^3.5.25", 65 | "vue-router": "^4.6.3", 66 | "vue-tsc": "^3.1.8" 67 | }, 68 | "peerDependencies": { 69 | "@prismicio/client": "^7", 70 | "vue": "^3" 71 | }, 72 | "engines": { 73 | "node": ">=20" 74 | }, 75 | "publishConfig": { 76 | "access": "public" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/SliceZone/SliceZone.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 85 | -------------------------------------------------------------------------------- /test/PrismicText.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest" 2 | 3 | import { type RichTextField, RichTextNodeType } from "@prismicio/client" 4 | import { mount } from "@vue/test-utils" 5 | import { markRaw } from "vue" 6 | 7 | import { WrapperComponent } from "./__fixtures__/WrapperComponent" 8 | 9 | import { PrismicText } from "../src" 10 | 11 | describe("renders a rich text field", () => { 12 | it("as plain text", () => { 13 | const wrapper = mount(PrismicText, { 14 | props: { 15 | field: [ 16 | { 17 | type: RichTextNodeType.heading1, 18 | text: "Heading 1", 19 | spans: [], 20 | }, 21 | ], 22 | }, 23 | }) 24 | 25 | expect(wrapper.html()).toBe("Heading 1") 26 | }) 27 | 28 | it("as nothing when passed an empty field", () => { 29 | const wrapper = mount(PrismicText, { 30 | props: { field: [] }, 31 | }) 32 | 33 | expect(wrapper.html()).toBe("") 34 | }) 35 | }) 36 | 37 | describe("renders fallback value", () => { 38 | it("when passed an empty field", () => { 39 | const nullField = mount(PrismicText, { 40 | props: { field: null, fallback: "fallback" }, 41 | }) 42 | 43 | const undefinedField = mount(PrismicText, { 44 | props: { field: undefined, fallback: "fallback" }, 45 | }) 46 | 47 | const emptyField = mount(PrismicText, { 48 | props: { field: [], fallback: "fallback" }, 49 | }) 50 | 51 | expect(nullField.html()).toBe("fallback") 52 | expect(undefinedField.html()).toBe("fallback") 53 | expect(emptyField.html()).toBe("fallback") 54 | }) 55 | }) 56 | 57 | it("throws error if passed a string-based field (e.g. Key Text or Select)", () => { 58 | expect(() => 59 | mount(PrismicText, { 60 | props: { 61 | // @ts-expect-error - We are purposely not providing a correct field. 62 | field: "not a rich text field", 63 | wrapper: markRaw(WrapperComponent), 64 | }, 65 | }), 66 | ).toThrowError(/prismictext-works-only-with-rich-text-and-title-fields/i) 67 | }) 68 | 69 | it("reacts to changes properly", async () => { 70 | const wrapper = mount(PrismicText, { 71 | props: { 72 | field: [ 73 | { 74 | type: RichTextNodeType.heading1, 75 | text: "Heading 1", 76 | spans: [], 77 | }, 78 | ], 79 | }, 80 | }) 81 | 82 | const firstRender = wrapper.html() 83 | 84 | await wrapper.setProps({ 85 | field: [ 86 | { type: RichTextNodeType.paragraph, text: "foo", spans: [] }, 87 | ] as RichTextField, 88 | }) 89 | 90 | const secondRender = wrapper.html() 91 | 92 | expect(secondRender).not.toBe(firstRender) 93 | expect(secondRender).toBe("foo") 94 | }) 95 | -------------------------------------------------------------------------------- /src/PrismicRichText/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | LinkResolverFunction, 3 | RTAnyNode, 4 | RTEmNode, 5 | RTEmbedNode, 6 | RTHeading1Node, 7 | RTHeading2Node, 8 | RTHeading3Node, 9 | RTHeading4Node, 10 | RTHeading5Node, 11 | RTHeading6Node, 12 | RTImageNode, 13 | RTLabelNode, 14 | RTLinkNode, 15 | RTListItemNode, 16 | RTListNode, 17 | RTOListItemNode, 18 | RTOListNode, 19 | RTParagraphNode, 20 | RTPreformattedNode, 21 | RTSpanNode, 22 | RTStrongNode, 23 | RichTextNodeTypes, 24 | } from "@prismicio/client" 25 | 26 | import type { VueComponent, VueComponentShorthand } from "../types" 27 | 28 | /** 29 | * A map of Rich Text block types to Vue Components. It is used to render Rich 30 | * Text or title fields. 31 | * 32 | * @see Templating Rich Text and title fields from Prismic {@link https://prismic.io/docs/rich-text} 33 | */ 34 | export type VueRichTextSerializer = { 35 | heading1?: RichTextComponentProps | VueComponentShorthand 36 | heading2?: RichTextComponentProps | VueComponentShorthand 37 | heading3?: RichTextComponentProps | VueComponentShorthand 38 | heading4?: RichTextComponentProps | VueComponentShorthand 39 | heading5?: RichTextComponentProps | VueComponentShorthand 40 | heading6?: RichTextComponentProps | VueComponentShorthand 41 | paragraph?: RichTextComponentProps | VueComponentShorthand 42 | preformatted?: 43 | | RichTextComponentProps 44 | | VueComponentShorthand 45 | strong?: RichTextComponentProps | VueComponentShorthand 46 | em?: RichTextComponentProps | VueComponentShorthand 47 | listItem?: RichTextComponentProps | VueComponentShorthand 48 | oListItem?: RichTextComponentProps | VueComponentShorthand 49 | list?: RichTextComponentProps | VueComponentShorthand 50 | oList?: RichTextComponentProps | VueComponentShorthand 51 | image?: 52 | | RichTextComponentProps 53 | | Omit 54 | embed?: RichTextComponentProps | VueComponentShorthand 55 | hyperlink?: 56 | | RichTextComponentProps 57 | | Omit 58 | label?: 59 | | RichTextComponentProps 60 | | Omit 61 | span?: RichTextComponentProps 62 | } 63 | 64 | /** Props for a component rendering nodes from a Prismic rich text field. */ 65 | export type RichTextComponentProps = { 66 | node: TRTNode 67 | } 68 | 69 | /** 70 | * A Vue component rendering a node from a Prismic rich text field. 71 | * 72 | * @typeParam TRTNode - The type of rich text node(s) this component handles 73 | */ 74 | export type VueRichTextComponent = 75 | VueComponent> 76 | 77 | export type InternalVueRichTextComponents = Record< 78 | RichTextNodeTypes, 79 | { 80 | is: VueRichTextComponent 81 | linkResolver?: LinkResolverFunction 82 | shorthand?: VueComponentShorthand 83 | } 84 | > 85 | -------------------------------------------------------------------------------- /src/SliceZone/getSliceComponentProps.ts: -------------------------------------------------------------------------------- 1 | import type { PropType } from "vue" 2 | 3 | import type { SliceComponentProps, SliceLike } from "./types" 4 | 5 | /** 6 | * Native Vue props for a component rendering content from a Prismic Slice using 7 | * the `` component. 8 | * 9 | * @typeParam TSlice - The Slice type 10 | * @typeParam TContext - Arbitrary data passed to `` and made 11 | * available to all Slice components 12 | */ 13 | type DefineComponentSliceComponentProps< 14 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 15 | TSlice extends SliceLike = any, 16 | TContext = unknown, 17 | > = { 18 | slice: { 19 | type: PropType["slice"]> 20 | required: true 21 | } 22 | index: { 23 | type: PropType["index"]> 24 | required: true 25 | } 26 | slices: { 27 | type: PropType["slices"]> 28 | required: true 29 | } 30 | context: { 31 | type: PropType["context"]> 32 | required: true 33 | } 34 | } 35 | 36 | /** 37 | * Gets native Vue props for a component rendering content from a Prismic Slice 38 | * using the `` component. 39 | * 40 | * Props are: `["slice", "index", "slices", "context"]` 41 | * 42 | * @example 43 | * 44 | * ```typescript 45 | * // Defining a new slice component 46 | * import { getSliceComponentProps } from "@prismicio/vue" 47 | * 48 | * defineProps(getSliceComponentProps()) 49 | * ``` 50 | * 51 | * @example 52 | * 53 | * ```typescript 54 | * // Defining a new slice component with visual hint 55 | * import { getSliceComponentProps } from "@prismicio/vue" 56 | * 57 | * defineProps( 58 | * getSliceComponentProps(["slice", "index", "slices", "context"]), 59 | * ) 60 | * ``` 61 | * 62 | * @typeParam TSlice - The Slice type 63 | * @typeParam TContext - Arbitrary data passed to `` and made 64 | * available to all Slice components 65 | * 66 | * @param propsHint - An optional array of prop names used for the sole purpose 67 | * of having a visual hint of which props are made available to the slice, 68 | * this parameters doesn't have any effect 69 | * 70 | * @returns Props object to use with `defineProps()` 71 | */ 72 | export const getSliceComponentProps = < 73 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 74 | TSlice extends SliceLike = any, 75 | TContext = unknown, 76 | >( 77 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 78 | propsHint?: ["slice", "index", "slices", "context"], 79 | ): DefineComponentSliceComponentProps => ({ 80 | slice: { 81 | type: Object as PropType["slice"]>, 82 | required: true, 83 | }, 84 | index: { 85 | type: Number as PropType["index"]>, 86 | required: true, 87 | }, 88 | slices: { 89 | type: Array as PropType["slices"]>, 90 | required: true, 91 | }, 92 | context: { 93 | type: null as unknown as PropType< 94 | SliceComponentProps["context"] 95 | >, 96 | required: true, 97 | }, 98 | }) 99 | -------------------------------------------------------------------------------- /src/PrismicTable/PrismicTable.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 107 | -------------------------------------------------------------------------------- /src/PrismicRichText/PrismicRichTextDefaultComponent.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 110 | -------------------------------------------------------------------------------- /src/PrismicLink.vue: -------------------------------------------------------------------------------- 1 | 110 | 111 | 118 | -------------------------------------------------------------------------------- /src/PrismicTable/getTableComponentProps.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | TableField, 3 | TableFieldBody, 4 | TableFieldBodyRow, 5 | TableFieldDataCell, 6 | TableFieldHead, 7 | TableFieldHeadRow, 8 | TableFieldHeaderCell, 9 | } from "@prismicio/client" 10 | import type { PropType } from "vue" 11 | 12 | /** 13 | * Gets native Vue props for a component rendering `table` elements from a 14 | * Prismic table field with ``. 15 | * 16 | * Props are: `["table"]` 17 | * 18 | * @example 19 | * 20 | * ```javascript 21 | * // Defining a new rich text component 22 | * import { getTableComponentProps } from "@prismicio/vue" 23 | * 24 | * defineProps(getTableComponentProps.table()) 25 | * ``` 26 | */ 27 | export const table = (): { 28 | table: { type: PropType>; required: true } 29 | } => ({ 30 | table: { type: Object as PropType>, required: true }, 31 | }) 32 | 33 | /** 34 | * Gets native Vue props for a component rendering `thead` elements from a 35 | * Prismic table field with ``. 36 | * 37 | * Props are: `["head"]` 38 | * 39 | * @example 40 | * 41 | * ```javascript 42 | * // Defining a new rich text component 43 | * import { getTableComponentProps } from "@prismicio/vue" 44 | * 45 | * defineProps(getTableComponentProps.thead()) 46 | * ``` 47 | */ 48 | export const thead = (): { 49 | head: { type: PropType; required: true } 50 | } => ({ 51 | head: { type: Object as PropType, required: true }, 52 | }) 53 | 54 | /** 55 | * Gets native Vue props for a component rendering `tbody` elements from a 56 | * Prismic table field with ``. 57 | * 58 | * Props are: `["body"]` 59 | * 60 | * @example 61 | * 62 | * ```javascript 63 | * // Defining a new rich text component 64 | * import { getTableComponentProps } from "@prismicio/vue" 65 | * 66 | * defineProps(getTableComponentProps.body()) 67 | * ``` 68 | */ 69 | export const tbody = (): { 70 | body: { type: PropType; required: true } 71 | } => ({ 72 | body: { type: Object as PropType, required: true }, 73 | }) 74 | 75 | /** 76 | * Gets native Vue props for a component rendering `tr` elements from a Prismic 77 | * table field with ``. 78 | * 79 | * Props are: `["row"]` 80 | * 81 | * @example 82 | * 83 | * ```javascript 84 | * // Defining a new rich text component 85 | * import { getTableComponentProps } from "@prismicio/vue" 86 | * 87 | * defineProps(getTableComponentProps.tr()) 88 | * ``` 89 | */ 90 | export const tr = (): { 91 | row: { 92 | type: PropType 93 | required: true 94 | } 95 | } => ({ 96 | row: { 97 | type: Object as PropType, 98 | required: true, 99 | }, 100 | }) 101 | 102 | /** 103 | * Gets native Vue props for a component rendering `th` elements from a Prismic 104 | * table field with ``. 105 | * 106 | * Props are: `["cell"]` 107 | * 108 | * @example 109 | * 110 | * ```javascript 111 | * // Defining a new rich text component 112 | * import { getTableComponentProps } from "@prismicio/vue" 113 | * 114 | * defineProps(getTableComponentProps.th()) 115 | * ``` 116 | */ 117 | export const th = (): { 118 | cell: { type: PropType; required: true } 119 | } => ({ 120 | cell: { type: Object as PropType, required: true }, 121 | }) 122 | 123 | /** 124 | * Gets native Vue props for a component rendering `td` elements from a Prismic 125 | * table field with ``. 126 | * 127 | * Props are: `["cell"]` 128 | * 129 | * @example 130 | * 131 | * ```javascript 132 | * // Defining a new rich text component 133 | * import { getTableComponentProps } from "@prismicio/vue" 134 | * 135 | * defineProps(getTableComponentProps.td()) 136 | * ``` 137 | */ 138 | export const td = (): { 139 | cell: { type: PropType; required: true } 140 | } => ({ 141 | cell: { type: Object as PropType, required: true }, 142 | }) 143 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This package is primarily maintained by [Prismic](https://prismic.io)[^1]. External contributions are welcome. Ask for help by [opening an issue](https://github.com/prismicio/prismic-vue/issues/new/choose), or request a review by opening a pull request. 4 | 5 | ## :gear: Setup 6 | 7 | 8 | 9 | The following setup is required to work on this project: 10 | 11 | - Node.js 24 or later 12 | - npm CLI 13 | 14 | ## :memo: Project-specific notes 15 | 16 | 17 | 18 | 19 | > [!TIP] 20 | > Please update this section with helpful notes for contributors. 21 | 22 | ## :construction_worker: Develop 23 | 24 | > [!NOTE] 25 | > It's highly recommended to discuss your changes with the Prismic team before starting by [opening an issue](https://github.com/prismicio/prismic-vue/issues/new/choose).[^2] 26 | > 27 | > A short discussion can accellerate your work and ship it faster. 28 | 29 | ```sh 30 | # Clone and prepare the project. 31 | git clone git@github.com:prismicio/prismic-vue.git 32 | cd prismic-vue 33 | npm install 34 | 35 | # Create a new branch for your changes (e.g. lh/fix-win32-paths). 36 | git checkout -b / 37 | 38 | # Start the development watcher. 39 | # Run this command while you are working on your changes. 40 | node --run dev 41 | 42 | # Build the project for production. 43 | # Run this command when you want to see the production version. 44 | node --run build 45 | 46 | # Lint your changes before requesting a review. No errors are allowed. 47 | node --run lint 48 | # Some errors can be fixed automatically: 49 | node --run lint -- --fix 50 | 51 | # Format your changes before requesting a review. No errors are allowed. 52 | node --run format 53 | 54 | # Test your changes before requesting a review. 55 | # All changes should be tested. No failing tests are allowed. 56 | node --run test 57 | # Run only unit tests (optionally in watch mode): 58 | node --run unit 59 | node --run unit:watch 60 | # Run only type tests 61 | node --run types 62 | ``` 63 | 64 | ## :building_construction: Submit a pull request 65 | 66 | > [!NOTE] 67 | > Code will be reviewed by the Prismic team before merging.[^3] 68 | > 69 | > Request a review by opening a pull request. 70 | 71 | ```sh 72 | # Open a pull request. This example uses the GitHub CLI. 73 | gh pr create 74 | 75 | # Someone from the Prismic team will review your work. This review will at 76 | # least consider the PR's general direction, code style, and test coverage. 77 | 78 | # Prereleases are published to npm automatically to upon pushing commits. 79 | # Install the prerelease using the `pr-${number}` tag. 80 | npm install @prismicio/vue@pr-101 81 | 82 | # When ready, PRs should be merged using the "Squash and merge" option. 83 | ``` 84 | 85 | ## :rocket: Publish 86 | 87 | > [!CAUTION] 88 | > Publishing is restricted to the Prismic team.[^4] 89 | 90 | This repository uses [Release Please](https://github.com/googleapis/release-please). To publish changes in `master`, merge [the pending Release Please PR](https://github.com/prismicio/prismic-vue/pulls?q=is%3Apr+is%3Aopen+label%3A%22autorelease%3A+pending%22). 91 | 92 | If you don't see a pending PR, there are no changes to publish from `master`. 93 | 94 | [^1]: This package is maintained by the DevX team. Prismic employees can ask for help or a review in the [#team-devx](https://prismic-team.slack.com/archives/C014VAACCQL) Slack channel. 95 | 96 | [^2]: Prismic employees are highly encouraged to discuss changes with the DevX team in the [#team-devx](https://prismic-team.slack.com/archives/C014VAACCQL) Slack channel before starting. 97 | 98 | [^3]: Code should be reviewed by the DevX team before merging. Prismic employees can request a review in the [#team-devx](https://prismic-team.slack.com/archives/CPG31MDL1) Slack channel. 99 | 100 | [^4]: Prismic employees can ask the DevX team for [npm](https://www.npmjs.com) publish access. 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | # @prismicio/vue 12 | 13 | [![npm version][npm-version-src]][npm-version-href] 14 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 15 | 16 | 17 | 18 | [Vue][vue] plugin, components, and composables to fetch and present [Prismic][prismic] content. 19 | 20 | - 📝  Display content from Prismic like [Rich Text][prismic-rich-text] and [Link][prismic-link] fields; 21 | - 🍡  Render Prismic [Slice Zones][prismic-slices] declaratively with Vue components; 22 | - 🎣  Fetch Prismic content using Vue [composition API][vue-composition] or [vue-options]. 23 | 24 | ## Install 25 | 26 | ```bash 27 | npm install @prismicio/vue 28 | ``` 29 | 30 | ## Documentation 31 | 32 | To discover what's new on this package check out [the changelog][changelog]. For full documentation, visit the [official Prismic documentation][prismic-docs]. 33 | 34 | ## Contributing 35 | 36 | Whether you're helping us fix bugs, improve the docs, or spread the word, we'd love to have you as part of the Prismic developer community! 37 | 38 | **Asking a question**: [Open a new topic][forum-question] on our community forum explaining what you want to achieve / your question. Our support team will get back to you shortly. 39 | 40 | **Reporting a bug**: [Open an issue][repo-bug-report] explaining your application's setup and the bug you're encountering. 41 | 42 | **Suggesting an improvement**: [Open an issue][repo-feature-request] explaining your improvement or feature so we can discuss and learn more. 43 | 44 | **Submitting code changes**: For small fixes, feel free to [open a pull request][repo-pull-requests] with a description of your changes. For large changes, please first [open an issue][repo-feature-request] so we can discuss if and how the changes should be implemented. 45 | 46 | For more clarity on this project and its structure you can also check out the detailed [CONTRIBUTING.md][contributing] document. 47 | 48 | ## License 49 | 50 | ``` 51 | Copyright 2013-2025 Prismic (https://prismic.io) 52 | 53 | Licensed under the Apache License, Version 2.0 (the "License"); 54 | you may not use this file except in compliance with the License. 55 | You may obtain a copy of the License at 56 | 57 | http://www.apache.org/licenses/LICENSE-2.0 58 | 59 | Unless required by applicable law or agreed to in writing, software 60 | distributed under the License is distributed on an "AS IS" BASIS, 61 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 62 | See the License for the specific language governing permissions and 63 | limitations under the License. 64 | ``` 65 | 66 | 67 | 68 | [prismic]: https://prismic.io 69 | 70 | 71 | 72 | [prismic-docs]: https://prismic.io/docs/technical-reference/prismicio-vue/v5 73 | [changelog]: ./CHANGELOG.md 74 | [contributing]: ./CONTRIBUTING.md 75 | [vue]: https://v3.vuejs.org 76 | [prismic-rich-text]: https://prismic.io/docs/core-concepts/rich-text-title 77 | [prismic-link]: https://prismic.io/docs/core-concepts/link-content-relationship 78 | [prismic-slices]: https://prismic.io/docs/core-concepts/slices 79 | [vue-composition]: https://v3.vuejs.org/guide/composition-api-introduction.html 80 | [vue-options]: https://v3.vuejs.org/guide/introduction.html 81 | 82 | 83 | 84 | [forum-question]: https://community.prismic.io 85 | [repo-bug-report]: https://github.com/prismicio/prismic-vue/issues/new?assignees=&labels=bug&template=bug_report.md&title= 86 | [repo-feature-request]: https://github.com/prismicio/prismic-vue/issues/new?assignees=&labels=enhancement&template=feature_request.md&title= 87 | [repo-pull-requests]: https://github.com/prismicio/prismic-vue/pulls 88 | 89 | 90 | 91 | [npm-version-src]: https://img.shields.io/npm/v/@prismicio/vue/latest.svg 92 | [npm-version-href]: https://npmjs.com/package/@prismicio/vue 93 | [npm-downloads-src]: https://img.shields.io/npm/dm/@prismicio/vue.svg 94 | [npm-downloads-href]: https://npmjs.com/package/@prismicio/vue 95 | -------------------------------------------------------------------------------- /test/PrismicRichText.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from "vitest" 2 | 3 | import type { RichTextField } from "@prismicio/client" 4 | import { RichTextNodeType } from "@prismicio/client" 5 | import { mount } from "@vue/test-utils" 6 | import { markRaw } from "vue" 7 | 8 | import { 9 | WrapperComponent, 10 | createWrapperComponent, 11 | } from "./__fixtures__/WrapperComponent" 12 | import { richTextFixture } from "./__fixtures__/richText" 13 | 14 | import { PrismicRichText } from "../src" 15 | import { getRichTextComponentProps } from "../src/PrismicRichText/getRichTextComponentProps" 16 | 17 | describe("renders a rich text field", () => { 18 | it("as components", () => { 19 | const wrapper = mount(PrismicRichText, { 20 | props: { 21 | field: [ 22 | { 23 | type: RichTextNodeType.heading1, 24 | text: "Heading 1", 25 | spans: [], 26 | }, 27 | ], 28 | }, 29 | }) 30 | 31 | expect(wrapper.html()).toBe(`

32 | Heading 1 33 |

`) 34 | }) 35 | 36 | it("as nothing when passed an empty field", () => { 37 | const wrapper = mount(PrismicRichText, { 38 | props: { field: [] }, 39 | }) 40 | 41 | expect(wrapper.html()).toBe("") 42 | }) 43 | }) 44 | 45 | describe("renders fallback value", () => { 46 | it("when passed an empty field", () => { 47 | const nullField = mount(PrismicRichText, { 48 | props: { field: null, fallback: markRaw(WrapperComponent) }, 49 | }) 50 | 51 | const undefinedField = mount(PrismicRichText, { 52 | props: { field: undefined, fallback: markRaw(WrapperComponent) }, 53 | }) 54 | 55 | const emptyField = mount(PrismicRichText, { 56 | props: { field: [], fallback: markRaw(WrapperComponent) }, 57 | }) 58 | 59 | expect(nullField.html()).toBe(`
`) 60 | expect(undefinedField.html()).toBe(`
`) 61 | expect(emptyField.html()).toBe(`
`) 62 | }) 63 | }) 64 | 65 | describe("uses link resolver", () => { 66 | it("from props", (ctx) => { 67 | const spiedLinkResolver = vi.fn(() => ctx.task.name) 68 | 69 | const wrapper = mount(PrismicRichText, { 70 | props: { field: richTextFixture.en, linkResolver: spiedLinkResolver }, 71 | }) 72 | 73 | expect(spiedLinkResolver).toHaveBeenCalled() 74 | expect(wrapper.html()).toMatch(`href="${ctx.task.name}"`) 75 | }) 76 | }) 77 | 78 | describe("uses components serializer", () => { 79 | it("from props", () => { 80 | const wrapper = mount(PrismicRichText, { 81 | props: { 82 | field: [ 83 | { 84 | type: RichTextNodeType.heading1, 85 | text: "Heading 1", 86 | spans: [], 87 | }, 88 | ], 89 | components: { 90 | heading1: markRaw( 91 | createWrapperComponent(undefined, getRichTextComponentProps()), 92 | ), 93 | }, 94 | }, 95 | }) 96 | 97 | expect(wrapper.html()).toBe(`
98 | Heading 1 99 |
`) 100 | }) 101 | }) 102 | 103 | describe("uses shorthand serializer", () => { 104 | it("from props", () => { 105 | const wrapper = mount(PrismicRichText, { 106 | props: { 107 | field: [ 108 | { 109 | type: RichTextNodeType.heading1, 110 | text: "Heading 1", 111 | spans: [], 112 | }, 113 | { 114 | type: RichTextNodeType.heading2, 115 | text: "Heading 2", 116 | spans: [], 117 | }, 118 | ], 119 | components: { 120 | heading1: { 121 | class: "heading-1", 122 | "data-testid": "heading-1", 123 | }, 124 | heading2: { 125 | as: "h3", 126 | class: "heading-2", 127 | "data-testid": "heading-2", 128 | }, 129 | }, 130 | }, 131 | }) 132 | 133 | expect(wrapper.html()).toBe(`

134 | Heading 1 135 |

136 |

137 | Heading 2 138 |

`) 139 | }) 140 | }) 141 | 142 | it("reacts to changes properly", async () => { 143 | const wrapper = mount(PrismicRichText, { 144 | props: { field: richTextFixture.en }, 145 | }) 146 | 147 | const firstRender = wrapper.html() 148 | 149 | await wrapper.setProps({ 150 | field: [{ type: "paragraph", text: "foo", spans: [] }] as RichTextField, 151 | }) 152 | 153 | const secondRender = wrapper.html() 154 | 155 | expect(secondRender).not.toBe(firstRender) 156 | expect(secondRender).toBe(`

157 | foo 158 |

`) 159 | }) 160 | -------------------------------------------------------------------------------- /src/PrismicRichText/PrismicRichText.vue: -------------------------------------------------------------------------------- 1 | 126 | 127 | 135 | -------------------------------------------------------------------------------- /src/SliceZone/types.ts: -------------------------------------------------------------------------------- 1 | import type { Slice } from "@prismicio/client" 2 | import type { 3 | DefineComponent, 4 | FunctionalComponent, 5 | defineAsyncComponent, 6 | } from "vue" 7 | 8 | /** 9 | * Returns the type of a `SliceLike` type. 10 | * 11 | * @typeParam TSlice - The Slice from which the type will be extracted. 12 | */ 13 | type ExtractSliceType = TSlice extends SliceLikeRestV2 14 | ? TSlice["slice_type"] 15 | : TSlice extends SliceLikeGraphQL 16 | ? TSlice["type"] 17 | : never 18 | 19 | /** 20 | * The minimum required properties to represent a Prismic Slice from the Prismic 21 | * Rest API V2 for the `unstable_mapSliceZone()` helper. 22 | * 23 | * @typeParam SliceType - Type name of the Slice. 24 | */ 25 | export type SliceLikeRestV2 = Pick< 26 | Slice, 27 | "id" | "slice_type" 28 | > 29 | 30 | /** 31 | * The minimum required properties to represent a Prismic Slice from the Prismic 32 | * GraphQL API for the `unstable_mapSliceZone()` helper. 33 | * 34 | * @typeParam SliceType - Type name of the Slice. 35 | */ 36 | export type SliceLikeGraphQL = { 37 | type: Slice["slice_type"] 38 | } 39 | 40 | /** 41 | * The minimum required properties to represent a Prismic Slice for the 42 | * `` component. 43 | * 44 | * If using Prismic's Rest API V2, use the `Slice` export from 45 | * `@prismicio/client` for a full interface. 46 | * 47 | * @typeParam TSliceType - Type name of the Slice 48 | */ 49 | export type SliceLike = ( 50 | | SliceLikeRestV2 51 | | SliceLikeGraphQL 52 | ) & { 53 | /** 54 | * If `true`, this Slice has been modified from its original value using a 55 | * mapper and `@prismicio/client`'s `mapSliceZone()`. 56 | * 57 | * @internal 58 | */ 59 | __mapped?: true 60 | } 61 | 62 | /** 63 | * A looser version of the `SliceZone` type from `@prismicio/client` using 64 | * `SliceLike`. 65 | * 66 | * If using Prismic's REST API, use the `SliceZone` export from 67 | * `@prismicio/client` for the full type. 68 | * 69 | * @typeParam TSlice - The type(s) of slices in the Slice Zone 70 | */ 71 | export type SliceZoneLike = 72 | readonly TSlice[] 73 | 74 | /** 75 | * Vue props for a component rendering content from a Prismic Slice using the 76 | * `` component. 77 | * 78 | * @typeParam TSlice - The type(s) of slices in the Slice Zone 79 | * @typeParam TContext - Arbitrary data passed to `` and made 80 | * available to all Slice components 81 | */ 82 | export type SliceComponentProps< 83 | TSlice extends SliceLike = SliceLike, 84 | TContext = unknown, 85 | > = { 86 | /** Slice data for this component. */ 87 | slice: TSlice 88 | 89 | /** The index of the Slice in the Slice Zone. */ 90 | index: number 91 | 92 | /** All Slices from the Slice Zone to which the Slice belongs. */ 93 | // TODO: We have to keep this list of Slices general due to circular 94 | // reference limtiations. If we had another generic to determine the full 95 | // union of Slice types, it would include TSlice. This causes TypeScript to 96 | // throw a compilation error. 97 | slices: SliceZoneLike< 98 | TSlice extends SliceLikeGraphQL ? SliceLikeGraphQL : SliceLikeRestV2 99 | > 100 | 101 | /** 102 | * Arbitrary data passed to `` and made available to all Slice 103 | * components. 104 | */ 105 | context: TContext 106 | } 107 | 108 | /** 109 | * A Vue component to be rendered for each instance of its Slice. 110 | * 111 | * @typeParam TSlice - The type(s) of slices in the Slice Zone 112 | * @typeParam TContext - Arbitrary data made available to all Slice components 113 | */ 114 | export type SliceComponentType< 115 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 116 | TSlice extends SliceLike = any, 117 | TContext = unknown, 118 | > = 119 | // For reference within TypeScript files when `*.vue` type cannot be inferred 120 | // eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-explicit-any 121 | | DefineComponent<{}, {}, any> 122 | // Likewise, for reference with TypeScript files. 123 | | ReturnType 124 | | DefineComponent> 125 | | FunctionalComponent 126 | 127 | /** 128 | * A record of Slice types mapped to Vue components. Each components will be 129 | * rendered for each instance of their Slice type. 130 | * 131 | * @typeParam TSlice - The type(s) of slices in the Slice Zone 132 | * @typeParam TContext - Arbitrary data made available to all Slice components 133 | */ 134 | export type SliceZoneComponents< 135 | TSlice extends SliceLike = SliceLike, 136 | TContext = unknown, 137 | > = 138 | // This is purposely not wrapped in Partial to ensure a component is provided 139 | // for all Slice types. will render a default component if one is 140 | // not provided, but it *should* be a type error if an explicit component is 141 | // missing. 142 | // 143 | // If a developer purposely does not want to provide a component, they can 144 | // assign it to the TODOSliceComponent exported from this package. This 145 | // signals to future developers that it is a placeholder and should be 146 | // implemented. 147 | { 148 | [SliceType in ExtractSliceType]: 149 | | SliceComponentType< 150 | Extract> extends never 151 | ? SliceLike 152 | : Extract>, 153 | TContext 154 | > 155 | // Global components 156 | | string 157 | } 158 | -------------------------------------------------------------------------------- /src/PrismicImage.vue: -------------------------------------------------------------------------------- 1 | 183 | 184 | 194 | -------------------------------------------------------------------------------- /test/SliceZone.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from "vitest" 2 | 3 | import { flushPromises, mount } from "@vue/test-utils" 4 | import type { DefineComponent } from "vue" 5 | import { defineAsyncComponent, markRaw } from "vue" 6 | 7 | import { createWrapperComponent } from "./__fixtures__/WrapperComponent" 8 | 9 | import type { 10 | SliceComponentProps, 11 | SliceComponentType, 12 | SliceLike, 13 | SliceZoneLike, 14 | } from "../src" 15 | import { 16 | SliceZone, 17 | defineSliceZoneComponents, 18 | getSliceComponentProps, 19 | } from "../src" 20 | 21 | const Foo = createWrapperComponent( 22 | "Foo", 23 | getSliceComponentProps(), 24 | ) 25 | const Bar = createWrapperComponent( 26 | "Bar", 27 | getSliceComponentProps(), 28 | ) 29 | const Baz = createWrapperComponent( 30 | "Baz", 31 | getSliceComponentProps(), 32 | ) 33 | 34 | describe("renders a slice zone", () => { 35 | it("from the REST API", async () => { 36 | const wrapper = mount(SliceZone, { 37 | props: { 38 | slices: [ 39 | { id: "1", slice_type: "foo" }, 40 | { id: "2", slice_type: "bar" }, 41 | { id: "3", slice_type: "baz" }, 42 | ], 43 | components: defineSliceZoneComponents({ 44 | foo: Foo, 45 | bar: defineAsyncComponent( 46 | () => new Promise((res) => res(Bar)), 47 | ), 48 | baz: "Baz", 49 | }), 50 | }, 51 | global: { 52 | components: { 53 | Baz, 54 | }, 55 | }, 56 | }) 57 | 58 | await flushPromises() 59 | 60 | expect(wrapper.html()).toBe( 61 | `
62 |
63 |
`, 64 | ) 65 | }) 66 | 67 | it("from the GraphQL API", async () => { 68 | const wrapper = mount(SliceZone, { 69 | props: { 70 | slices: [{ type: "foo" }, { type: "bar" }, { type: "baz" }], 71 | components: defineSliceZoneComponents({ 72 | foo: Foo, 73 | bar: defineAsyncComponent( 74 | () => new Promise((res) => res(Bar)), 75 | ), 76 | baz: "Baz", 77 | }), 78 | }, 79 | global: { 80 | components: { 81 | Baz, 82 | }, 83 | }, 84 | }) 85 | 86 | await flushPromises() 87 | 88 | expect(wrapper.html()).toBe( 89 | `
90 |
91 |
`, 92 | ) 93 | }) 94 | 95 | it("from @prismicio/client's mapSliceZone()", async () => { 96 | const Foo = createWrapperComponent<{ id: string; slice_type: string }>( 97 | "Foo", 98 | ["id", "slice_type"], 99 | ) 100 | const Bar = createWrapperComponent<{ id: string; slice_type: string }>( 101 | "Bar", 102 | ["id", "slice_type"], 103 | ) 104 | const Baz = createWrapperComponent( 105 | "Baz", 106 | getSliceComponentProps(), 107 | ) 108 | 109 | const wrapper = mount(SliceZone, { 110 | props: { 111 | slices: [ 112 | { __mapped: true, id: "1", slice_type: "foo", abc: "123" }, 113 | { __mapped: true, id: "2", slice_type: "bar", def: "456" }, 114 | { id: "3", slice_type: "baz" }, 115 | ], 116 | components: defineSliceZoneComponents({ 117 | foo: Foo, 118 | bar: defineAsyncComponent( 119 | () => new Promise((res) => res(Bar)), 120 | ), 121 | baz: "Baz", 122 | }), 123 | }, 124 | global: { 125 | components: { 126 | Baz, 127 | }, 128 | }, 129 | }) 130 | 131 | await flushPromises() 132 | 133 | expect(wrapper.html()).toBe( 134 | `
135 |
136 |
`, 137 | ) 138 | }) 139 | 140 | it("as nothing when invalid", () => { 141 | const wrapper = mount(SliceZone, { 142 | props: { 143 | slices: null as unknown as SliceZoneLike, 144 | components: defineSliceZoneComponents({}), 145 | }, 146 | }) 147 | 148 | expect(wrapper.html()).toBe("") 149 | }) 150 | }) 151 | 152 | it("provides context to each slice", () => { 153 | const Foo = createWrapperComponent( 154 | "Foo", 155 | getSliceComponentProps(), 156 | ) 157 | const Bar = createWrapperComponent( 158 | "Bar", 159 | getSliceComponentProps(), 160 | ) 161 | 162 | const context = { foo: "bar" } 163 | 164 | const wrapper = mount(SliceZone, { 165 | props: { 166 | slices: [ 167 | { id: "1", slice_type: "foo" }, 168 | { id: "2", slice_type: "bar" }, 169 | ], 170 | components: defineSliceZoneComponents({ 171 | foo: Foo, 172 | bar: Bar, 173 | }), 174 | context, 175 | }, 176 | }) 177 | 178 | expect( 179 | wrapper.getComponent(Foo as DefineComponent).props() 180 | .context, 181 | ).toStrictEqual(context) 182 | expect( 183 | wrapper.getComponent(Bar as DefineComponent).props() 184 | .context, 185 | ).toStrictEqual(context) 186 | }) 187 | 188 | describe("renders TODO component if component mapping is missing", () => { 189 | it("with the REST API", () => { 190 | vi.stubGlobal("console", { warn: vi.fn() }) 191 | 192 | const Foo = createWrapperComponent( 193 | "Foo", 194 | getSliceComponentProps(), 195 | ) 196 | 197 | const wrapper = mount(SliceZone, { 198 | props: { 199 | slices: [ 200 | { id: "1", slice_type: "foo" }, 201 | { id: "2", slice_type: "bar" }, 202 | { __mapped: true, id: "3", slice_type: "baz", abc: "123" }, 203 | ], 204 | components: defineSliceZoneComponents({ 205 | foo: Foo, 206 | }), 207 | }, 208 | }) 209 | 210 | expect(wrapper.html()).toBe( 211 | `
212 |
Could not find a component for Slice type "bar"
213 |
Could not find a component for Slice type "baz"
`, 214 | ) 215 | expect(console.warn).toHaveBeenCalledTimes(2) 216 | expect(vi.mocked(console.warn).mock.calls[0][0]).toMatch( 217 | /could not find a component/i, 218 | ) 219 | expect(vi.mocked(console.warn).mock.calls[1][0]).toMatch( 220 | /could not find a component/i, 221 | ) 222 | 223 | vi.resetAllMocks() 224 | }) 225 | 226 | it("with the GraphQL API", () => { 227 | vi.stubGlobal("console", { warn: vi.fn() }) 228 | 229 | const Foo = createWrapperComponent( 230 | "Foo", 231 | getSliceComponentProps(), 232 | ) 233 | 234 | const wrapper = mount(SliceZone, { 235 | props: { 236 | slices: [{ type: "foo" }, { type: "bar" }], 237 | components: defineSliceZoneComponents({ 238 | foo: Foo, 239 | }), 240 | }, 241 | }) 242 | 243 | expect(wrapper.html()).toBe( 244 | `
245 |
Could not find a component for Slice type "bar"
`, 246 | ) 247 | expect(console.warn).toHaveBeenCalledOnce() 248 | expect(vi.mocked(console.warn).mock.calls[0][0]).toMatch( 249 | /could not find a component/i, 250 | ) 251 | 252 | vi.resetAllMocks() 253 | }) 254 | 255 | it("from props", () => { 256 | const wrapper = mount(SliceZone, { 257 | props: { 258 | slices: [ 259 | { id: "1", slice_type: "foo" }, 260 | { id: "2", slice_type: "bar" }, 261 | ], 262 | components: defineSliceZoneComponents({ 263 | foo: Foo, 264 | }), 265 | defaultComponent: markRaw(Baz), 266 | }, 267 | }) 268 | 269 | expect(wrapper.html()).toBe( 270 | `
271 |
`, 272 | ) 273 | }) 274 | }) 275 | 276 | it("reacts to changes properly", async () => { 277 | const Foo = createWrapperComponent( 278 | "Foo", 279 | getSliceComponentProps(), 280 | ) 281 | const Bar = createWrapperComponent( 282 | "Bar", 283 | getSliceComponentProps(), 284 | ) 285 | 286 | const wrapper = mount(SliceZone, { 287 | props: { 288 | slices: [ 289 | { id: "1", slice_type: "foo" }, 290 | { id: "2", slice_type: "bar" }, 291 | ], 292 | components: defineSliceZoneComponents({ 293 | foo: Foo, 294 | bar: Bar, 295 | }), 296 | }, 297 | }) 298 | 299 | const firstRender = wrapper.html() 300 | 301 | await wrapper.setProps({ 302 | slices: [{ id: "2", slice_type: "bar" }], 303 | components: defineSliceZoneComponents({ 304 | foo: Foo, 305 | bar: Bar, 306 | }), 307 | }) 308 | 309 | const secondRender = wrapper.html() 310 | 311 | expect(secondRender).not.toBe(firstRender) 312 | expect(secondRender).toBe(`
`) 313 | }) 314 | -------------------------------------------------------------------------------- /test/PrismicLink.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from "vitest" 2 | 3 | import { LinkType } from "@prismicio/client" 4 | import { mount } from "@vue/test-utils" 5 | import { markRaw } from "vue" 6 | 7 | import { 8 | WrapperComponent, 9 | createWrapperComponent, 10 | } from "./__fixtures__/WrapperComponent" 11 | import router from "./__fixtures__/router" 12 | 13 | import { PrismicLink } from "../src" 14 | 15 | describe("renders a link field", () => { 16 | it("empty", (ctx) => { 17 | const wrapper = mount(PrismicLink, { 18 | props: { 19 | field: ctx.mock.value.link({ state: "empty", type: LinkType.Any }), 20 | }, 21 | slots: { default: "foo" }, 22 | }) 23 | 24 | expect(wrapper.html()).toBe("foo") 25 | }) 26 | 27 | it("link to web", (ctx) => { 28 | const wrapper = mount(PrismicLink, { 29 | props: { 30 | field: { 31 | ...ctx.mock.value.link({ 32 | type: LinkType.Web, 33 | withTargetBlank: false, 34 | }), 35 | url: "https://example.com", 36 | }, 37 | }, 38 | slots: { default: "foo" }, 39 | }) 40 | 41 | expect(wrapper.html()).toBe( 42 | 'foo', 43 | ) 44 | }) 45 | 46 | it("link to web (blank target)", (ctx) => { 47 | const wrapper = mount(PrismicLink, { 48 | props: { 49 | field: { 50 | ...ctx.mock.value.link({ 51 | type: LinkType.Web, 52 | withTargetBlank: true, 53 | }), 54 | url: "https://example.com", 55 | }, 56 | }, 57 | slots: { default: "foo" }, 58 | }) 59 | 60 | expect(wrapper.html()).toBe( 61 | 'foo', 62 | ) 63 | }) 64 | 65 | it("link to media", (ctx) => { 66 | const wrapper = mount(PrismicLink, { 67 | props: { 68 | field: { 69 | ...ctx.mock.value.link({ type: LinkType.Media }), 70 | url: "https://example.com/image.png", 71 | }, 72 | }, 73 | slots: { default: "foo" }, 74 | }) 75 | 76 | expect(wrapper.html()).toBe( 77 | 'foo', 78 | ) 79 | }) 80 | 81 | it("content relationship", (ctx) => { 82 | const wrapper = mount(PrismicLink, { 83 | props: { 84 | field: { 85 | ...ctx.mock.value.link({ type: LinkType.Document }), 86 | url: "/bar", 87 | }, 88 | }, 89 | slots: { default: "foo" }, 90 | global: { 91 | plugins: [router], 92 | }, 93 | }) 94 | 95 | expect(wrapper.html()).toBe('foo') 96 | }) 97 | }) 98 | 99 | describe("renders a document as link", () => { 100 | it("resolvable", (ctx) => { 101 | const wrapper = mount(PrismicLink, { 102 | props: { 103 | document: { 104 | ...ctx.mock.value.document(), 105 | url: "/bar", 106 | }, 107 | }, 108 | slots: { default: "foo" }, 109 | global: { 110 | plugins: [router], 111 | }, 112 | }) 113 | 114 | expect(wrapper.html()).toBe('foo') 115 | }) 116 | 117 | it("non-resolvable", (ctx) => { 118 | const wrapper = mount(PrismicLink, { 119 | props: { 120 | document: { 121 | ...ctx.mock.value.document(), 122 | url: null, 123 | }, 124 | }, 125 | slots: { default: "foo" }, 126 | }) 127 | 128 | expect(wrapper.html()).toBe("foo") 129 | }) 130 | }) 131 | 132 | describe("renders link content", () => { 133 | it("with link text", (ctx) => { 134 | const linkToWeb = mount(PrismicLink, { 135 | props: { 136 | field: { 137 | ...ctx.mock.value.link({ 138 | type: LinkType.Web, 139 | withTargetBlank: false, 140 | model: { type: "Link", config: { allowText: true } }, 141 | withText: true, 142 | }), 143 | url: "https://example.com", 144 | text: "bar", 145 | }, 146 | }, 147 | }) 148 | 149 | expect(linkToWeb.html()).toBe( 150 | 'bar', 151 | ) 152 | 153 | const linkToMedia = mount(PrismicLink, { 154 | props: { 155 | field: { 156 | ...ctx.mock.value.link({ type: LinkType.Media }), 157 | url: "https://example.com/image.png", 158 | text: "bar", 159 | }, 160 | }, 161 | }) 162 | 163 | expect(linkToMedia.html()).toBe( 164 | 'bar', 165 | ) 166 | 167 | const contentRelationship = mount(PrismicLink, { 168 | props: { 169 | field: { 170 | ...ctx.mock.value.link({ type: LinkType.Document }), 171 | url: "/bar", 172 | text: "bar", 173 | }, 174 | }, 175 | global: { 176 | plugins: [router], 177 | }, 178 | }) 179 | 180 | expect(contentRelationship.html()).toBe('bar') 181 | }) 182 | 183 | it("with slot (priority over link text)", (ctx) => { 184 | const linkToWeb = mount(PrismicLink, { 185 | props: { 186 | field: { 187 | ...ctx.mock.value.link({ 188 | type: LinkType.Web, 189 | withTargetBlank: false, 190 | model: { type: "Link", config: { allowText: true } }, 191 | withText: true, 192 | }), 193 | url: "https://example.com", 194 | text: "bar", 195 | }, 196 | }, 197 | slots: { default: "foo" }, 198 | }) 199 | 200 | expect(linkToWeb.html()).toBe( 201 | 'foo', 202 | ) 203 | 204 | const linkToMedia = mount(PrismicLink, { 205 | props: { 206 | field: { 207 | ...ctx.mock.value.link({ type: LinkType.Media }), 208 | url: "https://example.com/image.png", 209 | text: "bar", 210 | }, 211 | }, 212 | slots: { default: "foo" }, 213 | }) 214 | 215 | expect(linkToMedia.html()).toBe( 216 | 'foo', 217 | ) 218 | 219 | const contentRelationship = mount(PrismicLink, { 220 | props: { 221 | field: { 222 | ...ctx.mock.value.link({ type: LinkType.Document }), 223 | url: "/bar", 224 | text: "bar", 225 | }, 226 | }, 227 | slots: { default: "foo" }, 228 | global: { 229 | plugins: [router], 230 | }, 231 | }) 232 | 233 | expect(contentRelationship.html()).toBe('foo') 234 | }) 235 | }) 236 | 237 | describe("uses link resolver", () => { 238 | it("from props", (ctx) => { 239 | const spiedLinkResolver = vi.fn(() => "/bar") 240 | 241 | const wrapper = mount(PrismicLink, { 242 | props: { 243 | field: { 244 | ...ctx.mock.value.link({ type: LinkType.Document }), 245 | url: undefined, 246 | }, 247 | linkResolver: spiedLinkResolver, 248 | }, 249 | slots: { default: "foo" }, 250 | global: { 251 | plugins: [router], 252 | }, 253 | }) 254 | 255 | expect(spiedLinkResolver).toHaveBeenCalledOnce() 256 | expect(wrapper.html()).toBe('foo') 257 | }) 258 | }) 259 | 260 | describe("renders rel attribute", () => { 261 | it("omitted on internal links", (ctx) => { 262 | const wrapper = mount(PrismicLink, { 263 | props: { 264 | field: { 265 | ...ctx.mock.value.link({ type: LinkType.Document }), 266 | url: "/bar", 267 | }, 268 | }, 269 | global: { 270 | plugins: [router], 271 | }, 272 | }) 273 | 274 | expect(wrapper.html()).not.toContain('rel="') 275 | }) 276 | 277 | it("with default value on external links", (ctx) => { 278 | const wrapper = mount(PrismicLink, { 279 | props: { 280 | field: { 281 | ...ctx.mock.value.link({ 282 | type: LinkType.Web, 283 | withTargetBlank: false, 284 | }), 285 | url: "https://example.com", 286 | }, 287 | }, 288 | }) 289 | 290 | expect(wrapper.html()).toContain('rel="noreferrer"') 291 | }) 292 | 293 | it("with props function", (ctx) => { 294 | const wrapper = mount(PrismicLink, { 295 | props: { 296 | field: { 297 | ...ctx.mock.value.link({ 298 | type: LinkType.Web, 299 | withTargetBlank: false, 300 | }), 301 | url: "https://example.com", 302 | }, 303 | rel: () => "props", 304 | }, 305 | global: { 306 | plugins: [router], 307 | }, 308 | }) 309 | 310 | expect(wrapper.html()).toContain(`rel="props"`) 311 | }) 312 | 313 | it("with props string value", (ctx) => { 314 | const wrapper = mount(PrismicLink, { 315 | props: { 316 | field: { 317 | ...ctx.mock.value.link({ 318 | type: LinkType.Web, 319 | withTargetBlank: false, 320 | }), 321 | url: "https://example.com", 322 | }, 323 | rel: "props", 324 | }, 325 | global: { 326 | plugins: [router], 327 | }, 328 | }) 329 | 330 | expect(wrapper.html()).toContain(`rel="props"`) 331 | }) 332 | }) 333 | 334 | describe("renders external links using component", () => { 335 | it("from props", (ctx) => { 336 | const wrapper = mount(PrismicLink, { 337 | props: { 338 | field: { 339 | ...ctx.mock.value.link({ 340 | type: LinkType.Web, 341 | withTargetBlank: false, 342 | }), 343 | url: "https://example.com", 344 | }, 345 | externalComponent: markRaw(createWrapperComponent()), 346 | }, 347 | }) 348 | 349 | expect(wrapper.html()).toBe( 350 | '
', 351 | ) 352 | }) 353 | 354 | it("forwards attributes", (ctx) => { 355 | const wrapper = mount(PrismicLink, { 356 | props: { 357 | field: { 358 | ...ctx.mock.value.link({ 359 | type: LinkType.Web, 360 | withTargetBlank: true, 361 | }), 362 | url: "https://example.com", 363 | }, 364 | externalComponent: markRaw(WrapperComponent), 365 | }, 366 | slots: { default: "foo" }, 367 | }) 368 | 369 | expect(wrapper.html()).toBe( 370 | '
foo
', 371 | ) 372 | }) 373 | }) 374 | 375 | describe("renders internal links using component", () => { 376 | it("from props", (ctx) => { 377 | const wrapper = mount(PrismicLink, { 378 | props: { 379 | field: { 380 | ...ctx.mock.value.link({ type: LinkType.Document }), 381 | url: "/bar", 382 | }, 383 | internalComponent: markRaw(createWrapperComponent()), 384 | }, 385 | }) 386 | 387 | expect(wrapper.html()).toBe( 388 | '
', 389 | ) 390 | }) 391 | }) 392 | 393 | it("reacts to changes properly", async (ctx) => { 394 | const wrapper = mount(PrismicLink, { 395 | props: { 396 | field: { 397 | ...ctx.mock.value.link({ type: LinkType.Web }), 398 | url: "https://example.com", 399 | }, 400 | }, 401 | slots: { default: "foo" }, 402 | }) 403 | 404 | const firstRender = wrapper.html() 405 | 406 | await wrapper.setProps({ 407 | field: { 408 | ...ctx.mock.value.link({ 409 | type: LinkType.Web, 410 | withTargetBlank: false, 411 | }), 412 | target: undefined, 413 | url: "https://prismic.io", 414 | }, 415 | }) 416 | 417 | const secondRender = wrapper.html() 418 | 419 | expect(secondRender).not.toBe(firstRender) 420 | expect(secondRender).toMatchInlineSnapshot( 421 | `"foo"`, 422 | ) 423 | }) 424 | -------------------------------------------------------------------------------- /test/PrismicTable.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest" 2 | 3 | import type { TableField } from "@prismicio/client" 4 | import { mount } from "@vue/test-utils" 5 | import { defineComponent, markRaw } from "vue" 6 | 7 | import { WrapperComponent } from "./__fixtures__/WrapperComponent" 8 | 9 | import { 10 | PrismicTable, 11 | getRichTextComponentProps, 12 | getTableComponentProps, 13 | } from "../src" 14 | 15 | const filledTableField: TableField = { 16 | head: { 17 | rows: [ 18 | { 19 | key: "string", 20 | cells: [ 21 | { 22 | key: "string", 23 | type: "header", 24 | content: [{ type: "paragraph", text: "Method", spans: [] }], 25 | }, 26 | { 27 | key: "string", 28 | type: "header", 29 | content: [{ type: "paragraph", text: "Usage", spans: [] }], 30 | }, 31 | ], 32 | }, 33 | ], 34 | }, 35 | body: { 36 | rows: [ 37 | { 38 | key: "string", 39 | cells: [ 40 | { 41 | key: "string", 42 | type: "header", 43 | content: [{ type: "paragraph", text: "GET", spans: [] }], 44 | }, 45 | { 46 | key: "string", 47 | type: "data", 48 | content: [ 49 | { 50 | type: "paragraph", 51 | text: "For basic retrieval of information…", 52 | spans: [ 53 | { 54 | type: "strong", 55 | end: 19, 56 | start: 4, 57 | }, 58 | ], 59 | }, 60 | ], 61 | }, 62 | ], 63 | }, 64 | { 65 | key: "string", 66 | cells: [ 67 | { 68 | key: "string", 69 | type: "header", 70 | content: [{ type: "paragraph", text: "DELETE", spans: [] }], 71 | }, 72 | { 73 | key: "string", 74 | type: "data", 75 | content: [ 76 | { 77 | type: "paragraph", 78 | text: "To destroy a resource and remove…", 79 | spans: [ 80 | { 81 | type: "em", 82 | end: 7, 83 | start: 3, 84 | }, 85 | ], 86 | }, 87 | ], 88 | }, 89 | ], 90 | }, 91 | ], 92 | }, 93 | } 94 | 95 | describe("renders a table field", () => { 96 | it("as components", () => { 97 | const output = mount(PrismicTable, { 98 | props: { 99 | field: filledTableField, 100 | }, 101 | }) 102 | 103 | expect(output.html()).toBe(` 104 | 105 | 106 | 111 | 116 | 117 | 118 | 119 | 120 | 125 | 131 | 132 | 133 | 138 | 144 | 145 | 146 |
107 |

108 | Method 109 |

110 |
112 |

113 | Usage 114 |

115 |
121 |

122 | GET 123 |

124 |
126 |

127 | For basic retrieval 128 | of information… 129 |

130 |
134 |

135 | DELETE 136 |

137 |
139 |

140 | To dest 141 | roy a resource and remove… 142 |

143 |
`) 147 | }) 148 | 149 | it("as nothing when passed an empty field", () => { 150 | const output = mount(PrismicTable, { 151 | props: { 152 | field: undefined, 153 | }, 154 | }) 155 | 156 | expect(output.html()).toBe("") 157 | }) 158 | }) 159 | 160 | it("renders fallback when passed an empty field", () => { 161 | const undefinedField = mount(PrismicTable, { 162 | props: { field: undefined, fallback: markRaw(WrapperComponent) }, 163 | }) 164 | 165 | expect(undefinedField.html()).toBe(`
`) 166 | }) 167 | 168 | describe("uses table components", () => { 169 | it("from props", () => { 170 | const output = mount(PrismicTable, { 171 | props: { 172 | field: filledTableField, 173 | components: { 174 | table: markRaw( 175 | defineComponent({ 176 | template: /* html */ `
`, 177 | props: getTableComponentProps.table(), 178 | }), 179 | ), 180 | thead: markRaw( 181 | defineComponent({ 182 | template: /* html */ `
`, 183 | props: getTableComponentProps.thead(), 184 | }), 185 | ), 186 | tbody: markRaw( 187 | defineComponent({ 188 | template: /* html */ `
`, 189 | props: getTableComponentProps.tbody(), 190 | }), 191 | ), 192 | tr: markRaw( 193 | defineComponent({ 194 | template: /* html */ `
`, 195 | props: getTableComponentProps.tr(), 196 | }), 197 | ), 198 | th: markRaw( 199 | defineComponent({ 200 | template: /* html */ `
`, 201 | props: getTableComponentProps.th(), 202 | }), 203 | ), 204 | td: markRaw( 205 | defineComponent({ 206 | template: /* html */ `
`, 207 | props: getTableComponentProps.td(), 208 | }), 209 | ), 210 | }, 211 | }, 212 | }) 213 | 214 | expect(output.html()).toBe(`
215 |
216 |
217 |
218 |

219 | Method 220 |

221 |
222 |
223 |

224 | Usage 225 |

226 |
227 |
228 |
229 |
230 |
231 |
232 |

233 | GET 234 |

235 |
236 |
237 |

238 | For basic retrieval 239 | of information… 240 |

241 |
242 |
243 |
244 |
245 |

246 | DELETE 247 |

248 |
249 |
250 |

251 | To dest 252 | roy a resource and remove… 253 |

254 |
255 |
256 |
257 |
`) 258 | }) 259 | }) 260 | 261 | describe("uses table shorthands", () => { 262 | it("from props", () => { 263 | const output = mount(PrismicTable, { 264 | props: { 265 | field: filledTableField, 266 | components: { 267 | table: { class: "table" }, 268 | thead: { class: "thead" }, 269 | tbody: { class: "tbody" }, 270 | tr: { class: "tr" }, 271 | th: { class: "th" }, 272 | td: { class: "td" }, 273 | }, 274 | }, 275 | }) 276 | 277 | expect(output.html()).toBe(` 278 | 279 | 280 | 285 | 290 | 291 | 292 | 293 | 294 | 299 | 305 | 306 | 307 | 312 | 318 | 319 | 320 |
281 |

282 | Method 283 |

284 |
286 |

287 | Usage 288 |

289 |
295 |

296 | GET 297 |

298 |
300 |

301 | For basic retrieval 302 | of information… 303 |

304 |
308 |

309 | DELETE 310 |

311 |
313 |

314 | To dest 315 | roy a resource and remove… 316 |

317 |
`) 321 | }) 322 | }) 323 | 324 | describe("uses rich text components serializer", () => { 325 | it("from props", () => { 326 | const output = mount(PrismicTable, { 327 | props: { 328 | field: filledTableField, 329 | components: { 330 | table: markRaw( 331 | defineComponent({ 332 | template: /* html */ `
`, 333 | props: getTableComponentProps.table(), 334 | }), 335 | ), 336 | paragraph: markRaw( 337 | defineComponent({ 338 | template: /* html */ `

`, 339 | props: getRichTextComponentProps("paragraph"), 340 | }), 341 | ), 342 | }, 343 | }, 344 | }) 345 | 346 | expect(output.html()).toBe(` 347 | 348 | 349 | 354 | 359 | 360 | 361 | 362 | 363 | 368 | 374 | 375 | 376 | 381 | 387 | 388 | 389 |
350 |

351 | Method 352 |

353 |
355 |

356 | Usage 357 |

358 |
364 |

365 | GET 366 |

367 |
369 |

370 | For basic retrieval 371 | of information… 372 |

373 |
377 |

378 | DELETE 379 |

380 |
382 |

383 | To dest 384 | roy a resource and remove… 385 |

386 |
`) 390 | }) 391 | }) 392 | 393 | describe("uses rich text shorthand serializer", () => { 394 | it("from props", () => { 395 | const output = mount(PrismicTable, { 396 | props: { 397 | field: filledTableField, 398 | components: { 399 | table: { class: "table" }, 400 | paragraph: { as: "p", class: "paragraph" }, 401 | }, 402 | }, 403 | }) 404 | 405 | expect(output.html()).toBe(` 406 | 407 | 408 | 413 | 418 | 419 | 420 | 421 | 422 | 427 | 433 | 434 | 435 | 440 | 446 | 447 | 448 |
409 |

410 | Method 411 |

412 |
414 |

415 | Usage 416 |

417 |
423 |

424 | GET 425 |

426 |
428 |

429 | For basic retrieval 430 | of information… 431 |

432 |
436 |

437 | DELETE 438 |

439 |
441 |

442 | To dest 443 | roy a resource and remove… 444 |

445 |
`) 449 | }) 450 | }) 451 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /test/PrismicImage.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from "vitest" 2 | 3 | import { mount } from "@vue/test-utils" 4 | 5 | import { PrismicImage } from "../src" 6 | 7 | describe("renders an image field", () => { 8 | it("empty", () => { 9 | const wrapper = mount(PrismicImage, { 10 | props: { 11 | field: { url: null, alt: null, copyright: null }, 12 | }, 13 | }) 14 | 15 | expect(wrapper.html()).toBe("") 16 | }) 17 | 18 | it("with a width-based srcset by default", (ctx) => { 19 | const wrapper = mount(PrismicImage, { 20 | props: { field: ctx.mock.value.image() }, 21 | }) 22 | 23 | expect(wrapper.html()).toMatchInlineSnapshot( 24 | `"Bibendum enim facilisis gravida neque convallis a cras semper auctor"`, 25 | ) 26 | }) 27 | }) 28 | 29 | describe("renders a width-based srcset", () => { 30 | it("with given widths", (ctx) => { 31 | const wrapper = mount(PrismicImage, { 32 | props: { 33 | field: ctx.mock.value.image(), 34 | imgixParams: { sat: 100 }, 35 | widths: [100, 200, 300], 36 | }, 37 | }) 38 | 39 | expect(wrapper.html()).toMatchInlineSnapshot( 40 | `"Integer eget aliquet nibh praesent tristique magna sit amet purus"`, 41 | ) 42 | }) 43 | 44 | it("with default widths when widths is `defaults`", (ctx) => { 45 | const wrapper = mount(PrismicImage, { 46 | props: { 47 | field: ctx.mock.value.image(), 48 | imgixParams: { sat: 100 }, 49 | widths: "defaults", 50 | }, 51 | }) 52 | 53 | expect(wrapper.html()).toMatchInlineSnapshot( 54 | `"Adipiscing at tellus integer feugiat scelerisque varius morbi enim nunc faucibus a"`, 55 | ) 56 | }) 57 | 58 | it("with the field's responsive views if widths is `thumbnails`", (ctx) => { 59 | const wrapper = mount(PrismicImage, { 60 | props: { 61 | field: { 62 | ...ctx.mock.value.image(), 63 | foo: ctx.mock.value.image(), 64 | bar: ctx.mock.value.image(), 65 | baz: ctx.mock.value.image(), 66 | }, 67 | imgixParams: { sat: 100 }, 68 | widths: "thumbnails", 69 | }, 70 | }) 71 | 72 | expect(wrapper.html()).toMatchInlineSnapshot( 73 | `"Est sit amet facilisis magna etiam"`, 74 | ) 75 | }) 76 | }) 77 | 78 | describe("renders a pixel-density srcset", () => { 79 | it("with the given densities", (ctx) => { 80 | const wrapper = mount(PrismicImage, { 81 | props: { 82 | field: ctx.mock.value.image(), 83 | imgixParams: { sat: 100 }, 84 | pixelDensities: [1, 2], 85 | }, 86 | }) 87 | 88 | expect(wrapper.html()).toMatchInlineSnapshot( 89 | `"Tortor at auctor urna nunc"`, 90 | ) 91 | }) 92 | 93 | it("with default densities if pixelDensities is `defaults`", (ctx) => { 94 | const wrapper = mount(PrismicImage, { 95 | props: { 96 | field: ctx.mock.value.image(), 97 | imgixParams: { sat: 100 }, 98 | pixelDensities: "defaults", 99 | }, 100 | }) 101 | 102 | expect(wrapper.html()).toMatchInlineSnapshot( 103 | `"Nunc pulvinar sapien et ligula ullamcorper malesuada proin libero nunc consequat interdum"`, 104 | ) 105 | }) 106 | }) 107 | 108 | it("prioritizes widths prop over pixelDensities", (ctx) => { 109 | const consoleWarnSpy = vi 110 | .spyOn(console, "warn") 111 | .mockImplementation(() => void 0) 112 | 113 | const wrapper = mount(PrismicImage, { 114 | props: { 115 | field: ctx.mock.value.image(), 116 | imgixParams: { sat: 100 }, 117 | widths: "defaults", 118 | // @ts-expect-error - Purposely giving incompatible props. 119 | pixelDensities: "defaults", 120 | }, 121 | }) 122 | 123 | expect(wrapper.html()).toMatchInlineSnapshot( 124 | `"Mattis vulputate enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac"`, 125 | ) 126 | 127 | consoleWarnSpy.mockRestore() 128 | }) 129 | 130 | it("warns if both widths and pixelDensites are given", (ctx) => { 131 | const consoleWarnSpy = vi 132 | .spyOn(console, "warn") 133 | .mockImplementation(() => void 0) 134 | 135 | mount(PrismicImage, { 136 | props: { 137 | field: ctx.mock.value.image(), 138 | imgixParams: { sat: 100 }, 139 | widths: "defaults", 140 | // @ts-expect-error - Purposely giving incompatible props. 141 | pixelDensities: "defaults", 142 | }, 143 | }) 144 | 145 | expect(consoleWarnSpy).toHaveBeenCalledOnce() 146 | expect(consoleWarnSpy).toHaveBeenCalledWith( 147 | expect.stringMatching( 148 | /only one of "widths" or "pixelDensities" props can be provided/i, 149 | ), 150 | ) 151 | 152 | consoleWarnSpy.mockRestore() 153 | }) 154 | 155 | describe("renders alt attribute", () => { 156 | it("omitted if the field does not have an alt value", (ctx) => { 157 | const wrapper = mount(PrismicImage, { 158 | props: { field: { ...ctx.mock.value.image(), alt: null } }, 159 | }) 160 | 161 | expect(wrapper.html()).not.toContain('alt="') 162 | }) 163 | 164 | it("with the field's alt if given", (ctx) => { 165 | const wrapper = mount(PrismicImage, { 166 | props: { field: { ...ctx.mock.value.image(), alt: "foo" } }, 167 | }) 168 | 169 | expect(wrapper.html()).toContain('alt="foo"') 170 | }) 171 | 172 | it("with an explicit decorative fallback alt value if given", (ctx) => { 173 | const wrapper = mount(PrismicImage, { 174 | props: { 175 | field: { ...ctx.mock.value.image(), alt: null }, 176 | fallbackAlt: "", 177 | }, 178 | }) 179 | 180 | expect(wrapper.html()).toContain('alt=""') 181 | }) 182 | 183 | it("warns if a non-decorative fallback alt value is given", (ctx) => { 184 | const consoleWarnSpy = vi 185 | .spyOn(console, "warn") 186 | .mockImplementation(() => void 0) 187 | 188 | mount(PrismicImage, { 189 | props: { 190 | field: ctx.mock.value.image(), 191 | // @ts-expect-error - Purposely giving incompatible props. 192 | fallbackAlt: "non-decorative", 193 | }, 194 | }) 195 | 196 | expect(consoleWarnSpy).toHaveBeenCalledWith( 197 | expect.stringMatching(/alt-must-be-an-empty-string/i), 198 | ) 199 | 200 | consoleWarnSpy.mockRestore() 201 | }) 202 | 203 | it("with an explicit decorative alt when field has an alt value", (ctx) => { 204 | const wrapper = mount(PrismicImage, { 205 | props: { 206 | field: { ...ctx.mock.value.image(), alt: "foo" }, 207 | alt: "", 208 | }, 209 | }) 210 | 211 | expect(wrapper.html()).toContain('alt=""') 212 | }) 213 | 214 | it("with an explicit decorative alt when field does not have an alt value", (ctx) => { 215 | const wrapper = mount(PrismicImage, { 216 | props: { 217 | field: { ...ctx.mock.value.image(), alt: null }, 218 | alt: "", 219 | }, 220 | }) 221 | 222 | expect(wrapper.html()).toContain('alt=""') 223 | }) 224 | 225 | it("warns if a non-decorative alt value is given", (ctx) => { 226 | const consoleWarnSpy = vi 227 | .spyOn(console, "warn") 228 | .mockImplementation(() => void 0) 229 | 230 | mount(PrismicImage, { 231 | props: { 232 | field: { ...ctx.mock.value.image(), alt: null }, 233 | // @ts-expect-error - Purposely giving incompatible props. 234 | alt: "non-decorative", 235 | }, 236 | }) 237 | 238 | expect(consoleWarnSpy).toHaveBeenCalledWith( 239 | expect.stringMatching(/alt-must-be-an-empty-string/i), 240 | ) 241 | 242 | consoleWarnSpy.mockRestore() 243 | }) 244 | }) 245 | 246 | describe("renders width and height attributes", () => { 247 | it("with the field's dimensions by default", (ctx) => { 248 | const field = ctx.mock.value.image() 249 | const wrapper = mount(PrismicImage, { 250 | props: { field }, 251 | }) 252 | 253 | expect(wrapper.html()).toContain(`width="${field.dimensions.width}"`) 254 | expect(wrapper.html()).toContain(`height="${field.dimensions.height}"`) 255 | }) 256 | 257 | it("with number width and height props", (ctx) => { 258 | const field = ctx.mock.value.image() 259 | const wrapper = mount(PrismicImage, { 260 | props: { field, width: 100, height: 100 }, 261 | }) 262 | 263 | expect(wrapper.html()).toContain(`width="100"`) 264 | expect(wrapper.html()).toContain(`height="100"`) 265 | }) 266 | 267 | it("with string width and height props", (ctx) => { 268 | const field = ctx.mock.value.image() 269 | const wrapper = mount(PrismicImage, { 270 | props: { field, width: "100", height: "100" }, 271 | }) 272 | 273 | expect(wrapper.html()).toContain(`width="100"`) 274 | expect(wrapper.html()).toContain(`height="100"`) 275 | }) 276 | 277 | it("with NaN width and height props", (ctx) => { 278 | const field = ctx.mock.value.image() 279 | const wrapper = mount(PrismicImage, { 280 | props: { field, width: "foo", height: "bar" }, 281 | }) 282 | 283 | expect(wrapper.html()).toContain(`width="${field.dimensions.width}"`) 284 | expect(wrapper.html()).toContain(`height="${field.dimensions.height}"`) 285 | }) 286 | 287 | it("with the width prop and inferred height", (ctx) => { 288 | const field = ctx.mock.value.image() 289 | const wrapper = mount(PrismicImage, { 290 | props: { field, width: 100 }, 291 | }) 292 | 293 | const ar = field.dimensions.width / field.dimensions.height 294 | 295 | expect(wrapper.html()).toContain(`width="100"`) 296 | expect(wrapper.html()).toContain(`height="${Math.round(100 / ar)}"`) 297 | }) 298 | 299 | it("with the height prop and inferred width", (ctx) => { 300 | const field = ctx.mock.value.image() 301 | const wrapper = mount(PrismicImage, { 302 | props: { field, height: 100 }, 303 | }) 304 | 305 | const ar = field.dimensions.width / field.dimensions.height 306 | 307 | expect(wrapper.html()).toContain(`width="${Math.round(100 * ar)}"`) 308 | expect(wrapper.html()).toContain(`height="100"`) 309 | }) 310 | }) 311 | 312 | describe("renders with imgix parameters", () => { 313 | it("basic", (ctx) => { 314 | const wrapper = mount(PrismicImage, { 315 | props: { 316 | field: ctx.mock.value.image(), 317 | imgixParams: { sat: 100 }, 318 | }, 319 | }) 320 | 321 | expect(wrapper.html()).toMatchInlineSnapshot( 322 | `"Duis convallis convallis tellus id interdum velit laoreet id donec"`, 323 | ) 324 | }) 325 | 326 | it("allows removing existing Imgix params via the imgixParams prop", (ctx) => { 327 | const field = ctx.mock.value.image({ 328 | model: ctx.mock.model.image(), 329 | }) 330 | const fieldURL = new URL(field.url) 331 | fieldURL.searchParams.set("auto", "compress,format") 332 | fieldURL.searchParams.set("sat", "-100") 333 | fieldURL.searchParams.set("ar", "1:2") 334 | field.url = fieldURL.toString() 335 | 336 | const wrapper = mount(PrismicImage, { 337 | props: { 338 | field, 339 | imgixParams: { auto: undefined, sat: undefined }, 340 | }, 341 | }) 342 | 343 | expect(wrapper.html()).toMatchInlineSnapshot( 344 | `"Donec ac odio tempor orci dapibus ultrices iaculis nunc sed augue"`, 345 | ) 346 | }) 347 | }) 348 | 349 | it("reacts to changes properly", async (ctx) => { 350 | const wrapper = mount(PrismicImage, { 351 | props: { field: ctx.mock.value.image() }, 352 | }) 353 | 354 | const firstRender = wrapper.html() 355 | 356 | await wrapper.setProps({ 357 | field: ctx.mock.value.image(), 358 | }) 359 | 360 | const secondRender = wrapper.html() 361 | 362 | expect(secondRender).not.toBe(firstRender) 363 | expect(secondRender).toMatchInlineSnapshot( 364 | `"Enim sit amet venenatis urna cursus eget nunc scelerisque viverra"`, 365 | ) 366 | }) 367 | -------------------------------------------------------------------------------- /test/__fixtures__/enRichText.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "heading1", 4 | "text": "Lorem ipsum dolor sit amet.", 5 | "spans": [] 6 | }, 7 | { 8 | "type": "heading2", 9 | "text": "Lorem ipsum dolor sit amet.", 10 | "spans": [] 11 | }, 12 | { 13 | "type": "heading3", 14 | "text": "Lorem ipsum dolor sit amet.", 15 | "spans": [] 16 | }, 17 | { 18 | "type": "heading4", 19 | "text": "Lorem ipsum dolor sit amet.", 20 | "spans": [] 21 | }, 22 | { 23 | "type": "heading5", 24 | "text": "Lorem ipsum dolor sit amet.", 25 | "spans": [] 26 | }, 27 | { 28 | "type": "heading6", 29 | "text": "Lorem ipsum dolor sit amet.", 30 | "spans": [] 31 | }, 32 | { 33 | "type": "paragraph", 34 | "text": "Lorem ipsum dolor sit amet.", 35 | "spans": [] 36 | }, 37 | { 38 | "type": "paragraph", 39 | "text": "", 40 | "spans": [] 41 | }, 42 | { 43 | "type": "heading1", 44 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 45 | "spans": [ 46 | { 47 | "start": 18, 48 | "end": 26, 49 | "type": "strong" 50 | }, 51 | { 52 | "start": 40, 53 | "end": 55, 54 | "type": "em" 55 | }, 56 | { 57 | "start": 110, 58 | "end": 122, 59 | "type": "label", 60 | "data": { 61 | "label": "label" 62 | } 63 | }, 64 | { 65 | "start": 213, 66 | "end": 230, 67 | "type": "hyperlink", 68 | "data": { 69 | "link_type": "Web", 70 | "url": "https://google.com" 71 | } 72 | }, 73 | { 74 | "start": 319, 75 | "end": 333, 76 | "type": "hyperlink", 77 | "data": { 78 | "id": "XvoFFREAAM0WGBng", 79 | "type": "page", 80 | "tags": ["test"], 81 | "slug": "have-you-heard-of-that-bird-puffin", 82 | "lang": "en-us", 83 | "uid": "home", 84 | "link_type": "Document", 85 | "isBroken": false 86 | } 87 | }, 88 | { 89 | "start": 369, 90 | "end": 381, 91 | "type": "hyperlink", 92 | "data": { 93 | "link_type": "Media", 94 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 95 | "kind": "image", 96 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 97 | "size": "252237", 98 | "height": "1602", 99 | "width": "2400" 100 | } 101 | } 102 | ] 103 | }, 104 | { 105 | "type": "paragraph", 106 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 107 | "spans": [ 108 | { 109 | "start": 18, 110 | "end": 26, 111 | "type": "strong" 112 | }, 113 | { 114 | "start": 40, 115 | "end": 55, 116 | "type": "em" 117 | }, 118 | { 119 | "start": 110, 120 | "end": 122, 121 | "type": "label", 122 | "data": { 123 | "label": "testLabel" 124 | } 125 | }, 126 | { 127 | "start": 213, 128 | "end": 230, 129 | "type": "hyperlink", 130 | "data": { 131 | "link_type": "Web", 132 | "url": "https://google.com" 133 | } 134 | }, 135 | { 136 | "start": 319, 137 | "end": 333, 138 | "type": "hyperlink", 139 | "data": { 140 | "id": "XvoFFREAAM0WGBng", 141 | "type": "page", 142 | "tags": ["test"], 143 | "slug": "have-you-heard-of-that-bird-puffin", 144 | "lang": "en-us", 145 | "uid": "home", 146 | "link_type": "Document", 147 | "isBroken": false 148 | } 149 | }, 150 | { 151 | "start": 369, 152 | "end": 381, 153 | "type": "hyperlink", 154 | "data": { 155 | "link_type": "Media", 156 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 157 | "kind": "image", 158 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 159 | "size": "252237", 160 | "height": "1602", 161 | "width": "2400" 162 | } 163 | } 164 | ] 165 | }, 166 | { 167 | "type": "o-list-item", 168 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 169 | "spans": [ 170 | { 171 | "start": 18, 172 | "end": 26, 173 | "type": "strong" 174 | }, 175 | { 176 | "start": 40, 177 | "end": 55, 178 | "type": "em" 179 | }, 180 | { 181 | "start": 110, 182 | "end": 122, 183 | "type": "label", 184 | "data": { 185 | "label": "label" 186 | } 187 | }, 188 | { 189 | "start": 213, 190 | "end": 230, 191 | "type": "hyperlink", 192 | "data": { 193 | "link_type": "Web", 194 | "url": "https://google.com" 195 | } 196 | }, 197 | { 198 | "start": 319, 199 | "end": 333, 200 | "type": "hyperlink", 201 | "data": { 202 | "id": "XvoFFREAAM0WGBng", 203 | "type": "page", 204 | "tags": ["test"], 205 | "slug": "have-you-heard-of-that-bird-puffin", 206 | "lang": "en-us", 207 | "uid": "home", 208 | "link_type": "Document", 209 | "isBroken": false 210 | } 211 | }, 212 | { 213 | "start": 369, 214 | "end": 381, 215 | "type": "hyperlink", 216 | "data": { 217 | "link_type": "Media", 218 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 219 | "kind": "image", 220 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 221 | "size": "252237", 222 | "height": "1602", 223 | "width": "2400" 224 | } 225 | } 226 | ] 227 | }, 228 | { 229 | "type": "o-list-item", 230 | "text": "Lorem ipsum dolor sit amet.", 231 | "spans": [] 232 | }, 233 | { 234 | "type": "o-list-item", 235 | "text": "Lorem ipsum dolor sit amet.", 236 | "spans": [] 237 | }, 238 | { 239 | "type": "list-item", 240 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 241 | "spans": [ 242 | { 243 | "start": 18, 244 | "end": 26, 245 | "type": "strong" 246 | }, 247 | { 248 | "start": 40, 249 | "end": 55, 250 | "type": "em" 251 | }, 252 | { 253 | "start": 110, 254 | "end": 122, 255 | "type": "label", 256 | "data": { 257 | "label": "label" 258 | } 259 | }, 260 | { 261 | "start": 213, 262 | "end": 230, 263 | "type": "hyperlink", 264 | "data": { 265 | "link_type": "Web", 266 | "url": "https://google.com" 267 | } 268 | }, 269 | { 270 | "start": 319, 271 | "end": 333, 272 | "type": "hyperlink", 273 | "data": { 274 | "id": "XvoFFREAAM0WGBng", 275 | "type": "page", 276 | "tags": ["test"], 277 | "slug": "have-you-heard-of-that-bird-puffin", 278 | "lang": "en-us", 279 | "uid": "home", 280 | "link_type": "Document", 281 | "isBroken": false 282 | } 283 | }, 284 | { 285 | "start": 369, 286 | "end": 381, 287 | "type": "hyperlink", 288 | "data": { 289 | "link_type": "Media", 290 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 291 | "kind": "image", 292 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 293 | "size": "252237", 294 | "height": "1602", 295 | "width": "2400" 296 | } 297 | } 298 | ] 299 | }, 300 | { 301 | "type": "list-item", 302 | "text": "Lorem ipsum dolor sit amet.", 303 | "spans": [] 304 | }, 305 | { 306 | "type": "list-item", 307 | "text": "Lorem ipsum dolor sit amet.", 308 | "spans": [] 309 | }, 310 | { 311 | "type": "paragraph", 312 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 313 | "spans": [ 314 | { 315 | "start": 18, 316 | "end": 26, 317 | "type": "strong" 318 | }, 319 | { 320 | "start": 40, 321 | "end": 55, 322 | "type": "em" 323 | }, 324 | { 325 | "start": 110, 326 | "end": 122, 327 | "type": "label", 328 | "data": { 329 | "label": "label" 330 | } 331 | }, 332 | { 333 | "start": 213, 334 | "end": 230, 335 | "type": "hyperlink", 336 | "data": { 337 | "link_type": "Web", 338 | "url": "https://google.com" 339 | } 340 | }, 341 | { 342 | "start": 319, 343 | "end": 333, 344 | "type": "hyperlink", 345 | "data": { 346 | "id": "XvoFFREAAM0WGBng", 347 | "type": "page", 348 | "tags": ["test"], 349 | "slug": "have-you-heard-of-that-bird-puffin", 350 | "lang": "en-us", 351 | "uid": "home", 352 | "link_type": "Document", 353 | "isBroken": false 354 | } 355 | }, 356 | { 357 | "start": 369, 358 | "end": 381, 359 | "type": "hyperlink", 360 | "data": { 361 | "link_type": "Media", 362 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 363 | "kind": "image", 364 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 365 | "size": "252237", 366 | "height": "1602", 367 | "width": "2400" 368 | } 369 | } 370 | ], 371 | "direction": "rtl" 372 | }, 373 | { 374 | "type": "paragraph", 375 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 376 | "spans": [ 377 | { 378 | "start": 18, 379 | "end": 26, 380 | "type": "strong" 381 | }, 382 | { 383 | "start": 40, 384 | "end": 55, 385 | "type": "em" 386 | }, 387 | { 388 | "start": 110, 389 | "end": 122, 390 | "type": "label", 391 | "data": { 392 | "label": "label" 393 | } 394 | }, 395 | { 396 | "start": 213, 397 | "end": 230, 398 | "type": "hyperlink", 399 | "data": { 400 | "link_type": "Web", 401 | "url": "https://google.com" 402 | } 403 | }, 404 | { 405 | "start": 319, 406 | "end": 333, 407 | "type": "hyperlink", 408 | "data": { 409 | "id": "XvoFFREAAM0WGBng", 410 | "type": "page", 411 | "tags": ["test"], 412 | "slug": "have-you-heard-of-that-bird-puffin", 413 | "lang": "en-us", 414 | "uid": "home", 415 | "link_type": "Document", 416 | "isBroken": false 417 | } 418 | }, 419 | { 420 | "start": 369, 421 | "end": 381, 422 | "type": "hyperlink", 423 | "data": { 424 | "link_type": "Media", 425 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 426 | "kind": "image", 427 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 428 | "size": "252237", 429 | "height": "1602", 430 | "width": "2400" 431 | } 432 | }, 433 | { 434 | "start": 406, 435 | "end": 445, 436 | "type": "label", 437 | "data": { 438 | "label": "testLabel" 439 | } 440 | } 441 | ] 442 | }, 443 | { 444 | "type": "image", 445 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 446 | "alt": "An Atlantic Puffin", 447 | "copyright": null, 448 | "dimensions": { 449 | "width": 2400, 450 | "height": 1602 451 | } 452 | }, 453 | { 454 | "type": "image", 455 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 456 | "alt": "An Atlantic Puffin", 457 | "copyright": "Unsplash", 458 | "dimensions": { 459 | "width": 2400, 460 | "height": 1602 461 | } 462 | }, 463 | { 464 | "type": "paragraph", 465 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 466 | "spans": [ 467 | { 468 | "start": 0, 469 | "end": 36, 470 | "type": "label", 471 | "data": { 472 | "label": "testLabel" 473 | } 474 | }, 475 | { 476 | "start": 18, 477 | "end": 26, 478 | "type": "strong" 479 | }, 480 | { 481 | "start": 40, 482 | "end": 55, 483 | "type": "em" 484 | }, 485 | { 486 | "start": 110, 487 | "end": 122, 488 | "type": "label", 489 | "data": { 490 | "label": "label" 491 | } 492 | }, 493 | { 494 | "start": 213, 495 | "end": 230, 496 | "type": "hyperlink", 497 | "data": { 498 | "link_type": "Web", 499 | "url": "https://google.com" 500 | } 501 | }, 502 | { 503 | "start": 319, 504 | "end": 333, 505 | "type": "hyperlink", 506 | "data": { 507 | "id": "XvoFFREAAM0WGBng", 508 | "type": "page", 509 | "tags": ["test"], 510 | "slug": "have-you-heard-of-that-bird-puffin", 511 | "lang": "en-us", 512 | "uid": "home", 513 | "link_type": "Document", 514 | "isBroken": false 515 | } 516 | }, 517 | { 518 | "start": 369, 519 | "end": 381, 520 | "type": "hyperlink", 521 | "data": { 522 | "link_type": "Media", 523 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 524 | "kind": "image", 525 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 526 | "size": "252237", 527 | "height": "1602", 528 | "width": "2400" 529 | } 530 | } 531 | ] 532 | }, 533 | { 534 | "type": "image", 535 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 536 | "alt": "An Atlantic Puffin", 537 | "copyright": null, 538 | "dimensions": { 539 | "width": 2400, 540 | "height": 1602 541 | } 542 | }, 543 | { 544 | "type": "paragraph", 545 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 546 | "spans": [ 547 | { 548 | "start": 0, 549 | "end": 36, 550 | "type": "label", 551 | "data": { 552 | "label": "label" 553 | } 554 | }, 555 | { 556 | "start": 18, 557 | "end": 26, 558 | "type": "strong" 559 | }, 560 | { 561 | "start": 40, 562 | "end": 55, 563 | "type": "em" 564 | }, 565 | { 566 | "start": 110, 567 | "end": 122, 568 | "type": "label", 569 | "data": { 570 | "label": "label" 571 | } 572 | }, 573 | { 574 | "start": 213, 575 | "end": 230, 576 | "type": "hyperlink", 577 | "data": { 578 | "link_type": "Web", 579 | "url": "https://google.com" 580 | } 581 | }, 582 | { 583 | "start": 319, 584 | "end": 333, 585 | "type": "hyperlink", 586 | "data": { 587 | "id": "XvoFFREAAM0WGBng", 588 | "type": "page", 589 | "tags": ["test"], 590 | "slug": "have-you-heard-of-that-bird-puffin", 591 | "lang": "en-us", 592 | "uid": "home", 593 | "link_type": "Document", 594 | "isBroken": false 595 | } 596 | }, 597 | { 598 | "start": 369, 599 | "end": 381, 600 | "type": "hyperlink", 601 | "data": { 602 | "link_type": "Media", 603 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 604 | "kind": "image", 605 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 606 | "size": "252237", 607 | "height": "1602", 608 | "width": "2400" 609 | } 610 | } 611 | ] 612 | }, 613 | { 614 | "type": "preformatted", 615 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 616 | "spans": [] 617 | }, 618 | { 619 | "type": "paragraph", 620 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 621 | "spans": [ 622 | { 623 | "start": 0, 624 | "end": 36, 625 | "type": "label", 626 | "data": { 627 | "label": "label" 628 | } 629 | }, 630 | { 631 | "start": 18, 632 | "end": 26, 633 | "type": "strong" 634 | }, 635 | { 636 | "start": 40, 637 | "end": 55, 638 | "type": "em" 639 | }, 640 | { 641 | "start": 110, 642 | "end": 122, 643 | "type": "label", 644 | "data": { 645 | "label": "label" 646 | } 647 | }, 648 | { 649 | "start": 213, 650 | "end": 230, 651 | "type": "hyperlink", 652 | "data": { 653 | "link_type": "Web", 654 | "url": "https://google.com" 655 | } 656 | }, 657 | { 658 | "start": 319, 659 | "end": 333, 660 | "type": "hyperlink", 661 | "data": { 662 | "id": "XvoFFREAAM0WGBng", 663 | "type": "page", 664 | "tags": ["test"], 665 | "slug": "have-you-heard-of-that-bird-puffin", 666 | "lang": "en-us", 667 | "uid": "home", 668 | "link_type": "Document", 669 | "isBroken": false 670 | } 671 | }, 672 | { 673 | "start": 369, 674 | "end": 381, 675 | "type": "hyperlink", 676 | "data": { 677 | "link_type": "Media", 678 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 679 | "kind": "image", 680 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 681 | "size": "252237", 682 | "height": "1602", 683 | "width": "2400" 684 | } 685 | } 686 | ] 687 | }, 688 | { 689 | "type": "embed", 690 | "oembed": { 691 | "type": "video", 692 | "embed_url": "https://www.youtube.com/watch?v=n5CwXuyNfoc", 693 | "title": "[MV] REOL - ギミアブレスタッナウ/ Give me a break Stop now", 694 | "provider_name": "YouTube", 695 | "thumbnail_url": "https://i.ytimg.com/vi/n5CwXuyNfoc/hqdefault.jpg", 696 | "provider_url": "https://www.youtube.com/", 697 | "author_name": "Reol Official", 698 | "author_url": "https://www.youtube.com/user/reolch", 699 | "height": 113, 700 | "width": 200, 701 | "version": "1.0", 702 | "thumbnail_height": 360, 703 | "thumbnail_width": 480, 704 | "html": "" 705 | } 706 | }, 707 | { 708 | "type": "paragraph", 709 | "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 710 | "spans": [ 711 | { 712 | "start": 0, 713 | "end": 36, 714 | "type": "label", 715 | "data": { 716 | "label": "label" 717 | } 718 | }, 719 | { 720 | "start": 18, 721 | "end": 26, 722 | "type": "strong" 723 | }, 724 | { 725 | "start": 40, 726 | "end": 55, 727 | "type": "em" 728 | }, 729 | { 730 | "start": 110, 731 | "end": 122, 732 | "type": "label", 733 | "data": { 734 | "label": "label" 735 | } 736 | }, 737 | { 738 | "start": 213, 739 | "end": 230, 740 | "type": "hyperlink", 741 | "data": { 742 | "link_type": "Web", 743 | "url": "https://google.com" 744 | } 745 | }, 746 | { 747 | "start": 319, 748 | "end": 333, 749 | "type": "hyperlink", 750 | "data": { 751 | "id": "XvoFFREAAM0WGBng", 752 | "type": "page", 753 | "tags": ["test"], 754 | "slug": "have-you-heard-of-that-bird-puffin", 755 | "lang": "en-us", 756 | "uid": "home", 757 | "link_type": "Document", 758 | "isBroken": false 759 | } 760 | }, 761 | { 762 | "start": 369, 763 | "end": 381, 764 | "type": "hyperlink", 765 | "data": { 766 | "link_type": "Media", 767 | "name": "guilherme-romano-KI2KaOeT670-unsplash.jpg", 768 | "kind": "image", 769 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 770 | "size": "252237", 771 | "height": "1602", 772 | "width": "2400" 773 | } 774 | } 775 | ] 776 | }, 777 | { 778 | "type": "image", 779 | "url": "https://images.prismic.io/200629-sms-hoy/f0a757f6-770d-4eb8-a08b-f1727f1a58e4_guilherme-romano-KI2KaOeT670-unsplash.jpg?auto=compress,format", 780 | "alt": "An Atlantic Puffin", 781 | "copyright": "Unsplash", 782 | "dimensions": { 783 | "width": 2400, 784 | "height": 1602 785 | }, 786 | "linkTo": { 787 | "link_type": "Web", 788 | "url": "https://google.com", 789 | "target": "_blank" 790 | } 791 | } 792 | ] 793 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [5.3.0](https://github.com/prismicio/prismic-vue/compare/v5.2.1...v5.3.0) (2025-07-16) 6 | 7 | 8 | ### Features 9 | 10 | * cast width and height on images ([#84](https://github.com/prismicio/prismic-vue/issues/84)) ([ca4de6f](https://github.com/prismicio/prismic-vue/commit/ca4de6f074be593cdbcaaee92b18173c8dfb7c51)) 11 | 12 | 13 | ### Documentation 14 | 15 | * update contributing guide ([#81](https://github.com/prismicio/prismic-vue/issues/81)) ([c724265](https://github.com/prismicio/prismic-vue/commit/c724265a8ccad68d7e472631704e2ce131626402)) 16 | * update README.md ([36c5f70](https://github.com/prismicio/prismic-vue/commit/36c5f701b8f576f9f0b9de0c1ffb4d5997db4b93)) 17 | 18 | ### [5.2.1](https://github.com/prismicio/prismic-vue/compare/v5.2.0...v5.2.1) (2025-03-31) 19 | 20 | 21 | ### Bug Fixes 22 | 23 | * complex props declaration on `` ([72015ce](https://github.com/prismicio/prismic-vue/commit/72015ce7ee161257b49489ae0181e9922d421685)) 24 | 25 | 26 | ### Chore 27 | 28 | * **deps:** maintain dependencies ([f637d09](https://github.com/prismicio/prismic-vue/commit/f637d0970343569e3b807f949e70a1d62db92586)) 29 | * **deps:** maintain lockfile ([a095f5a](https://github.com/prismicio/prismic-vue/commit/a095f5a3d65b9f2c1408654f96e5dd97d859ed14)) 30 | 31 | ## [5.2.0](https://github.com/prismicio/prismic-vue/compare/v5.1.1...v5.2.0) (2025-03-19) 32 | 33 | 34 | ### Features 35 | 36 | * support key for table field content ([#78](https://github.com/prismicio/prismic-vue/issues/78)) ([c9c29a0](https://github.com/prismicio/prismic-vue/commit/c9c29a0fbb640c19fa915bd55e3ca5d829f12d19)) 37 | 38 | ### [5.1.1](https://github.com/prismicio/prismic-vue/compare/v5.1.0...v5.1.1) (2025-03-11) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * **rich-text:** apply listeners on mounted ([e1a83a1](https://github.com/prismicio/prismic-vue/commit/e1a83a11675dbb806576719545b0bc0e30707e7e)) 44 | 45 | ## [5.1.0](https://github.com/prismicio/prismic-vue/compare/v5.0.3...v5.1.0) (2025-02-28) 46 | 47 | 48 | ### Features 49 | 50 | * add PrismicTable component ([da69384](https://github.com/prismicio/prismic-vue/commit/da69384a347ad711bce19194b373c4a64dc8b013)) 51 | * add PrismicTable component ([59c2621](https://github.com/prismicio/prismic-vue/commit/59c26212de96c4c696ecaec0f439df8dae9f95b1)) 52 | * inject PrismicTable globally ([7d1ab1e](https://github.com/prismicio/prismic-vue/commit/7d1ab1e96a19954db24b482911b868cb768aa5c4)) 53 | 54 | 55 | ### Bug Fixes 56 | 57 | * default table row components ([08395e4](https://github.com/prismicio/prismic-vue/commit/08395e49f261cc5e8bec6064423dc46e58d55d4c)) 58 | 59 | 60 | ### Documentation 61 | 62 | * typo on rich text example ([d527b58](https://github.com/prismicio/prismic-vue/commit/d527b5861d9edae089a493184ce0a4050e68fc73)) 63 | 64 | 65 | ### Chore 66 | 67 | * update deps ([f9eb69f](https://github.com/prismicio/prismic-vue/commit/f9eb69f2f3631facd7206fcd180cf61a144e674a)) 68 | 69 | 70 | ### Refactor 71 | 72 | * extract default components ([d3087a9](https://github.com/prismicio/prismic-vue/commit/d3087a96138b9c203de8fc4cd06e4b8b21a03323)) 73 | * introduce `getTableComponentProps` for consistency ([eb58734](https://github.com/prismicio/prismic-vue/commit/eb587349b30c746d3ebb0071215ab5e0ee52c7d5)) 74 | * introduce `getTableComponentProps` for consistency ([be52602](https://github.com/prismicio/prismic-vue/commit/be5260234a315102ff8be7cb59e2b552b1d20b9b)) 75 | * misc PR changes ([bb205d3](https://github.com/prismicio/prismic-vue/commit/bb205d34206b662f6dd846b07ef77517b0f4fb8f)) 76 | * simplify types ([2904bee](https://github.com/prismicio/prismic-vue/commit/2904beef84add8ca1411f65f489164a0b3e902f5)) 77 | 78 | ### [5.0.3](https://github.com/prismicio/prismic-vue/compare/v5.0.2...v5.0.3) (2025-02-10) 79 | 80 | 81 | ### Documentation 82 | 83 | * typo on rich text example ([77ee28e](https://github.com/prismicio/prismic-vue/commit/77ee28e23f413643b3fe860f1ae67a78914d459e)) 84 | 85 | ### [5.0.2](https://github.com/prismicio/prismic-vue/compare/v5.0.1...v5.0.2) (2025-02-10) 86 | 87 | 88 | ### Bug Fixes 89 | 90 | * message typo ([ce65610](https://github.com/prismicio/prismic-vue/commit/ce65610ab0d505a942ee76635fe3b982aa289eb1)) 91 | * message typo ([16fe16a](https://github.com/prismicio/prismic-vue/commit/16fe16aa4ac200418ff6d29cb21a668830aebade)) 92 | 93 | ### [5.0.1](https://github.com/prismicio/prismic-vue/compare/v5.0.0...v5.0.1) (2025-01-23) 94 | 95 | 96 | ### Bug Fixes 97 | 98 | * remove link warnings for graphql ([6d54ca8](https://github.com/prismicio/prismic-vue/commit/6d54ca8b39d95220da6d3a9d1c1232cc7e6e0cf2)) 99 | 100 | ## [5.0.0](https://github.com/prismicio/prismic-vue/compare/v5.0.0-alpha.1...v5.0.0) (2025-01-20) 101 | 102 | 103 | ### Chore 104 | 105 | * **deps:** maintain dependencies ([d7c8950](https://github.com/prismicio/prismic-vue/commit/d7c8950dd93e1351c8f0b41b1147950d444592b8)) 106 | * **deps:** maintain lock file ([43f896b](https://github.com/prismicio/prismic-vue/commit/43f896b1997477e7efc3aaaf299843dbb7410cc1)) 107 | 108 | 109 | ### Documentation 110 | 111 | * fix changelog ([27693ce](https://github.com/prismicio/prismic-vue/commit/27693cecaecc6b152a5deafc94c1fe5b25de18e3)) 112 | * wording ([53d3a03](https://github.com/prismicio/prismic-vue/commit/53d3a031807a4628535ac83ae84e34e31618de3f)) 113 | 114 | ### [4.3.1](https://github.com/prismicio/prismic-vue/compare/v5.0.0-alpha.0...v4.3.1) (2025-01-02) 115 | 116 | 117 | ### Bug Fixes 118 | 119 | * resolve slots as `VNode[] | Slots | Slot` ([258bea1](https://github.com/prismicio/prismic-vue/commit/258bea14ba0dc524d82ce29ac23412379be282ed)) 120 | 121 | 122 | ### Chore 123 | 124 | * **release:** 4.3.1 ([c1ba125](https://github.com/prismicio/prismic-vue/commit/c1ba1251b04ac89b92110e60230d8187134f1d70)) 125 | 126 | ## [5.0.0-alpha.1](https://github.com/prismicio/prismic-vue/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2025-01-14) 127 | 128 | 129 | ### Bug Fixes 130 | 131 | * ``'s `slices` prop type ([65bf265](https://github.com/prismicio/prismic-vue/commit/65bf2659dfe7d67a4a18efde467392cbcb153fae)) 132 | 133 | 134 | ### Documentation 135 | 136 | * tsdocs ([c354e8b](https://github.com/prismicio/prismic-vue/commit/c354e8bff53ad667cd3f207c26f93fe3a82fd28b)) 137 | * wording ([bb5aa2d](https://github.com/prismicio/prismic-vue/commit/bb5aa2d8a560871d7c28c6ff75382afefbfac2b6)) 138 | 139 | 140 | ### Chore 141 | 142 | * **deps:** maintain dependencies ([3abf7ac](https://github.com/prismicio/prismic-vue/commit/3abf7aca03ff7d41154fd0511b51bd9bfdd14792)) 143 | 144 | ## [5.0.0-alpha.0](https://github.com/prismicio/prismic-vue/compare/v4.3.0...v5.0.0-alpha.0) (2025-01-02) 145 | 146 | 147 | ### ⚠ BREAKING CHANGES 148 | 149 | * 150 | * 151 | * remove vetur definitions in favor of TypeScript docs 152 | * 153 | * remove query-related composables 154 | * 155 | * 156 | * 157 | * remove `isomorphic-unfetch` fallback 158 | 159 | ### Features 160 | 161 | * component-based serializer ([8e15571](https://github.com/prismicio/prismic-vue/commit/8e15571f121653fbfa840f821526d08e047aa3bf)) 162 | * remove `isomorphic-unfetch` fallback ([e889af1](https://github.com/prismicio/prismic-vue/commit/e889af1e19a9f9995c1095c6163dec713f8f3736)) 163 | * remove query-related composables ([6c14d14](https://github.com/prismicio/prismic-vue/commit/6c14d14d4145e3ab6c4dc33790c8f0386095312e)) 164 | * remove vetur definitions in favor of TypeScript docs ([b5881f2](https://github.com/prismicio/prismic-vue/commit/b5881f2706d94243934efc06cf09c0498cd6bf07)) 165 | 166 | 167 | ### Bug Fixes 168 | 169 | * forwards attributes to `` ([b179f9d](https://github.com/prismicio/prismic-vue/commit/b179f9d9f39269e77834be44de3337b96b0e5253)) 170 | 171 | 172 | ### Chore 173 | 174 | * **deps:** update to Vite 6 and ESLint 9 ([560cffa](https://github.com/prismicio/prismic-vue/commit/560cffa788c3869b518e4135f822c1f757193bb6)) 175 | 176 | 177 | ### Refactor 178 | 179 | * ([a9a43a3](https://github.com/prismicio/prismic-vue/commit/a9a43a3029aee945af922468a2880647a41363d9)) 180 | * ([34f121a](https://github.com/prismicio/prismic-vue/commit/34f121a96e6db9a16fa637a0f96ac1d3b431e4ab)) 181 | * ([de2cb68](https://github.com/prismicio/prismic-vue/commit/de2cb68b571c1085fe6660563992b758f449664c)) 182 | * ([61721ff](https://github.com/prismicio/prismic-vue/commit/61721ff5741a17d4fbd78ee7b03814a9250839f4)) 183 | * ([a4cc1a5](https://github.com/prismicio/prismic-vue/commit/a4cc1a5b88e993cb770bb5180514fcbd3bbe7381)) 184 | * ([3325e86](https://github.com/prismicio/prismic-vue/commit/3325e86394ec43be6d518d3ca980371f3c76f858)) 185 | * migrate to `esm-env` ([1a9b3a7](https://github.com/prismicio/prismic-vue/commit/1a9b3a7cd71d20269fcad1969baf59aebcc2de61)) 186 | * Prismic Vue plugin ([dc33283](https://github.com/prismicio/prismic-vue/commit/dc33283be9d9355cdff64f66f176935677405e5c)) 187 | * simplify project structure ([8c59d56](https://github.com/prismicio/prismic-vue/commit/8c59d5685fbfd32926f6204cb73fa5233649546f)) 188 | * simplify project structure ([0cb1ca7](https://github.com/prismicio/prismic-vue/commit/0cb1ca73880b7907cf20bd0c11cb79af937ac3cd)) 189 | * test layout ([36bb64b](https://github.com/prismicio/prismic-vue/commit/36bb64b51136f817f9e5bbc24391e568a714fd42)) 190 | * use `` component ([b038101](https://github.com/prismicio/prismic-vue/commit/b038101368a8751d7c04c7af4e3c03cee856b929)) 191 | 192 | 193 | ### Documentation 194 | 195 | * pull dev messages ([48bc62e](https://github.com/prismicio/prismic-vue/commit/48bc62e53111ac79b47785ab67f6e7c6bc42e36f)) 196 | * update contributing guide ([5445d46](https://github.com/prismicio/prismic-vue/commit/5445d464e1888bd85786adf56a9fd7c62987403e)) 197 | * wording ([55ee4c7](https://github.com/prismicio/prismic-vue/commit/55ee4c71d2d789d4ed938d6a92244db35dc88cda)) 198 | 199 | 200 | ## [4.3.0](https://github.com/prismicio/prismic-vue/compare/v4.2.3...v4.3.0) (2024-09-27) 201 | 202 | 203 | ### Features 204 | 205 | * expose text to default slot ([0fbd7b4](https://github.com/prismicio/prismic-vue/commit/0fbd7b474edc0cf6295fb5ed1e2744c874b8f3bd)) 206 | * support link text ([24e6464](https://github.com/prismicio/prismic-vue/commit/24e6464b016b47c107a93454028aa813de093bb9)) 207 | 208 | ### [4.2.3](https://github.com/prismicio/prismic-vue/compare/v4.2.2...v4.2.3) (2024-09-16) 209 | 210 | 211 | ### Bug Fixes 212 | 213 | * augment `vue` rather than `@vue/runtime-core` ([#71](https://github.com/prismicio/prismic-vue/issues/71)) ([fc66c4b](https://github.com/prismicio/prismic-vue/commit/fc66c4ba20472acf3884fb7174b9519e2d664c7b)) 214 | 215 | ### [4.2.2](https://github.com/prismicio/prismic-vue/compare/v4.2.1...v4.2.2) (2024-04-25) 216 | 217 | 218 | ### Bug Fixes 219 | 220 | * ``'s `slices` type issue ([f88c6b4](https://github.com/prismicio/prismic-vue/commit/f88c6b460d2c12dcce6ea437c8a8d84f08eb3abc)) 221 | 222 | ### [4.2.1](https://github.com/prismicio/prismic-vue/compare/v4.2.0...v4.2.1) (2024-03-28) 223 | 224 | 225 | ### Bug Fixes 226 | 227 | * display slice type for mapped slices in todo component ([95d5771](https://github.com/prismicio/prismic-vue/commit/95d5771b51dc45df5bd6663178afceae8942dc4c)) 228 | 229 | 230 | ### Refactor 231 | 232 | * `` ([#70](https://github.com/prismicio/prismic-vue/issues/70)) ([436834a](https://github.com/prismicio/prismic-vue/commit/436834a233acaefcb595d5e282862613439f9900)) 233 | 234 | ## [4.2.0](https://github.com/prismicio/prismic-vue/compare/v4.1.0...v4.2.0) (2024-03-27) 235 | 236 | 237 | ### Features 238 | 239 | * support @prismicio/client `mapSliceZone` ([528581d](https://github.com/prismicio/prismic-vue/commit/528581dd0fda06a84ff048b64689f5c187532724)) 240 | 241 | 242 | ### Chore 243 | 244 | * **deps:** maintain dependencies ([581ea59](https://github.com/prismicio/prismic-vue/commit/581ea59c927f24ff6480e8117d8bf94c30004121)) 245 | * typescript compat ([9d7d481](https://github.com/prismicio/prismic-vue/commit/9d7d4816a15cb4452a9f6d79a1e88c17c085acb0)) 246 | 247 | ## [4.1.0](https://github.com/prismicio/prismic-vue/compare/v4.0.3...v4.1.0) (2023-09-26) 248 | 249 | 250 | ### Features 251 | 252 | * export `isFilled` object to helpers ([e51adf2](https://github.com/prismicio/prismic-vue/commit/e51adf27cf0c90f145d7c1b4d2a953ad3d46468e)) 253 | 254 | ### [4.0.3](https://github.com/prismicio/prismic-vue/compare/v4.0.2...v4.0.3) (2023-08-30) 255 | 256 | 257 | ### Bug Fixes 258 | 259 | * looser slice component type ([61c87e3](https://github.com/prismicio/prismic-vue/commit/61c87e3c43811529cb5ceada953b43e6525a0881)) 260 | 261 | 262 | ### Chore 263 | 264 | * **deps:** maintain dependencies ([49a2e4f](https://github.com/prismicio/prismic-vue/commit/49a2e4fae4cc6cd44d1fce7f98e0b0eb2f93cfec)) 265 | 266 | ### [4.0.2](https://github.com/prismicio/prismic-vue/compare/v4.0.1...v4.0.2) (2023-07-06) 267 | 268 | 269 | ### Chore 270 | 271 | * **deps:** maintain dependencies ([7dd808f](https://github.com/prismicio/prismic-vue/commit/7dd808fc0c867ad8548461ac083447e71d1ed386)) 272 | 273 | 274 | ### Documentation 275 | 276 | * tsdoc `[@example](https://github.com/example)` code snippet format ([#62](https://github.com/prismicio/prismic-vue/issues/62), [#67](https://github.com/prismicio/prismic-vue/issues/67)) ([eeaddc4](https://github.com/prismicio/prismic-vue/commit/eeaddc42a258e63c415464a3c0e98944fe551daf)) 277 | 278 | ### [4.0.1](https://github.com/prismicio/prismic-vue/compare/v4.0.0...v4.0.1) (2023-06-09) 279 | 280 | 281 | ### Bug Fixes 282 | 283 | * use config object serializer properly with `asHTML` ([dfc0558](https://github.com/prismicio/prismic-vue/commit/dfc0558df0746e6a8fcd847b1d646136f12c31ab)) 284 | 285 | ## [4.0.0](https://github.com/prismicio/prismic-vue/compare/v3.2.1...v4.0.0) (2023-06-02) 286 | 287 | 288 | ### ⚠ BREAKING CHANGES 289 | 290 | * update to client v7 (#64) 291 | 292 | ### Features 293 | 294 | * update to client v7 ([#64](https://github.com/prismicio/prismic-vue/issues/64)) ([00a71ca](https://github.com/prismicio/prismic-vue/commit/00a71cae45f562746654e91cff2b24278ecdf25f)) 295 | 296 | ### [3.2.1](https://github.com/prismicio/prismic-vue/compare/v3.2.0...v3.2.1) (2023-05-30) 297 | 298 | 299 | ### Bug Fixes 300 | 301 | * allow client type extensions ([bb63a73](https://github.com/prismicio/prismic-vue/commit/bb63a73c9efc500f5ba79c3a979e72f5e99b1c14)) 302 | 303 | 304 | ### Chore 305 | 306 | * update project structure ([d680ecb](https://github.com/prismicio/prismic-vue/commit/d680ecb756401970f373086ee94204070e31609b)) 307 | 308 | ## [3.2.0](https://github.com/prismicio/prismic-vue/compare/v3.1.4...v3.2.0) (2023-05-16) 309 | 310 | 311 | ### Features 312 | 313 | * deprecate `htmlSerializer` in favor of `serializer` and `richTextSerializer` ([a73284e](https://github.com/prismicio/prismic-vue/commit/a73284eaafb6e17f760722893be2855458ee4cc7)) 314 | 315 | 316 | ### Chore 317 | 318 | * **deps:** maintain dependencies ([c8ad43f](https://github.com/prismicio/prismic-vue/commit/c8ad43fa86d5c562b1e45c8a25f8aa81f1b8c3f9)) 319 | 320 | ### [3.1.4](https://github.com/prismicio/prismic-vue/compare/v3.1.3...v3.1.4) (2023-04-12) 321 | 322 | 323 | ### Bug Fixes 324 | 325 | * allow looser `SliceComponentType` for TypeScript reference ([0f96b7a](https://github.com/prismicio/prismic-vue/commit/0f96b7a37fc45dfac522bbbf17a677fe2aaabe73)) 326 | 327 | 328 | ### Chore 329 | 330 | * **deps:** maintain dependencies ([b8f1c7d](https://github.com/prismicio/prismic-vue/commit/b8f1c7de1d7dd6c3c20b8384df1f76ddf727383b)) 331 | 332 | 333 | ### Documentation 334 | 335 | * update license ([64a0b0a](https://github.com/prismicio/prismic-vue/commit/64a0b0a230124df12f5987fbe1fb4d21137d6128)) 336 | 337 | ### [3.1.3](https://github.com/prismicio/prismic-vue/compare/v3.1.2...v3.1.3) (2023-03-13) 338 | 339 | 340 | ### Bug Fixes 341 | 342 | * forward `blank` and `rel` attributes to external link component ([e6d9e74](https://github.com/prismicio/prismic-vue/commit/e6d9e745e75e0a2c1dd1902eb1099e57e8d023d6)) 343 | 344 | 345 | ### Chore 346 | 347 | * **deps:** maintain dependencies ([366999c](https://github.com/prismicio/prismic-vue/commit/366999c37981971bebab24bd13b7a3d0dddaf13f)) 348 | 349 | ### [3.1.2](https://github.com/prismicio/prismic-vue/compare/v3.1.1...v3.1.2) (2022-12-19) 350 | 351 | 352 | ### Chore 353 | 354 | * **deps:** maintain dependencies ([0b012a8](https://github.com/prismicio/prismic-vue/commit/0b012a8bda8620e403e1088fd256710f78190b14)) 355 | 356 | ### [3.1.1](https://github.com/prismicio/prismic-vue/compare/v3.1.0...v3.1.1) (2022-10-05) 357 | 358 | 359 | ### Bug Fixes 360 | 361 | * `getSliceComponentProps` default generic value ([28c328f](https://github.com/prismicio/prismic-vue/commit/28c328fc9a610ed7990938f4dcaeff762b81d3f6)) 362 | 363 | 364 | ### Chore 365 | 366 | * **deps:** maintain dependencies ([ba28d73](https://github.com/prismicio/prismic-vue/commit/ba28d73ed9a380708bad725d5e8d972fc1cb8cc8)) 367 | * **deps:** use caret version ([5e253e4](https://github.com/prismicio/prismic-vue/commit/5e253e43ea3f3fc12535765dcdfc1a73911aaaaf)) 368 | 369 | ## [3.1.0](https://github.com/prismicio/prismic-vue/compare/v3.0.0...v3.1.0) (2022-08-23) 370 | 371 | 372 | ### Features 373 | 374 | * use Slice `id` property to key Slices in `` when available ([c74f188](https://github.com/prismicio/prismic-vue/commit/c74f18866ff12097b6718e3a120d16ec8ba72a12)) 375 | 376 | 377 | ### Documentation 378 | 379 | * fix changelog following merge ([93c9048](https://github.com/prismicio/prismic-vue/commit/93c9048ee6cd7858ce34a0accd6c6977536aeac9)) 380 | 381 | 382 | ### Chore 383 | 384 | * **deps:** maintain dependencies ([7976de4](https://github.com/prismicio/prismic-vue/commit/7976de4c46f00b6bc3b42fdd7391b8da39ad7684)) 385 | 386 | ## [3.0.0](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.9...v3.0.0) (2022-07-18) 387 | 388 | 389 | ### Chore 390 | 391 | * cleanup examples ([066fa21](https://github.com/prismicio/prismic-vue/commit/066fa2103080d0ccf923a95b1dc42334933a3a02)) 392 | * **deps:** maintain dependencies ([0afdce2](https://github.com/prismicio/prismic-vue/commit/0afdce2a49a82dacb340e51697d840dbd817e5ec)) 393 | * fix playground ([c5d3406](https://github.com/prismicio/prismic-vue/commit/c5d34066eccc6031ed52200e484f793af3f58d47)) 394 | 395 | 396 | ### Documentation 397 | 398 | * fix links ([767c145](https://github.com/prismicio/prismic-vue/commit/767c1457713d2301be7bddbf53467653838b83db)) 399 | 400 | ## [3.0.0-beta.9](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.8...v3.0.0-beta.9) (2022-06-15) 401 | 402 | 403 | ### Bug Fixes 404 | 405 | * reactivity issue on `` ([5c824e8](https://github.com/prismicio/prismic-vue/commit/5c824e88b51681088a4f202fa7fe7109c490fc7b)) 406 | 407 | ## [3.0.0-beta.8](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.7...v3.0.0-beta.8) (2022-06-10) 408 | 409 | 410 | ### Bug Fixes 411 | 412 | * loosen type on `` ([5a78c15](https://github.com/prismicio/prismic-vue/commit/5a78c15d55060633c44ba1f3bf021711b6ab1b96)) 413 | * prop types on `` and `` ([2709c32](https://github.com/prismicio/prismic-vue/commit/2709c32fc3c5ffca3b32ced0fa11ec382b3e15d3)) 414 | 415 | ## [3.0.0-beta.7](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.6...v3.0.0-beta.7) (2022-06-09) 416 | 417 | 418 | ### Features 419 | 420 | * add `fallback` prop to `` and `` ([689fb1f](https://github.com/prismicio/prismic-vue/commit/689fb1f8e7b4d73bc9ae70036da688431a52ad51)) 421 | * support GraphQL API in `` ([82bad5d](https://github.com/prismicio/prismic-vue/commit/82bad5df5343f75b32c8b9ba083031bae6145338)) 422 | 423 | 424 | ### Chore 425 | 426 | * **deps:** maintain dependencies ([e9f9ec7](https://github.com/prismicio/prismic-vue/commit/e9f9ec71569dc980ff174f3538e08cb00a7fccb1)) 427 | 428 | 429 | ### Refactor 430 | 431 | * warn in development mode only ([8a76d3f](https://github.com/prismicio/prismic-vue/commit/8a76d3fc4f514cb1a5773af9cfc178d8fb65b2d0)) 432 | 433 | ## [3.0.0-beta.6](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.5...v3.0.0-beta.6) (2022-04-29) 434 | 435 | 436 | ### ⚠ BREAKING CHANGES 437 | 438 | * change `auto` to `thumbnails` for consistency 439 | 440 | ### Refactor 441 | 442 | * change `auto` to `thumbnails` for consistency ([c10cb3a](https://github.com/prismicio/prismic-vue/commit/c10cb3a99577e19222635d7005329d587f65eaaa)) 443 | 444 | 445 | ### Chore 446 | 447 | * **deps:** maintain dependencies ([ec7b0cd](https://github.com/prismicio/prismic-vue/commit/ec7b0cdc15086a24e2d94b8f1ebd9bb3574de241)) 448 | * **deps:** maintain package lock ([040a795](https://github.com/prismicio/prismic-vue/commit/040a79504ae667a0467ca8ce4f20da5499b31228)) 449 | * maintain template ([b30adcc](https://github.com/prismicio/prismic-vue/commit/b30adcc3cfdcb91d78b8ed937ebe15a9a1474a31)) 450 | 451 | ## [3.0.0-beta.5](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.4...v3.0.0-beta.5) (2022-03-02) 452 | 453 | 454 | ### Chore 455 | 456 | * **deps:** maintain dependencies ([5ad23db](https://github.com/prismicio/prismic-vue/commit/5ad23db168acf53217f2877de5a184a3dcd89076)) 457 | 458 | ## [3.0.0-beta.4](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.3...v3.0.0-beta.4) (2022-02-24) 459 | 460 | 461 | ### Bug Fixes 462 | 463 | * render alt attribute properly for a11y, fixes [#55](https://github.com/prismicio/prismic-vue/issues/55) ([9d5e082](https://github.com/prismicio/prismic-vue/commit/9d5e082027936e1cfbada6b3df56982391c50796)) 464 | 465 | ## [3.0.0-beta.3](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.2...v3.0.0-beta.3) (2022-02-17) 466 | 467 | 468 | ### Features 469 | 470 | * allow configuring defaults for width-based and pixel-density-based `srcset` ([b06ff51](https://github.com/prismicio/prismic-vue/commit/b06ff516707cbf3dcd71c641d4c3ba8a0da732e4)) 471 | 472 | 473 | ### Refactor 474 | 475 | * simplify client creation using new client features ([92ad117](https://github.com/prismicio/prismic-vue/commit/92ad117dc17152e846b32848e6ff86f5f6dd236e)) 476 | 477 | 478 | ### Chore 479 | 480 | * **deps:** maintain dependencies ([87dc6d4](https://github.com/prismicio/prismic-vue/commit/87dc6d4291410f3542fc34f03ec0a1bb38a4cc13)) 481 | * remove nonsense export ([26a9a17](https://github.com/prismicio/prismic-vue/commit/26a9a170ca0176025c965594b8c8b1eacfb5d3f4)) 482 | * typo in component name ([eedc79b](https://github.com/prismicio/prismic-vue/commit/eedc79b382d316fb51003f828769c9a1e9269c5e)) 483 | 484 | ## [3.0.0-beta.2](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.1...v3.0.0-beta.2) (2022-02-15) 485 | 486 | 487 | ### Features 488 | 489 | * update image components and add image helpers ([f1fa427](https://github.com/prismicio/prismic-vue/commit/f1fa42743c0fa2891eb4eee0bd01818a07e587ec)) 490 | 491 | 492 | ### Chore 493 | 494 | * **deps:** maintain dependencies ([fefbca9](https://github.com/prismicio/prismic-vue/commit/fefbca974de9f2eb30605c226a46206c955930f8)) 495 | * update license ([1c608ba](https://github.com/prismicio/prismic-vue/commit/1c608ba7c9c4360913509b8f2a727e6b2bf357ba)) 496 | * update template ([60d5763](https://github.com/prismicio/prismic-vue/commit/60d5763237d670f97f73a9bdcb06319a6dda74bc)) 497 | * update test file name ([840cc11](https://github.com/prismicio/prismic-vue/commit/840cc11d321070de4f3e413b8fc46d3172a9cebd)) 498 | 499 | ## [3.0.0-beta.1](https://github.com/prismicio/prismic-vue/compare/v3.0.0-beta.0...v3.0.0-beta.1) (2021-12-03) 500 | 501 | 502 | ### Chore 503 | 504 | * **deps:** maintain dependencies ([cafeae1](https://github.com/prismicio/prismic-vue/commit/cafeae16da952c69b292828dff62bffb7fb0a890)) 505 | 506 | ## [3.0.0-beta.0](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.6...v3.0.0-beta.0) (2021-12-02) 507 | 508 | 509 | ### Bug Fixes 510 | 511 | * prop type for `target`, `rel` & `blankTargetRelAttribute` props ([23ba61e](https://github.com/prismicio/prismic-vue/commit/23ba61ef5e68c0eec309e919b90b231425aa4506)) 512 | 513 | 514 | ### Chore 515 | 516 | * cleanup ignore files ([cee5697](https://github.com/prismicio/prismic-vue/commit/cee5697b5c04ffbcc78f192461f309080627a3a0)) 517 | * update release scripts to beta ([0696d2f](https://github.com/prismicio/prismic-vue/commit/0696d2f236367e7ce84b20dcf78221a6599fdd54)) 518 | 519 | ## [3.0.0-alpha.6](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.5...v3.0.0-alpha.6) (2021-11-23) 520 | 521 | 522 | ### Features 523 | 524 | * add `resolver` prop to SliceZone for backwards compatibility with `vue-slicezone` ([adf62e2](https://github.com/prismicio/prismic-vue/commit/adf62e242308e87d8fb6c62c31cd36e014613463)) 525 | 526 | 527 | ### Chore 528 | 529 | * **deps:** fix types after update ([eb82e0e](https://github.com/prismicio/prismic-vue/commit/eb82e0ea845b1f7cd8bf1e484b196d86d9b6bb68)) 530 | * key slicezone components for consistency ([d814e1c](https://github.com/prismicio/prismic-vue/commit/d814e1c213fa216a15684f80655dfe450cab3500)) 531 | 532 | 533 | ### Refactor 534 | 535 | * allow `null` or `""` as truthy value on `` ([0bc4f29](https://github.com/prismicio/prismic-vue/commit/0bc4f29ca799fffd73874261456d794aafffdc5a)) 536 | * rename `getSliceZoneComponents()` to `defineSliceZoneComponents()` ([17f6736](https://github.com/prismicio/prismic-vue/commit/17f673629a9a3fb04e5236be7d8b9c3c4caca6e6)) 537 | 538 | ## [3.0.0-alpha.5](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.4...v3.0.0-alpha.5) (2021-11-17) 539 | 540 | 541 | ### Bug Fixes 542 | 543 | * **deps:** new @prismicio/client types ([293f397](https://github.com/prismicio/prismic-vue/commit/293f39735bdbe4fb7791cecbc879679b80824ccd)) 544 | 545 | 546 | ### Documentation 547 | 548 | * fix contributing doc link ([8b80a17](https://github.com/prismicio/prismic-vue/commit/8b80a170569a7b41219935381c41678143172de7)) 549 | 550 | 551 | ### Chore 552 | 553 | * **deps:** maintain dependencies ([eb8a31a](https://github.com/prismicio/prismic-vue/commit/eb8a31a7d0d73b439e4e96088d979d8cd4d06568)) 554 | 555 | ## [3.0.0-alpha.4](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.3...v3.0.0-alpha.4) (2021-09-27) 556 | 557 | 558 | ### Refactor 559 | 560 | * simplify internal links handling for ([010f714](https://github.com/prismicio/prismic-vue/commit/010f7147ccc8d57b61aa619ad005f3dd99541941)) 561 | 562 | 563 | ### Chore 564 | 565 | * **deps:** maintain dependencies ([1111f70](https://github.com/prismicio/prismic-vue/commit/1111f701fbc89baf709acf517b5008a10bea70f7)) 566 | * **deps:** maintain dependencies ([e4964be](https://github.com/prismicio/prismic-vue/commit/e4964beea33f1ebc083b6a9f1612aaeeee600bcc)) 567 | * update template config ([338f1cb](https://github.com/prismicio/prismic-vue/commit/338f1cbe1606be1e3da83d496c2da0357850681d)) 568 | 569 | ## [3.0.0-alpha.3](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.2...v3.0.0-alpha.3) (2021-08-19) 570 | 571 | 572 | ### Chore 573 | 574 | * mark component implementations as pue ([1910abf](https://github.com/prismicio/prismic-vue/commit/1910abf3efd90645fd9f547fcb91675fde455614)) 575 | 576 | 577 | ### Documentation 578 | 579 | * add readme ([3e0bddb](https://github.com/prismicio/prismic-vue/commit/3e0bddb50bed390bb53972a55d57eedf0facc30b)) 580 | 581 | ## [3.0.0-alpha.2](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2021-08-19) 582 | 583 | 584 | ### Features 585 | 586 | * allow for props hint on getSliceComponentProps ([3245a5c](https://github.com/prismicio/prismic-vue/commit/3245a5c9f0e536ea02cfffe119edc6f6ba33ba23)) 587 | * lazy load fetch ponyfill when needed ([bee79c2](https://github.com/prismicio/prismic-vue/commit/bee79c27032cd229063ee78a9ab5fea3d7124514)) 588 | 589 | 590 | ### Chore 591 | 592 | * **deps:** maintain dependencies ([f7df24d](https://github.com/prismicio/prismic-vue/commit/f7df24dc7661d418b141f2bb9bb7d7c23ec7fdb8)) 593 | 594 | ## [3.0.0-alpha.1](https://github.com/prismicio/prismic-vue/compare/v3.0.0-alpha.0...v3.0.0-alpha.1) (2021-08-17) 595 | 596 | 597 | ### Features 598 | 599 | * add basic components and related composables ([eb4e1d6](https://github.com/prismicio/prismic-vue/commit/eb4e1d6f228b685b85f0f087072943e201842412)) 600 | * add composables ([a7ab05c](https://github.com/prismicio/prismic-vue/commit/a7ab05c58eda77f104bfc0aad3d50ab4dbd7019c)) 601 | * add PrismicLink component ([5f0b4cf](https://github.com/prismicio/prismic-vue/commit/5f0b4cf0a418973b4af6cea5d804436e3ce840e1)) 602 | * add slice zone component ([ec0c50a](https://github.com/prismicio/prismic-vue/commit/ec0c50aa76a020898dadcb2303750b799ff601ef)) 603 | * navigate internal links using vue-router if present ([0e0de61](https://github.com/prismicio/prismic-vue/commit/0e0de615266e79fdac09fa49bc999697df3f90f3)) 604 | 605 | 606 | ### Bug Fixes 607 | 608 | * don't use ts ignore comments ([ff5ad93](https://github.com/prismicio/prismic-vue/commit/ff5ad93404a405dbf84d8201867f6d8eaa768aca)) 609 | * provide accurate default for usePrismic ([f5a14de](https://github.com/prismicio/prismic-vue/commit/f5a14de4cae2ce7f1c8dcd93bb5b9ad10b63d078)) 610 | * use shallowRef to prevent object unnecessary unwrap ([90eb09c](https://github.com/prismicio/prismic-vue/commit/90eb09cc55651d78d15c00f19e3669f5c1d11813)) 611 | 612 | 613 | ### Refactor 614 | 615 | * composables ([0867de3](https://github.com/prismicio/prismic-vue/commit/0867de34b7c07c5d6144b0cfcfd8d429dd5f7cdb)) 616 | * migrate to new typescript template ([804b09d](https://github.com/prismicio/prismic-vue/commit/804b09d20c8eb46ae93b8fb825fa56bfde7b9fdd)) 617 | * use computed properties for memo cache ([b642a87](https://github.com/prismicio/prismic-vue/commit/b642a877d5cf04ac119e0b1cb71a7d49761cfa93)) 618 | * use dedicated file for plugin and use API ([6c3e944](https://github.com/prismicio/prismic-vue/commit/6c3e9440e6dadb9488586769754b77def789a35b)) 619 | 620 | 621 | ### Chore 622 | 623 | * add .versionrc ([05392cc](https://github.com/prismicio/prismic-vue/commit/05392cce6f549afdd4d1d6baf5a076e35c7ddf61)) 624 | * **config:** add tsdocs lint ([2ff9de7](https://github.com/prismicio/prismic-vue/commit/2ff9de7b401a4cb06ab00af4ac90506556dcc430)) 625 | * **deps:** maintain dependencies ([11ddb6b](https://github.com/prismicio/prismic-vue/commit/11ddb6b99be563428062baa5fcda47799096f420)) 626 | * fix package lock ([b213b56](https://github.com/prismicio/prismic-vue/commit/b213b56411ddc896b20e8ced4514d95ed89898b5)) 627 | * fix package lock ([2b63475](https://github.com/prismicio/prismic-vue/commit/2b63475c4a5e3a6f6f8ab435c26c36b67b3df697)) 628 | * maintain dependencies ([9cb8bf9](https://github.com/prismicio/prismic-vue/commit/9cb8bf92952e831fa2dee49322f8d062bc9b5dcc)) 629 | * **playground:** update playground ([1641316](https://github.com/prismicio/prismic-vue/commit/1641316499dfd874ecbb3da5de3dd674ddbdc4c1)) 630 | * update .github templates ([2b92d7e](https://github.com/prismicio/prismic-vue/commit/2b92d7e8874d8611445747ba9993ed6938a2153c)) 631 | * update playground ([782bb20](https://github.com/prismicio/prismic-vue/commit/782bb2054c1e551e81e27fec12795e37937889d8)) 632 | 633 | 634 | ### Documentation 635 | 636 | * add tsdocs for components ([e2f0bd7](https://github.com/prismicio/prismic-vue/commit/e2f0bd7f800729e46744383d2ed0b2ef4bda8713)) 637 | * add tsdocs for composables ([8b8e9f2](https://github.com/prismicio/prismic-vue/commit/8b8e9f2068c6e4e61e598b68cff0e91129153489)) 638 | * document root level source files ([26fab78](https://github.com/prismicio/prismic-vue/commit/26fab78a0796a5842aa5c0f0980bcca5a6a62694)) 639 | * remove docus doc ([3946e3e](https://github.com/prismicio/prismic-vue/commit/3946e3e4f54bfa95a5335980e7b48a2ccdc7742c)) 640 | * tsdocs for plugin types ([18dbbaf](https://github.com/prismicio/prismic-vue/commit/18dbbafc9f988b19380b0721d448ef1ed6328b61)) 641 | * typo ([e137db6](https://github.com/prismicio/prismic-vue/commit/e137db6a407d7ea674380f37c3506217c418680f)) 642 | * update vetur tags and attributes ([1c927a4](https://github.com/prismicio/prismic-vue/commit/1c927a4c54e83baba0f6ebb29b0f71292f018b07)) 643 | 644 | ## [3.0.0-alpha.0](https://github.com/prismicio/prismic-vue/compare/v2.0.11...v3.0.0-alpha.0) (2021-04-09) 645 | 646 | 647 | ### ⚠ BREAKING CHANGES 648 | 649 | * migrate client and dom sdk 650 | 651 | ### Features 652 | 653 | * **components:** add embed component ([d1bab33](https://github.com/prismicio/prismic-vue/commit/d1bab337902cc1299a1c9d3532d24c3ab82ff775)) 654 | * **components:** add image component ([0ca4723](https://github.com/prismicio/prismic-vue/commit/0ca4723eb4394393603af4e8fc6f3dcc1f2ae8cf)) 655 | * **components:** add link and rich text to vetur definitions ([b790dd4](https://github.com/prismicio/prismic-vue/commit/b790dd44cd80fb697e162693c2fb320768f71c97)) 656 | * **components:** add link component ([62efeee](https://github.com/prismicio/prismic-vue/commit/62efeee35a13c5143ba27fd346b3dae788abd4c3)) 657 | * **components:** add richtext component & refactor comonents sdk ([155f6c1](https://github.com/prismicio/prismic-vue/commit/155f6c1145e1daa22adec504c87de76bd92a8dc9)) 658 | * **core:** export field types ([58b8907](https://github.com/prismicio/prismic-vue/commit/58b89076f371b99c1d543410fa196960e9b5df24)) 659 | * **core:** setup vetur ([a8a123e](https://github.com/prismicio/prismic-vue/commit/a8a123e756f2977b73b713a22572ba242c45b865)) 660 | 661 | 662 | ### Bug Fixes 663 | 664 | * **components:** export image type properly ([5ab0b4b](https://github.com/prismicio/prismic-vue/commit/5ab0b4b85a9eecbb9beca384ea3e6ef749e03b28)) 665 | * **core:** worng type on usePrismic ([03b3a23](https://github.com/prismicio/prismic-vue/commit/03b3a23008b4f8febe1fd9f2b582e9e09f3c00e4)) 666 | * **dom:** asDate using wrong return value ([6df2abe](https://github.com/prismicio/prismic-vue/commit/6df2abe71019bf05bf979bac5861732c4c770027)) 667 | * **sdk:** fix `this` context on dom ([3fbaa4e](https://github.com/prismicio/prismic-vue/commit/3fbaa4e172dad9e25f2f05c40b1b57d19290c8d9)) 668 | 669 | 670 | * migrate client and dom sdk ([cd46806](https://github.com/prismicio/prismic-vue/commit/cd468068c70ce37b86bdbd17fc4dfdf6e08e2a44)) 671 | 672 | ### [2.1.2](https://github.com/prismicio/prismic-vue/compare/v2.1.1...v2.1.2) (2022-02-24) 673 | 674 | 675 | ### Bug Fixes 676 | 677 | * render alt attribute properly for a11y, fixes [#55](https://github.com/prismicio/prismic-vue/issues/55) ([58aa764](https://github.com/prismicio/prismic-vue/commit/58aa7646e97ef4afc11986b048d96ee66389bc06)) 678 | 679 | ### [2.1.1](https://github.com/prismicio/prismic-vue/compare/v2.1.0...v2.1.1) (2022-02-16) 680 | 681 | 682 | ### Bug Fixes 683 | 684 | * prop type for wrapper component ([1f0c800](https://github.com/prismicio/prismic-vue/commit/1f0c80024c50681a66f3e67fe6eba9245cc6f750)) 685 | 686 | 687 | ### Chore 688 | 689 | * typo in component name ([62c0568](https://github.com/prismicio/prismic-vue/commit/62c0568fa2c119165720103f65e41568515d0c93)) 690 | * update license ([228cfaa](https://github.com/prismicio/prismic-vue/commit/228cfaa5ca9a7202112a514bdfaee9da4902ea06)) 691 | 692 | ## [2.1.0](https://github.com/prismicio/prismic-vue/compare/v2.1.0-alpha.1...v2.1.0) (2021-12-02) 693 | 694 | 695 | ### Features 696 | 697 | * provide `predicate` and `cookie` like v3 ([1ac9ad9](https://github.com/prismicio/prismic-vue/commit/1ac9ad9b986291ef12f93ee920bdd5ffea5cf64c)) 698 | 699 | 700 | ### Refactor 701 | 702 | * link target attribute definition ([1e049ca](https://github.com/prismicio/prismic-vue/commit/1e049ca5e789ae5c193db2af13d4e8f86a6bc3e5)) 703 | * use `PrismicVue` ([b4be607](https://github.com/prismicio/prismic-vue/commit/b4be6076b6cc7b8dd72c84e5067acafb6a5df973)) 704 | 705 | ## [2.1.0-alpha.1](https://github.com/prismicio/prismic-vue/compare/v2.1.0-alpha.0...v2.1.0-alpha.1) (2021-11-30) 706 | 707 | 708 | ### Chore 709 | 710 | * add lint and unit test setup ([d555d0a](https://github.com/prismicio/prismic-vue/commit/d555d0a43d5185547e0f859dc63d7ad2f661750d)) 711 | * **deps:** maintain dependencies ([0edd8aa](https://github.com/prismicio/prismic-vue/commit/0edd8aa1a956b84fa5ac3f5f07b6337c18dd35d3)) 712 | * **deps:** maintain dependencies ([cdf6d2a](https://github.com/prismicio/prismic-vue/commit/cdf6d2a663e809c13719ca8bf8529f784cef926e)) 713 | * **deps:** maintain dependencies ([c67db0a](https://github.com/prismicio/prismic-vue/commit/c67db0a5c7a8f8f667e1076743c3551bbe1110f0)) 714 | * **deps:** update dependencies ([ba52780](https://github.com/prismicio/prismic-vue/commit/ba52780a92b1303666fe2dfc02a9105f84fd2cd0)) 715 | * update release script ([235d9b1](https://github.com/prismicio/prismic-vue/commit/235d9b182bf502b6271f5c66a22ecaec187083d4)) 716 | --------------------------------------------------------------------------------