feat: add skin-tone-change event and types for events

This commit is contained in:
Nolan Lawson 2020-06-11 18:43:18 -07:00
parent fa3ca2045f
commit 144b72863f
5 changed files with 120 additions and 28 deletions

View File

@ -378,24 +378,68 @@ all wait for this promise to resolve before doing anything.
<!-- database API end -->
### Emoji object
### Events
This object is returned as the Event `detail` in the `emoji-click` event, or when querying the Database. Here is the format:
#### `emoji-click`
```ts
interface Emoji {
annotation: string;
emoticon?: string;
group: number;
name: string;
order: number;
shortcodes: string[];
tags?: string[];
version: number;
unicode: string;
The `emoji-click` event is fired when an emoji is selected by the user. Example format:
```javascript
{
emoji: {
annotation: 'thumbs up',
group: 1,
order: 280,
shortcodes: ['thumbsup', '+1', 'yes'],
tags: ['+1', 'hand', 'thumb', 'up'],
tokens: ['+1', 'hand', 'thumb', 'thumbs', 'thumbsup', 'up', 'yes'],
unicode: '👍️',
version: 0.6,
skins: [
{ tone: 1, unicode: '👍🏻', version: 1 },
{ tone: 2, unicode: '👍🏼', version: 1 },
{ tone: 3, unicode: '👍🏽', version: 1 },
{ tone: 4, unicode: '👍🏾', version: 1 },
{ tone: 5, unicode: '👍🏿', version: 1 }
]
},
skinTone: 4,
unicode: '👍🏾'
}
```
And usage:
```js
picker.addEventListener('emoji-click', event => {
console.log(event.detail); // will log something like the above
});
```
Note that `unicode` will represent whatever the emoji should look like
with the given `skinTone`. If the `skinTone` is 0, or if the emoji has
no skin tones, then no skin tone is applied to `unicode`.
#### `skin-tone-change`
This event is fired whenever the user selects a new skin tone. Example format:
```js
{
skinTone: 6
}
```
And usage:
```js
picker.addEventListener('skin-tone-change', event => {
console.log(event.detail); // will log something like the above
})
```
Note that skin tones are an integer from 0 (default) to 1 (light) through 6 (dark).
### Tree-shaking
If you want to import the `Database` without the `Picker`, or you want to code-split them separately, then do:

View File

@ -282,6 +282,14 @@ function onNavKeydown (event) {
}
}
function fireEvent (name, detail) {
rootElement.dispatchEvent(new CustomEvent(name, {
detail,
bubbles: true,
composed: true
}))
}
async function clickEmoji (unicode) {
const [emojiSupportLevel, emoji] = await Promise.all([
emojiSupportLevelPromise,
@ -293,15 +301,11 @@ async function clickEmoji (unicode) {
unicode = foundSkin.unicode
}
}
rootElement.dispatchEvent(new CustomEvent('emoji-click', {
detail: {
emoji,
skinTone: currentSkinTone,
unicode
},
bubbles: true,
composed: true
}))
fireEvent('emoji-click', {
emoji,
skinTone: currentSkinTone,
unicode
})
}
// eslint-disable-next-line no-unused-vars
@ -331,6 +335,7 @@ function onClickSkinToneOption (event) {
currentSkinTone = skinTone
skinTonePickerExpanded = false
focus('skintone-button')
fireEvent('skin-tone-change', { skinTone })
}
// eslint-disable-next-line no-unused-vars

View File

@ -1,4 +1,4 @@
import {I18n, PickerConstructorOptions} from "./shared";
import {I18n, PickerConstructorOptions, EmojiPickerEventMap, EmojiClickEvent, SkinToneChangeEvent} from "./shared";
export default class Picker extends HTMLElement {
dataSource: string;
@ -18,6 +18,12 @@ export default class Picker extends HTMLElement {
}: PickerConstructorOptions = {}) {
super()
}
// Adding types for addEventListener is hard... I basically just copy-pasted this from lib.dom.d.ts. Not sure
// Why I need the @ts-ignore
addEventListener<K extends keyof EmojiPickerEventMap>(type: K, listener: (this: TextTrackCue, ev: EmojiPickerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
// @ts-ignore
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
}
// see https://justinfagnani.com/2019/11/01/how-to-publish-web-components-to-npm/

View File

@ -47,4 +47,30 @@ export interface I18nCategories {
objects: string,
symbols: string,
flags: string
}
export interface EmojiClickEventDetail {
emoji: Emoji,
skinTone: number,
unicode: string,
}
export interface SkinToneChangeEventDetail {
skinTone: number
}
// via https://stackoverflow.com/a/55032655/680742
type Modify<T, R> = Omit<T, keyof R> & R;
export type EmojiClickEvent = Modify<UIEvent, {
detail: EmojiClickEventDetail
}>
export type SkinToneChangeEvent = Modify<UIEvent, {
detail: SkinToneChangeEventDetail
}>
export interface EmojiPickerEventMap {
"emoji-click": EmojiClickEvent;
"skin-tone-change": SkinToneChangeEvent;
}

View File

@ -61,6 +61,10 @@ describe('Picker tests', () => {
})
test('basic skintone test', async () => {
let event
picker.addEventListener('skin-tone-change', newEvent => {
event = newEvent
})
await waitFor(() => expect(getByRole('button', { name: 'Choose a skin tone (currently Default)' })).toBeVisible())
expect(queryAllByRole('listbox', { name: 'Skin tones' })).toHaveLength(0)
await fireEvent.click(getByRole('button', { name: 'Choose a skin tone (currently Default)' }))
@ -74,16 +78,23 @@ describe('Picker tests', () => {
await waitFor(() => expect(getByRole('option', { name, selected: true })).toBe(activeElement()))
}
await pressKeyAndExpectActiveOption('ArrowDown', 'Light')
await pressKeyAndExpectActiveOption('ArrowDown', 'Medium-Light')
await pressKeyAndExpectActiveOption('ArrowUp', 'Light')
await pressKeyAndExpectActiveOption('ArrowUp', 'Default')
await pressKeyAndExpectActiveOption('ArrowUp', 'Dark')
await pressKeyAndExpectActiveOption('ArrowDown', 'Light', 1)
await pressKeyAndExpectActiveOption('ArrowDown', 'Medium-Light', 2)
await pressKeyAndExpectActiveOption('ArrowUp', 'Light', 1)
await pressKeyAndExpectActiveOption('ArrowUp', 'Default', 0)
await pressKeyAndExpectActiveOption('ArrowUp', 'Dark', 5)
await fireEvent.click(activeElement(), { key: 'Enter', code: 'Enter' })
await waitFor(() => expect(event && event.detail).toStrictEqual({ skinTone: 5 }))
await waitFor(() => expect(queryAllByRole('listbox', { name: 'Skin tones' })).toHaveLength(0))
expect(getByRole('button', { name: 'Choose a skin tone (currently Dark)' })).toBeVisible()
getByRole('button', { name: 'Choose a skin tone (currently Dark)' }).click()
await waitFor(() => expect(queryAllByRole('listbox', { name: 'Skin tones' })).toHaveLength(1))
getByRole('option', { name: 'Default' }).click()
await waitFor(() => expect(event && event.detail).toStrictEqual({ skinTone: 0 }))
expect(getByRole('button', { name: 'Choose a skin tone (currently Default)' })).toBeVisible()
})
test('nav keyboard test', async () => {