diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5f693..d695df2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ 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. +### [0.25.3](https://github.com/Fantom-foundation/Fantom-Vue3-Components/compare/v0.25.2...v0.25.3) (2022-09-13) + + +### Features + +* **DocumentMeta:** create plugin class ([d52557a](https://github.com/Fantom-foundation/Fantom-Vue3-Components/commit/d52557a8fe5a4496bd9d94375e55794f63094436)) +* **useDocumentMeta:** create composable ([2d6fa4e](https://github.com/Fantom-foundation/Fantom-Vue3-Components/commit/2d6fa4e673717daccc2aa63b156dbf2a440a3394)) + + +### Bug Fixes + +* **FUploadAreaFormInput:** fix style when input is invalid ([390713a](https://github.com/Fantom-foundation/Fantom-Vue3-Components/commit/390713a2e1444fc4bdca0f35bdfd76ff2643b6c2)) + ### [0.25.2](https://github.com/Fantom-foundation/Fantom-Vue3-Components/compare/v0.25.1...v0.25.2) (2022-09-12) ### [0.25.1](https://github.com/Fantom-foundation/Fantom-Vue3-Components/compare/v0.25.0...v0.25.1) (2022-09-12) diff --git a/package-lock.json b/package-lock.json index 3749b9d..a96a65d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fantom-vue3-components", - "version": "0.25.2", + "version": "0.25.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "fantom-vue3-components", - "version": "0.25.2", + "version": "0.25.3", "license": "MIT", "dependencies": { "@vueuse/core": "^8.5.0", diff --git a/package.json b/package.json index b6a5e01..aa1050a 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fantom-vue3-components", - "version": "0.25.2", + "version": "0.25.3", "description": "Library of various Vue 3 components with focus on accessibility", "author": "Fantom Foundation", "license": "MIT", diff --git a/src/components/FUploadAreaFormInput/FUploadAreaFormInput.vue b/src/components/FUploadAreaFormInput/FUploadAreaFormInput.vue index 1cae9e2..a75a2c5 100644 --- a/src/components/FUploadAreaFormInput/FUploadAreaFormInput.vue +++ b/src/components/FUploadAreaFormInput/FUploadAreaFormInput.vue @@ -1,6 +1,7 @@ @@ -48,6 +54,7 @@ defineExpose({ validate }); { + it('should return instance of DocumentMeta', () => { + const { composableResult, app } = withSetup({ composable: () => useDocumentMeta({ pageInfo: {} }) }); + + expect(composableResult.documentMeta instanceof DocumentMeta).toBe(true); + + app.unmount(); + }); +}); diff --git a/src/plugins/DocumentMeta/DocumentMeta.js b/src/plugins/DocumentMeta/DocumentMeta.js new file mode 100644 index 0000000..61842c0 --- /dev/null +++ b/src/plugins/DocumentMeta/DocumentMeta.js @@ -0,0 +1,66 @@ +export class DocumentMeta { + constructor({ mainTitle = '' } = {}) { + this.mainTitle = mainTitle; + } + + /** + * @param {string|array} title + * @param {boolean} [addMainTitle] Add main title at the end of the title + */ + setTitle(title, addMainTitle = false) { + let t = ''; + + if (title !== undefined) { + if (Array.isArray(title)) { + t = title.map((str) => str.trim()).join(' | '); + } else { + t = title; + } + + if (addMainTitle && this.mainTitle) { + t += t !== '' ? ` | ${this.mainTitle}` : this.mainTitle; + } + + document.title = t; + DocumentMeta.#setMetaContentAttribute('og:title', t); + DocumentMeta.#setMetaContentAttribute('twitter:title', t); + } + } + + /** + * @param {string} description + */ + setDescription(description) { + if (description !== undefined) { + DocumentMeta.#setMetaContentAttribute('og:description', description); + DocumentMeta.#setMetaContentAttribute('twitter:description', description); + } + } + + /** + * @param {string} mainTitle + */ + setMainTitle(mainTitle = '') { + this.mainTitle = mainTitle; + } + + /** + * @return {string[]} + */ + getSplittedTitle() { + return document.title.split('|').map((title) => title.trim()); + } + + /** + * @param {string} property + * @param {string} value + */ + static #setMetaContentAttribute(property, value = '') { + if (property) { + const elem = document.querySelector(`meta[property="${property}"]`); + if (elem) { + elem.setAttribute('content', value); + } + } + } +} diff --git a/src/plugins/DocumentMeta/DocumentMeta.spec.js b/src/plugins/DocumentMeta/DocumentMeta.spec.js new file mode 100644 index 0000000..896458b --- /dev/null +++ b/src/plugins/DocumentMeta/DocumentMeta.spec.js @@ -0,0 +1,101 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { DocumentMeta } from './DocumentMeta.js'; + +let dm = null; +const TEST_TITLE = 'Test title'; +const TEST_DESCRIPTION = 'Test description'; + +function createMetaElement(property = '', content = '') { + const elem = document.createElement('meta'); + + if (property) { + elem.setAttribute('property', property); + } + + if (content) { + elem.setAttribute('content', content); + } + + return elem; +} + +beforeEach(() => { + dm = new DocumentMeta(); +}); + +afterEach(() => { + dm = null; +}); + +describe('DocumentMeta', () => { + it('should set html document title', () => { + dm.setTitle(TEST_TITLE); + + expect(document.title).toEqual(TEST_TITLE); + }); + + it('should set og and twitter meta title', () => { + const ogMetaTitle = createMetaElement('og:title'); + const twitterMetaTitle = createMetaElement('twitter:title'); + + document.head.appendChild(ogMetaTitle); + document.head.appendChild(twitterMetaTitle); + + dm.setTitle(TEST_TITLE); + + expect(ogMetaTitle.getAttribute('content')).toEqual(TEST_TITLE); + expect(twitterMetaTitle.getAttribute('content')).toEqual(TEST_TITLE); + + ogMetaTitle.remove(); + twitterMetaTitle.remove(); + }); + + it('should set og and twitter meta description', () => { + const ogMetaDescription = createMetaElement('og:description'); + const twitterMetaDescription = createMetaElement('twitter:description'); + + document.head.appendChild(ogMetaDescription); + document.head.appendChild(twitterMetaDescription); + + dm.setDescription(TEST_DESCRIPTION); + + expect(ogMetaDescription.getAttribute('content')).toEqual(TEST_DESCRIPTION); + expect(twitterMetaDescription.getAttribute('content')).toEqual(TEST_DESCRIPTION); + + ogMetaDescription.remove(); + twitterMetaDescription.remove(); + }); + + it('should set title by given array and display it separated with `|`', () => { + dm.setTitle([' part1 ', 'part2']); + + expect(document.title).toEqual('part1 | part2'); + }); + + it('should add main title at the end of the title, separated by `|`', () => { + const mainTitle = 'MyApp'; + + dm = new DocumentMeta({ mainTitle }); + + dm.setTitle(TEST_TITLE, true); + expect(document.title).toEqual(`${TEST_TITLE} | ${mainTitle}`); + + dm.setTitle('', true); + expect(document.title).toEqual(`${mainTitle}`); + }); + + it('should be able to set main title', () => { + const mainTitle = 'MyApp'; + + dm.setMainTitle(mainTitle); + dm.setTitle(TEST_TITLE, true); + + expect(document.title).toEqual(`${TEST_TITLE} | ${mainTitle}`); + }); + + it('should get current title splitted by `|`', () => { + dm.setTitle(`${TEST_TITLE} | ${TEST_TITLE}`); + + expect(dm.getSplittedTitle()).toEqual([TEST_TITLE, TEST_TITLE]); + }); +}); diff --git a/src/plugins/index.js b/src/plugins/index.js index 8ff8428..9a4f8a1 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -3,3 +3,4 @@ export * from './jazzicon/jazzicon.js'; export * from './Formatters/Formatters.js'; export * from './Locale/Locale.js'; export * from './api/index.js'; +export * from './DocumentMeta/DocumentMeta.js';