feat: add ability to change skin tone emoji

This commit is contained in:
Nolan Lawson 2020-06-11 20:29:48 -07:00
parent d5be361a49
commit 294d615e8a
9 changed files with 78 additions and 20 deletions

View File

@ -165,8 +165,9 @@ The `new Picker(options)` constructor supports several options:
Name | Type | Default | Description |
------ | ------ | ------ | ------ |
`dataSource` | string | "https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json" | URL to fetch the emojibase data from |
`i18n` | I18n | - | i18n object (see below for details) |
`i18n` | I18n | - | i18n object (see below for details) |
`locale` | string | "en" | Locale string |
`skinToneEmoji` | string | "🖐️" | the emoji to use for the skin tone picker |
**Returns:** *Picker*
@ -363,6 +364,16 @@ Name | Type | Description |
___
#### getPreferredSkinTone
**getPreferredSkinTone**(): *PromiseSkinTone*
Get the user's preferred skin tone. Returns 0 if not found.
**Returns:** *PromiseSkinTone*
___
#### ready
**ready**(): *Promisevoid*
@ -375,6 +386,22 @@ all wait for this promise to resolve before doing anything.
**Returns:** *Promisevoid*
___
#### setPreferredSkinTone
**setPreferredSkinTone**(`skinTone`: SkinTone): *Promisevoid*
Set the user's preferred skin tone. Non-numbers throw an error.
**Parameters:**
Name | Type | Description |
------ | ------ | ------ |
`skinTone` | SkinTone | preferred skin tone |
**Returns:** *Promisevoid*
<!-- database API end -->

View File

@ -3,12 +3,14 @@ import Database from './ImportedDatabase'
import { DEFAULT_DATA_SOURCE, DEFAULT_LOCALE } from '../database/constants'
import enI18n from './i18n/en'
import { mark } from '../shared/marks'
import { DEFAULT_SKIN_TONE_EMOJI } from './constants'
export default class Picker extends SveltePicker {
constructor ({
locale = DEFAULT_LOCALE,
dataSource = DEFAULT_DATA_SOURCE,
i18n = enI18n
i18n = enI18n,
skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
} = {}) {
mark('initialLoad')
// Make the API simpler, directly pass in the props
@ -16,7 +18,8 @@ export default class Picker extends SveltePicker {
super({
props: {
database: new Database({ dataSource, locale }),
i18n
i18n,
skinToneEmoji
}
})
this._locale = locale
@ -25,7 +28,7 @@ export default class Picker extends SveltePicker {
}
static get observedAttributes () {
return ['locale', 'dataSource', 'i18n']
return ['locale', 'dataSource', 'i18n', 'skinToneEmoji']
}
get locale () {

View File

@ -4,7 +4,7 @@ import Database from '../../ImportedDatabase'
import enI18n from '../../i18n/en'
import { categories } from '../../categories'
import { DEFAULT_LOCALE, DEFAULT_DATA_SOURCE } from '../../../database/constants'
import { MIN_SEARCH_TEXT_LENGTH } from '../../../shared/constants'
import { MIN_SEARCH_TEXT_LENGTH, NUM_SKIN_TONES } from '../../../shared/constants'
import { requestIdleCallback } from '../../utils/requestIdleCallback'
import { calculateTextWidth } from '../../utils/calculateTextWidth'
import { hasZwj } from '../../utils/hasZwj'
@ -16,14 +16,9 @@ import { applySkinTone } from '../../utils/applySkinTone'
import { halt } from '../../utils/halt'
import { incrementOrDecrement } from '../../utils/incrementOrDecrement'
import { tick } from 'svelte'
import { DEFAULT_SKIN_TONE_EMOJI, TIMEOUT_BEFORE_LOADING_MESSAGE } from '../../constants'
const TIMEOUT_BEFORE_LOADING_MESSAGE = 1000 // 1 second
const SKIN_TONE_BASE_TEXT = '\u270c'
const numSkinTones = 6
const skinToneTextForSkinTone = i => (i > 0 ? applySkinTone(SKIN_TONE_BASE_TEXT, i) : SKIN_TONE_BASE_TEXT)
const skinTones = Array(numSkinTones).fill().map((_, i) => skinToneTextForSkinTone(i))
let skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
let i18n = enI18n
let initialLoad = true
let database = null
@ -45,6 +40,8 @@ let activeSkinTone = 0
let skinToneText // eslint-disable-line no-unused-vars
let style = '' // eslint-disable-line no-unused-vars
let skinToneButtonLabel = '' // eslint-disable-line no-unused-vars
let skinToneTextForSkinTone = ''
let skinTones = []
const getBaselineEmojiWidth = thunk(() => calculateTextWidth(baselineEmoji))
@ -92,10 +89,12 @@ Promise.resolve().then(() => {
$: style = `
--num-categories: ${categories.length};
--indicator-opacity: ${searchMode ? 0 : 1};
--num-skintones: ${numSkinTones};`
--num-skintones: ${NUM_SKIN_TONES};`
$: skinToneText = skinToneTextForSkinTone(currentSkinTone)
$: skinToneButtonLabel = i18n.skinToneLabel.replace('{skinTone}', i18n.skinTones[currentSkinTone])
$: skinToneTextForSkinTone = i => applySkinTone(skinToneEmoji, i)
$: skinTones = Array(NUM_SKIN_TONES).fill().map((_, i) => skinToneTextForSkinTone(i))
// TODO: Chrome has an unfortunate bug where we can't use a simple percent-based transform
// here, becuause it's janky. You can especially see this on a Nexus 5.
@ -390,5 +389,6 @@ async function onSkinToneOptionsBlur () {
export {
database,
i18n
i18n,
skinToneEmoji
}

2
src/picker/constants.js Normal file
View File

@ -0,0 +1,2 @@
export const TIMEOUT_BEFORE_LOADING_MESSAGE = 1000 // 1 second
export const DEFAULT_SKIN_TONE_EMOJI = '🖐️'

View File

@ -1 +1,2 @@
export const MIN_SEARCH_TEXT_LENGTH = 2
export const NUM_SKIN_TONES = 6

View File

@ -1,20 +1,23 @@
import {I18n, PickerConstructorOptions, EmojiPickerEventMap, EmojiClickEvent, SkinToneChangeEvent} from "./shared";
export default class Picker extends HTMLElement {
dataSource: string;
locale: string;
i18n: I18n;
dataSource: string
locale: string
i18n: I18n
skinToneEmoji: string
/**
*
* @param dataSource - URL to fetch the emojibase data from
* @param locale - Locale string
* @param i18n - i18n object (see below for details)
* @param skinToneEmoji - the emoji to use for the skin tone picker
*/
constructor({
dataSource = 'https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json',
locale = 'en',
i18n
i18n,
skinToneEmoji = '🖐️'
}: PickerConstructorOptions = {}) {
super()
}

View File

@ -35,6 +35,7 @@ export interface PickerConstructorOptions {
dataSource?: string
locale?: string
i18n?: I18n
skinToneEmoji?: string
}
export interface I18n {

View File

@ -2,6 +2,7 @@ import { basicBeforeEach, basicAfterEach, ALL_EMOJI, truncatedEmoji, tick } from
import * as testingLibrary from '@testing-library/dom'
import Picker from '../../../src/picker/PickerElement.js'
import userEvent from '@testing-library/user-event'
import { DEFAULT_SKIN_TONE_EMOJI } from '../../../src/picker/constants'
const { waitFor, fireEvent } = testingLibrary
const { type } = userEvent
@ -243,4 +244,11 @@ describe('Picker tests', () => {
unicode: '🐒'
}))
})
test('Change default skin tone emoji', async () => {
expect(getByRole('button', { name: /Choose a skin tone/ }).innerHTML).toContain(DEFAULT_SKIN_TONE_EMOJI)
picker.skinToneEmoji = '👇'
await waitFor(() => expect(getByRole('button', { name: /Choose a skin tone/ }).innerHTML)
.toContain('👇'))
})
})

View File

@ -20,7 +20,6 @@ describe('skintones tests', () => {
const actualUnicode = applySkinTone(emoji.emoji, skin.tone)
if (actualUnicode !== skin.emoji) {
wrong.push({ emoji, actualUnicode, skin })
applySkinTone(emoji.emoji, skin.tone)
} else {
right.push({ emoji, actualUnicode, skin })
}
@ -31,10 +30,24 @@ describe('skintones tests', () => {
}
console.log('wrong', wrong.length, 'right', right.length)
for (const w of wrong) {
console.log('\n' + debugIt(w.emoji.emoji).join(',') + '\n' +
console.log('\n' + w.emoji.emoji + '\n' + debugIt(w.emoji.emoji).join(',') + '\n' +
debugIt(w.skin.emoji).join(',') + '\n' +
debugIt(w.actualUnicode).join(',') + '\n\n')
}
console.log('Right')
for (const r of right) {
console.log('\n' + r.emoji.emoji + '\n' + debugIt(r.emoji.emoji).join(',') + '\n' +
debugIt(r.skin.emoji).join(',') + '\n' +
debugIt(r.actualUnicode).join(',') + '\n\n')
}
})
test('can compute some correct unicode tones', () => {
expect(applySkinTone('👍', 0)).toBe('👍')
expect(applySkinTone('👍', 3)).toBe('👍🏽')
expect(applySkinTone('🧘‍♀️', 3)).toBe('🧘🏽‍♀️')
expect(applySkinTone('🤌', 2)).toBe('🤌🏼')
expect(applySkinTone('🖐️', 5)).toBe('🖐🏿')
})
})
})