Fix types for emoji-mart emojiIndex (#37966)

* fix types for emojiIndex

* run prettier

* bump minor version to latest

* alphabetize exports

* use EmojiSkin as index for EmojiEntry

* move TS 2.9 to support `[index in union]` syntax
This commit is contained in:
elvis-wolcott 2019-08-28 20:17:18 -07:00 committed by Ron Buckton
parent ec7c6ded2a
commit 81d59d6e17
10 changed files with 149 additions and 89 deletions

View File

@ -3,7 +3,7 @@ import React = require('react');
import { Emoji, EmojiData, EmojiProps, I18n, CategoryName } from '..';
export interface Props {
emojis?: Array<string|EmojiData>;
emojis?: Array<string | EmojiData>;
hasStickyPosition?: boolean;
id: CategoryName;
name: string;

View File

@ -20,6 +20,7 @@ export {
BaseEmoji,
CustomEmoji,
EmojiData,
EmojiEntry,
EmojiSkin,
default as NimbleEmojiIndex,
} from './utils/emoji-index/nimble-emoji-index';
@ -32,5 +33,5 @@ export {
NimbleEmoji,
NimbleEmojiProps,
Category,
CategoryProps
CategoryProps,
} from './components';

View File

@ -1,11 +1,11 @@
import { EmojiData } from './nimble-emoji-index';
import { EmojiData, EmojiEntry } from './nimble-emoji-index';
// tslint:disable-next-line strict-export-declare-modifiers
declare const _default: {
search(query: ''): null;
search(query: string): EmojiData|null;
search(query: string): EmojiData[] | null;
emojis: { [emoji: string]: EmojiData };
emojis: { [emoji: string]: EmojiEntry };
/** Mapping of string to keyof emojis */
emoticons: { [emoticon: string]: string };

View File

@ -1,4 +1,4 @@
import { Data } from "../data";
import { Data } from '../data';
import { CategoryName } from '../shared-props';
@ -29,11 +29,12 @@ export interface CustomEmoji {
}
export type EmojiData = BaseEmoji | CustomEmoji;
export type EmojiEntry = EmojiData | { [variant in EmojiSkin]: EmojiData }; // emoji with skin tones will return
export default class NimbleEmojiIndex {
constructor(data: Data);
search(query: ''): null;
search(query: string): EmojiData[]|null;
search(query: string): EmojiData[] | null;
emojis: { [emoji: string]: EmojiData };
/** Mapping of string to keyof emojis */
emoticons: { [emoticon: string]: string };

View File

@ -2,8 +2,8 @@ import { EmojiData } from '..';
// tslint:disable-next-line strict-export-declare-modifiers
declare const _default: {
add(emoji: Pick<EmojiData, 'id'>): void
get(perLine: number): string[]
add(emoji: Pick<EmojiData, 'id'>): void;
get(perLine: number): string[];
};
export { _default as default };

View File

@ -32,7 +32,18 @@ export interface EmojiProps {
/** data is omitted here as it should be used for NimbleEmoji only - not emoji */
}
export type CategoryName = 'search' | 'recent' | 'people' | 'nature' | 'foods' | 'activity' | 'places' | 'objects' | 'symbols' | 'flags' | 'custom';
export type CategoryName =
| 'search'
| 'recent'
| 'people'
| 'nature'
| 'foods'
| 'activity'
| 'places'
| 'objects'
| 'symbols'
| 'flags'
| 'custom';
// tslint:disable-next-line interface-name
export interface I18n {

View File

@ -5,11 +5,11 @@ export interface StoreHandlers {
// tslint:disable-next-line strict-export-declare-modifiers
declare const _default: {
setHandlers(handlers?: StoreHandlers): void
setNamespace(namespace: string): void
update(state: {[key: string]: any}): void
set(key: string, value: any): void
get(key: string): any
setHandlers(handlers?: StoreHandlers): void;
setNamespace(namespace: string): void;
update(state: { [key: string]: any }): void;
set(key: string, value: any): void;
get(key: string): any;
};
export { _default as default };

View File

@ -1,35 +1,46 @@
// Port of https://github.com/missive/emoji-mart/blob/v2.8.0/stories/index.js
import React = require('react');
import { useState } from 'react';
import { Picker, Emoji, EmojiProps, CustomEmoji, BaseEmoji, NimbleEmojiIndex } from 'emoji-mart';
import {
Picker,
Emoji,
EmojiProps,
CustomEmoji,
BaseEmoji,
NimbleEmojiIndex,
emojiIndex,
EmojiData,
EmojiEntry,
} from 'emoji-mart';
declare var console: { log(...args: any[]): void; };
declare var console: { log(...args: any[]): void };
const CUSTOM_EMOJIS: CustomEmoji[] = [
{
name: 'Party Parrot',
short_names: ['parrot'],
keywords: ['party'],
imageUrl: 'http://cultofthepartyparrot.com/parrots/hd/parrot.gif'
},
{
name: 'Octocat',
short_names: ['octocat'],
keywords: ['github'],
imageUrl: 'https://assets-cdn.github.com/images/icons/emoji/octocat.png?v7'
},
{
name: 'Squirrel',
short_names: ['shipit', 'squirrel'],
keywords: ['github'],
imageUrl: 'https://assets-cdn.github.com/images/icons/emoji/shipit.png?v7'
},
{
name: 'Party Parrot',
short_names: ['parrot'],
keywords: ['party'],
imageUrl: 'http://cultofthepartyparrot.com/parrots/hd/parrot.gif',
},
{
name: 'Octocat',
short_names: ['octocat'],
keywords: ['github'],
imageUrl: 'https://assets-cdn.github.com/images/icons/emoji/octocat.png?v7',
},
{
name: 'Squirrel',
short_names: ['shipit', 'squirrel'],
keywords: ['github'],
imageUrl: 'https://assets-cdn.github.com/images/icons/emoji/shipit.png?v7',
},
];
interface State {
native: boolean;
set: EmojiProps['set']|'native';
set: EmojiProps['set'] | 'native';
emoji: string;
title: string;
custom: CustomEmoji[];
@ -41,53 +52,92 @@ class Example extends React.Component<{}, State> {
set: 'apple',
emoji: 'point_up',
title: 'Pick your emoji…',
custom: CUSTOM_EMOJIS
custom: CUSTOM_EMOJIS,
};
render() {
render() {
return (
<div>
<div className="row">
<h1>Emoji Mart 🏬</h1>
</div>
<div className="row">
{(['native', 'apple', 'google', 'twitter', 'emojione', 'messenger', 'facebook'] as Array<
EmojiProps['set'] | 'native'
>).map(set => {
const props = { disabled: !this.state.native && set === this.state.set };
if (set === 'native' && this.state.native) {
props.disabled = true;
}
return (
<button
key={set}
value={set}
onClick={() => {
if (set === 'native') {
this.setState({ native: true });
} else {
this.setState({ set, native: false });
}
}}
{...props}
>
{set}
</button>
);
})}
</div>
<div className="row">
<Picker
{...this.state}
// NOTE: The original code passes the this.state directly, which includes a potential
// invalid 'set' value. The value of 'set' happens to be ignored if native is true, but no
// good way to represent it in the typings.
set={this.state.set === 'native' ? undefined : this.state.set}
onClick={console.log}
/>
</div>
</div>
);
}
}
const AutoCompleteExample: React.FC = () => {
const [search, setSearch] = useState('');
const changed = (e: React.ChangeEvent<HTMLInputElement>) => {
const target = e.target as { value: any };
setSearch(target.value);
};
const match = search.match(/:([a-z_]+)(:)?/);
let suggestions: ReturnType<typeof emojiIndex.search> = [];
if (match && match[2]) {
// smiley is closed
let emoji = emojiIndex.emojis[match[1]];
const isSingleEmoji = (e: EmojiEntry): e is EmojiData => {
return 'name' in e;
};
const isNativeEmoji = (e: EmojiData): e is BaseEmoji => {
return 'native' in e;
};
if (!isSingleEmoji(emoji)) {
emoji = emoji[1];
}
const native = (isNativeEmoji(emoji) && emoji.native) || '';
setSearch(search.replace(match[0], native));
} else if (match && match[1].length > 2) {
suggestions = emojiIndex.search(match[1]) || [];
}
return (
<div>
<div className="row">
<h1>Emoji Mart 🏬</h1>
</div>
<div className="row">
{(['native', 'apple', 'google', 'twitter', 'emojione', 'messenger', 'facebook'] as Array<EmojiProps['set']|'native'>).map((set) => {
const props = { disabled: !this.state.native && set === this.state.set };
if (set === 'native' && this.state.native) {
props.disabled = true;
}
return (
<button
key={set}
value={set}
onClick={() => {
if (set === 'native') {
this.setState({ native: true });
} else {
this.setState({ set, native: false });
}
}}
{...props}
>
{set}
</button>
);
})}
</div>
<div className="row">
<Picker
{...this.state}
// NOTE: The original code passes the this.state directly, which includes a potential
// invalid 'set' value. The value of 'set' happens to be ignored if native is true, but no
// good way to represent it in the typings.
set={this.state.set === 'native' ? undefined : this.state.set}
onClick={console.log}
/>
</div>
{suggestions.map(emoji => {
return 'native' in emoji && <span>{emoji.native}</span>;
})}
{match && <div>{match[0]}</div>}
<input type="text" onInput={changed} placeholder="enter a message..." value={search} />
</div>
);
}
}
};

View File

@ -1,8 +1,9 @@
// Type definitions for emoji-mart 2.8
// Type definitions for emoji-mart 2.11
// Project: https://github.com/missive/emoji-mart
// Definitions by: Jessica Franco <https://github.com/Jessidhia>
// Nick Winans <https://github.com/Nicell>
// Elvis Wolcott <https://github.com/elvis-wolcott>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
// TypeScript Version: 2.9
export * from './dist-es';

View File

@ -1,17 +1,13 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"lib": ["es6"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"typeRoots": ["../"],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true,