From c3bcf114d9abd4fda8523ec44ab9a234bf8a24d8 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 9 Aug 2020 16:38:26 -0700 Subject: [PATCH] feat: support attribute format for some properties This allows users to set the locale, data source, and skin tone emoji using HTML attributes as well as properties. Fixes #7 --- README.md | 19 ++++++++++-- src/picker/PickerElement.js | 13 ++++++++ src/types/picker.ts | 4 +-- test/spec/picker/attributes.test.js | 47 +++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 test/spec/picker/attributes.test.js diff --git a/README.md b/README.md index 8834c8be..925cf9bc 100644 --- a/README.md +++ b/README.md @@ -233,10 +233,10 @@ The `new Picker(options)` constructor supports several options: Name | Type | Default | Description | ------ | ------ | ------ | ------ | `customEmoji` | CustomEmoji[] | - | Array of custom emoji | -`dataSource` | string | "https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json" | URL to fetch the emojibase data from | +`dataSource` | string | "https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json" | URL to fetch the emojibase data from (`data-source` when used as an attribute) | `i18n` | I18n | - | i18n object (see below for details) | `locale` | string | "en" | Locale string | -`skinToneEmoji` | string | "🖐️" | The emoji to use for the skin tone picker | +`skinToneEmoji` | string | "🖐️" | The emoji to use for the skin tone picker (`skin-tone-emoji` when used as an attribute) | @@ -251,13 +251,26 @@ const picker = new Picker({ }) ``` -These values can also be set at runtime, e.g.: +These values can also be set at runtime: ```js const picker = new Picker(); picker.dataSource = '/my-emoji.json'; ``` +Some values can also be set as declarative attributes: + +```html + +``` + +Note that complex properties like `i18n` or `customEmoji` are not supported as attributes, because the DOM only +supports string attributes, not complex objects. + #### i18n structure Here is the default English `i18n` object (`"en"` locale): diff --git a/src/picker/PickerElement.js b/src/picker/PickerElement.js index 6a4e28a6..fc4ae1c3 100644 --- a/src/picker/PickerElement.js +++ b/src/picker/PickerElement.js @@ -15,6 +15,19 @@ export default class Picker extends SveltePicker { log('disconnectedCallback') this.$destroy() } + + static get observedAttributes () { + return ['locale', 'data-source', 'skin-tone-emoji'] // complex objects aren't supported, also use kebab-case + } + + // via https://github.com/sveltejs/svelte/issues/3852#issuecomment-665037015 + attributeChangedCallback (attrName, oldValue, newValue) { + super.attributeChangedCallback( + attrName.replace(/-([a-z])/g, (_, up) => up.toUpperCase()), + oldValue, + newValue + ) + } } customElements.define('emoji-picker', Picker) diff --git a/src/types/picker.ts b/src/types/picker.ts index 1a106a41..d5700a44 100644 --- a/src/types/picker.ts +++ b/src/types/picker.ts @@ -9,10 +9,10 @@ export default class Picker extends HTMLElement { /** * - * @param dataSource - URL to fetch the emojibase data from + * @param dataSource - URL to fetch the emojibase data from (`data-source` when used as an attribute) * @param locale - Locale string * @param i18n - i18n object (see below for details) - * @param skinToneEmoji - The emoji to use for the skin tone picker + * @param skinToneEmoji - The emoji to use for the skin tone picker (`skin-tone-emoji` when used as an attribute) * @param customEmoji - Array of custom emoji */ constructor({ diff --git a/test/spec/picker/attributes.test.js b/test/spec/picker/attributes.test.js new file mode 100644 index 00000000..5e59352b --- /dev/null +++ b/test/spec/picker/attributes.test.js @@ -0,0 +1,47 @@ +import { basicAfterEach, basicBeforeEach, FR_EMOJI, ALL_EMOJI, mockFrenchDataSource, tick } from '../shared' +import Picker from '../../../src/picker/PickerElement.js' +import { getByRole } from '@testing-library/dom' + +describe('attributes tests', () => { + beforeEach(async () => { + basicBeforeEach() + mockFrenchDataSource() + }) + + afterEach(basicAfterEach) + + test('setting initial locale/dataSource issues only one GET', async () => { + const picker = new Picker() + picker.setAttribute('locale', 'fr') + picker.setAttribute('data-source', FR_EMOJI) + document.body.appendChild(picker) + await tick(20) + + expect(fetch).toHaveBeenCalledTimes(1) + expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined) + + expect(picker.locale).toEqual('fr') + expect(picker.dataSource).toEqual(FR_EMOJI) + expect(picker.getAttribute('locale')).toEqual('fr') + expect(picker.getAttribute('data-source')).toEqual(FR_EMOJI) + }) + + test('can set skintone emoji using an attribute', async () => { + const picker = new Picker() + picker.setAttribute('data-source', ALL_EMOJI) + picker.setAttribute('skin-tone-emoji', '✌') + document.body.appendChild(picker) + await tick(20) + expect(getByRole(picker.shadowRoot, 'button', { name: /Choose a skin tone/ }).innerHTML) + .toContain('✌') + expect(picker.skinToneEmoji).toEqual('✌') + expect(picker.getAttribute('skin-tone-emoji')).toEqual('✌') + expect(picker.locale).toEqual('en') + + picker.setAttribute('skin-tone-emoji', '🏃') + await tick(20) + expect(getByRole(picker.shadowRoot, 'button', { name: /Choose a skin tone/ }).innerHTML) + .toContain('🏃') + expect(picker.skinToneEmoji).toEqual('🏃') + }) +})