parent
561ba3afd4
commit
6be51f1806
17
README.md
17
README.md
|
@ -36,6 +36,7 @@ Features:
|
|||
* [JavaScript API](#javascript-api)
|
||||
+ [Picker](#picker)
|
||||
- [i18n structure](#i18n-structure)
|
||||
- [Custom category order](#custom-category-order)
|
||||
+ [Database](#database)
|
||||
- [Constructors](#constructors)
|
||||
* [constructor](#constructor)
|
||||
|
@ -232,7 +233,8 @@ The `new Picker(options)` constructor supports several options:
|
|||
|
||||
Name | Type | Default | Description |
|
||||
------ | ------ | ------ | ------ |
|
||||
`customEmoji` | CustomEmoji[] | - | Array of custom emoji |
|
||||
`customCategorySort` | function | - | Function to sort custom category strings (sorted alphabetically by default) |
|
||||
`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 (`data-source` when used as an attribute) |
|
||||
`i18n` | I18n | - | i18n object (see below for details) |
|
||||
`locale` | string | "en" | Locale string |
|
||||
|
@ -319,6 +321,19 @@ Here is the default English `i18n` object (`"en"` locale):
|
|||
Note that some of these strings are only visible to users of screen readers.
|
||||
But you should still support them if you internationalize your app!
|
||||
|
||||
#### Custom category order
|
||||
|
||||
By default, custom categories are sorted alphabetically. To change this, pass in your own `customCategorySort`:
|
||||
|
||||
```js
|
||||
picker.customCategorySort = (category1, category2) => { /* your sorting code */ };
|
||||
```
|
||||
|
||||
This function should accept two strings and return a number.
|
||||
|
||||
Custom emoji with no category will pass in `undefined`. By default, these are shown first, with the label `"Custom"`
|
||||
(determined by `i18n.categories.custom`).
|
||||
|
||||
### Database
|
||||
|
||||
You can work with the database API separately, which allows you to query emoji the same
|
||||
|
|
|
@ -35,6 +35,7 @@ let skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
|
|||
let i18n = enI18n
|
||||
let database = null
|
||||
let customEmoji = null
|
||||
let customCategorySort = (a, b) => a < b ? -1 : a > b ? 1 : 0
|
||||
|
||||
// private
|
||||
let initialLoad = true
|
||||
|
@ -406,7 +407,7 @@ $: {
|
|||
}
|
||||
return [...categoriesToEmoji.entries()]
|
||||
.map(([category, emojis]) => ({ category, emojis }))
|
||||
.sort((a, b) => a.category < b.category ? -1 : 1)
|
||||
.sort((a, b) => customCategorySort(a.category, b.category))
|
||||
}
|
||||
|
||||
currentEmojisWithCategories = calculateCurrentEmojisWithCategories()
|
||||
|
@ -614,5 +615,6 @@ export {
|
|||
database,
|
||||
i18n,
|
||||
skinToneEmoji,
|
||||
customEmoji
|
||||
customEmoji,
|
||||
customCategorySort
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export default class Picker extends HTMLElement {
|
|||
i18n: I18n
|
||||
skinToneEmoji: string
|
||||
customEmoji?: CustomEmoji[]
|
||||
customCategorySort?: (a: string, b: string) => number
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -14,13 +15,15 @@ export default class Picker extends HTMLElement {
|
|||
* @param i18n - i18n object (see below for details)
|
||||
* @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
|
||||
* @param customCategorySort - Function to sort custom category strings (sorted alphabetically by default)
|
||||
*/
|
||||
constructor({
|
||||
dataSource = 'https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json',
|
||||
locale = 'en',
|
||||
i18n,
|
||||
skinToneEmoji = '🖐️',
|
||||
customEmoji
|
||||
customEmoji,
|
||||
customCategorySort
|
||||
}: PickerConstructorOptions = {}) {
|
||||
super()
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ export interface PickerConstructorOptions {
|
|||
i18n?: I18n
|
||||
skinToneEmoji?: string
|
||||
customEmoji?: CustomEmoji[]
|
||||
customCategorySort?: (a: string, b: string) => number
|
||||
}
|
||||
|
||||
export interface I18n {
|
||||
|
|
|
@ -4,6 +4,7 @@ import Picker from '../../../src/picker/PickerElement.js'
|
|||
import userEvent from '@testing-library/user-event'
|
||||
import { groups } from '../../../src/picker/groups'
|
||||
import Database from '../../../src/database/Database'
|
||||
import { getAccessibleName } from '../utils'
|
||||
|
||||
const { waitFor, fireEvent } = testingLibrary
|
||||
const { type } = userEvent
|
||||
|
@ -358,11 +359,80 @@ describe('Picker tests', () => {
|
|||
await waitFor(() => expect(getByRole('menuitem', { name: 'donkey' })).toBeVisible())
|
||||
await waitFor(() => expect(getByRole('menuitem', { name: 'monkey' })).toBeVisible())
|
||||
await waitFor(() => expect(getByRole('menuitem', { name: 'horse' })).toBeVisible())
|
||||
// TODO: can't actually test the category names because they're only exposed as menus, and
|
||||
// testing-library doesn't seem to understand that menus can have aria-labels
|
||||
|
||||
// confirm alphabetical order for categories
|
||||
expect(
|
||||
await Promise.all(getAllByRole('menu').map(node => getAccessibleName(container, node)))
|
||||
).toStrictEqual(
|
||||
['Custom', 'Primates', 'Ungulates', 'Favorites']
|
||||
)
|
||||
|
||||
// try searching
|
||||
await type(getByRole('combobox'), 'donkey')
|
||||
await waitFor(() => expect(getByRole('option', { name: 'donkey' })).toBeVisible())
|
||||
})
|
||||
|
||||
test('Custom emoji with sorted categories', async () => {
|
||||
picker.customEmoji = [
|
||||
{
|
||||
name: 'monkey',
|
||||
shortcodes: ['monkey'],
|
||||
url: 'monkey.png',
|
||||
category: 'Primates'
|
||||
},
|
||||
{
|
||||
name: 'donkey',
|
||||
shortcodes: ['donkey'],
|
||||
url: 'donkey.png',
|
||||
category: 'Ungulates'
|
||||
},
|
||||
{
|
||||
name: 'horse',
|
||||
shortcodes: ['horse'],
|
||||
url: 'horse.png',
|
||||
category: 'Ungulates'
|
||||
},
|
||||
{
|
||||
name: 'bird',
|
||||
shortcodes: ['bird'],
|
||||
url: 'bird.png',
|
||||
category: 'Avians'
|
||||
},
|
||||
{
|
||||
name: 'human',
|
||||
shortcodes: ['human'],
|
||||
url: 'human.png'
|
||||
}
|
||||
]
|
||||
await waitFor(() => expect(getAllByRole('tab')).toHaveLength(groups.length + 1))
|
||||
await waitFor(() => expect(getAllByRole('menu')).toHaveLength(5)) // favorites + four custom categories
|
||||
|
||||
// confirm alphabetical order for categories
|
||||
expect(
|
||||
await Promise.all(getAllByRole('menu').map(node => getAccessibleName(container, node)))
|
||||
).toStrictEqual([
|
||||
'Custom',
|
||||
'Avians',
|
||||
'Primates',
|
||||
'Ungulates',
|
||||
'Favorites'
|
||||
])
|
||||
|
||||
const order = ['Ungulates', 'Primates', 'Avians']
|
||||
picker.customCategorySort = (a, b) => {
|
||||
const aIdx = order.indexOf(a)
|
||||
const bIdx = order.indexOf(b)
|
||||
return aIdx < bIdx ? -1 : 1
|
||||
}
|
||||
|
||||
await waitFor(async () => (
|
||||
expect(
|
||||
await Promise.all(getAllByRole('menu').map(node => getAccessibleName(container, node)))
|
||||
).toStrictEqual([
|
||||
'Custom',
|
||||
...order,
|
||||
'Favorites'
|
||||
])
|
||||
))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import * as testingLibrary from '@testing-library/dom'
|
||||
|
||||
export async function getAccessibleName (container, node) {
|
||||
let label = node.getAttribute('aria-label')
|
||||
if (!label) {
|
||||
const labeledBy = node.getAttribute('aria-labelledby')
|
||||
if (labeledBy) {
|
||||
label = testingLibrary.getNodeText(await container.getRootNode().getElementById(labeledBy))
|
||||
}
|
||||
}
|
||||
return label
|
||||
}
|
Loading…
Reference in New Issue