Remove t from utils/emojis and related code (#27273)

* Update make_emojis.mjs and remove unused field

* Remove skin tone translations from emojis.ts

* Remove category name translations from emojis.ts

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Harrison Healey 2024-06-06 12:25:33 -04:00 committed by GitHub
parent a3bc73c1dc
commit 65f7a623ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 216 additions and 118 deletions

View File

@ -46,15 +46,15 @@ describe('Verify Accessibility Support in Popovers', () => {
cy.get('#emojiPickerCategories').children().eq(0).focus().tab({shift: true}).tab();
const categories = [
{ariaLabel: 'emoji_picker.smileys-emotion', header: 'Smileys & Emotion'},
{ariaLabel: 'emoji_picker.people-body', header: 'People & Body'},
{ariaLabel: 'emoji_picker.animals-nature', header: 'Animals & Nature'},
{ariaLabel: 'emoji_picker.food-drink', header: 'Food & Drink'},
{ariaLabel: 'emoji_picker.travel-places', header: 'Travel Places'},
{ariaLabel: 'emoji_picker.activities', header: 'Activities'},
{ariaLabel: 'emoji_picker.objects', header: 'Objects'},
{ariaLabel: 'emoji_picker.symbols', header: 'Symbols'},
{ariaLabel: 'emoji_picker.flags', header: 'Flags'},
{ariaLabel: 'Smileys & Emotion', header: 'Smileys & Emotion'},
{ariaLabel: 'People & Body', header: 'People & Body'},
{ariaLabel: 'Animals & Nature', header: 'Animals & Nature'},
{ariaLabel: 'Food & Drink', header: 'Food & Drink'},
{ariaLabel: 'Travel Places', header: 'Travel Places'},
{ariaLabel: 'Activities', header: 'Activities'},
{ariaLabel: 'Objects', header: 'Objects'},
{ariaLabel: 'Symbols', header: 'Symbols'},
{ariaLabel: 'Flags', header: 'Flags'},
];
// * Verify if emoji Categories gets the focus when tab is pressed

View File

@ -13,17 +13,17 @@
* npm run make-emojis -- --help
*/
import path from 'node:path';
import * as fsPromise from 'node:fs/promises';
import * as fs from 'node:fs';
import * as fsPromise from 'node:fs/promises';
import path from 'node:path';
import * as url from 'node:url';
import yargs from 'yargs';
import chalk from 'chalk';
import jsonData from 'emoji-datasource/emoji.json';
import jsonCategories from 'emoji-datasource/categories.json';
import jsonCategories from 'emoji-datasource/categories.json' assert {type: 'json'};
import jsonData from 'emoji-datasource/emoji.json' assert {type: 'json'};
import yargs from 'yargs';
import additionalShortnames from './additional_shortnames.json';
import additionalShortnames from './additional_shortnames.json' assert {type: 'json'};
const EMOJI_SIZE = 64;
const EMOJI_SIZE_PADDED = EMOJI_SIZE + 2; // 1px per side
@ -142,7 +142,6 @@ const emojiIndicesByCategory = new Map();
const emojiIndicesByCategoryAndSkin = new Map();
const emojiIndicesByCategoryNoSkin = new Map();
const categoryNamesSet = new Set();
const categoryDefaultTranslation = new Map();
const emojiImagesByAlias = [];
const emojiFilePositions = new Map();
const skinCodes = {
@ -237,7 +236,6 @@ fullEmoji.forEach((emoji, index) => {
}
const safeCat = normalizeCategoryName(emoji.category);
categoryDefaultTranslation.set(safeCat, emoji.category);
emoji.category = safeCat;
addIndexToMap(emojiIndicesByCategory, safeCat, index);
if (emoji.skins || emoji.skin_variations) {
@ -351,17 +349,11 @@ endResults.push(writeToFileAndPrint('emoji.json', webappUtilsEmojiDir, JSON.stri
const categoryList = Object.keys(jsonCategories).filter((item) => item !== 'Component').map(normalizeCategoryName);
const categoryNames = ['recent', ...categoryList, 'custom'];
categoryDefaultTranslation.set('recent', 'Recently Used');
categoryDefaultTranslation.set('searchResults', 'Search Results');
categoryDefaultTranslation.set('custom', 'Custom');
const categoryTranslations = ['recent', 'searchResults', ...categoryNames].map((c) => `['${c}', t('emoji_picker.${c}')]`);
const writeableSkinCategories = [];
const skinTranslations = [];
const skinnedCats = [];
for (const skin of emojiIndicesByCategoryAndSkin.keys()) {
writeableSkinCategories.push(`['${skin}', new Map(${JSON.stringify(Array.from(emojiIndicesByCategoryAndSkin.get(skin)))})]`);
skinTranslations.push(`['${skin}', t('emoji_skin.${skinCodes[skin]}')]`);
skinnedCats.push(`['${skin}', genSkinnedCategories('${skin}')]`);
}
@ -373,8 +365,6 @@ const emojiJSX = `// Copyright (c) 2015-present Mattermost, Inc. All Rights Rese
/* eslint-disable */
import {t} from 'utils/i18n';
import memoize from 'memoize-one';
import emojis from 'utils/emoji.json';
@ -389,14 +379,6 @@ export const EmojiIndicesByUnicode = new Map(${JSON.stringify(emojiIndicesByUnic
export const CategoryNames = ${JSON.stringify(categoryNames)};
export const CategoryMessage = new Map(${JSON.stringify(Array.from(categoryDefaultTranslation))});
export const CategoryTranslations = new Map([${categoryTranslations}]);
export const SkinTranslations = new Map([${skinTranslations.join(', ')}]);
export const ComponentCategory = 'Component';
const AllEmojiIndicesByCategory = new Map(${JSON.stringify(Array.from(emojiIndicesByCategory))});
const EmojiIndicesByCategoryAndSkin = new Map([${writeableSkinCategories.join(', ')}]);

View File

@ -39,7 +39,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
id="emojiPickerCategories"
>
<a
aria-label="emoji_picker.smileys-emotion"
aria-label="Smileys & Emotion"
class="emoji-picker__category emoji-picker__category--selected"
href="#"
>
@ -48,7 +48,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.people-body"
aria-label="People & Body"
class="emoji-picker__category"
href="#"
>
@ -57,7 +57,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.animals-nature"
aria-label="Animals & Nature"
class="emoji-picker__category"
href="#"
>
@ -66,7 +66,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.food-drink"
aria-label="Food & Drink"
class="emoji-picker__category"
href="#"
>
@ -75,7 +75,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.travel-places"
aria-label="Travel & Places"
class="emoji-picker__category"
href="#"
>
@ -84,7 +84,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.activities"
aria-label="Activities"
class="emoji-picker__category"
href="#"
>
@ -93,7 +93,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.objects"
aria-label="Objects"
class="emoji-picker__category"
href="#"
>
@ -102,7 +102,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.symbols"
aria-label="Symbols"
class="emoji-picker__category"
href="#"
>
@ -111,7 +111,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.flags"
aria-label="Flags"
class="emoji-picker__category"
href="#"
>
@ -120,7 +120,7 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
/>
</a>
<a
aria-label="emoji_picker.custom"
aria-label="Custom"
class="emoji-picker__category"
href="#"
>

View File

@ -3,7 +3,7 @@
exports[`EmojiPickerCategory should match snapshot 1`] = `
<DocumentFragment>
<a
aria-label="categoryId"
aria-label="category name"
class="emoji-picker__category"
href="#"
>

View File

@ -76,7 +76,7 @@ function EmojiPickerCategories({
return (
<EmojiPickerCategory
key={`${category.id}-${category.name}`}
key={category.name}
category={category}
categoryRowIndex={calculateCategoryRowIndex(categories, categoryName as EmojiCategory)}
onClick={onClick}

View File

@ -11,14 +11,16 @@ import {renderWithContext, userEvent} from 'tests/react_testing_utils';
import EmojiPickerCategory from './emoji_picker_category';
import type {Props} from './emoji_picker_category';
const categoryMessage = 'categoryMessage';
const categoryMessage = 'category name';
const defaultProps: Props = {
category: {
className: 'categoryClass',
emojiIds: ['emojiId'],
id: 'categoryId',
message: categoryMessage,
name: 'recent',
emojiIds: ['emojiId'],
iconClassName: 'categoryClass',
label: {
id: 'categoryId',
defaultMessage: categoryMessage,
},
} as Category,
categoryRowIndex: 0,
selected: false,

View File

@ -3,7 +3,7 @@
import classNames from 'classnames';
import React, {memo} from 'react';
import {FormattedMessage} from 'react-intl';
import {FormattedMessage, useIntl} from 'react-intl';
import type {EmojiCategory} from '@mattermost/types/emojis';
@ -19,6 +19,8 @@ export interface Props {
}
function EmojiPickerCategory({category, categoryRowIndex, selected, enable, onClick}: Props) {
const intl = useIntl();
const handleClick = (event: React.MouseEvent) => {
event.preventDefault();
@ -39,19 +41,16 @@ function EmojiPickerCategory({category, categoryRowIndex, selected, enable, onCl
id={`emojiPickerCategoryTooltip-${category.name}`}
placement='bottom'
title={
<FormattedMessage
id={`emoji_picker.${category.name}`}
defaultMessage={category.message}
/>
<FormattedMessage {...category.label}/>
}
>
<a
className={className}
href='#'
onClick={handleClick}
aria-label={category.id}
aria-label={intl.formatMessage(category.label)}
>
<i className={category.className}/>
<i className={category.iconClassName}/>
</a>
</WithTooltip>
);

View File

@ -3,27 +3,74 @@
import classNames from 'classnames';
import React from 'react';
import {FormattedMessage, injectIntl} from 'react-intl';
import type {IntlShape} from 'react-intl';
import {FormattedMessage, defineMessage, injectIntl} from 'react-intl';
import type {IntlShape, MessageDescriptor} from 'react-intl';
import {CSSTransition} from 'react-transition-group';
import {CloseIcon} from '@mattermost/compass-icons/components';
import type {SystemEmoji} from '@mattermost/types/emojis';
import WithTooltip from 'components/with_tooltip';
import imgTrans from 'images/img_trans.gif';
import * as Emoji from 'utils/emoji';
const skinsList = [
['raised_hand_with_fingers_splayed_dark_skin_tone', '1F3FF'],
['raised_hand_with_fingers_splayed_medium_dark_skin_tone', '1F3FE'],
['raised_hand_with_fingers_splayed_medium_skin_tone', '1F3FD'],
['raised_hand_with_fingers_splayed_medium_light_skin_tone', '1F3FC'],
['raised_hand_with_fingers_splayed_light_skin_tone', '1F3FB'],
['raised_hand_with_fingers_splayed', 'default'],
];
interface SkinTone {
emoji: SystemEmoji;
label: MessageDescriptor;
value: string;
}
const skinToneEmojis = new Map(skinsList.map((pair) => [pair[1], Emoji.Emojis[Emoji.EmojiIndicesByAlias.get(pair[0])!]]));
const skinTones = [
{
emoji: Emoji.Emojis[Emoji.EmojiIndicesByAlias.get('raised_hand_with_fingers_splayed_dark_skin_tone')!],
label: defineMessage({
id: 'emoji_skin.dark_skin_tone',
defaultMessage: 'Dark skin tone',
}),
value: '1F3FF',
},
{
emoji: Emoji.Emojis[Emoji.EmojiIndicesByAlias.get('raised_hand_with_fingers_splayed_medium_dark_skin_tone')!],
label: defineMessage({
id: 'emoji_skin.medium_dark_skin_tone',
defaultMessage: 'Medium dark skin tone',
}),
value: '1F3FE',
},
{
emoji: Emoji.Emojis[Emoji.EmojiIndicesByAlias.get('raised_hand_with_fingers_splayed_medium_skin_tone')!],
label: defineMessage({
id: 'emoji_skin.medium_skin_tone',
defaultMessage: 'Medium skin tone',
}),
value: '1F3FD',
},
{
emoji: Emoji.Emojis[Emoji.EmojiIndicesByAlias.get('raised_hand_with_fingers_splayed_medium_light_skin_tone')!],
label: defineMessage({
id: 'emoji_skin.medium_light_skin_tone',
defaultMessage: 'Medium light skin tone',
}),
value: '1F3FC',
},
{
emoji: Emoji.Emojis[Emoji.EmojiIndicesByAlias.get('raised_hand_with_fingers_splayed_light_skin_tone')!],
label: defineMessage({
id: 'emoji_skin.light_skin_tone',
defaultMessage: 'Light skin tone',
}),
value: '1F3FB',
},
{
emoji: Emoji.Emojis[Emoji.EmojiIndicesByAlias.get('raised_hand_with_fingers_splayed')!],
label: defineMessage({
id: 'emoji_skin.default',
defaultMessage: 'Default skin tone',
}),
value: 'default',
},
] satisfies SkinTone[];
export type Props = {
userSkinTone: string;
@ -44,13 +91,13 @@ export class EmojiPickerSkin extends React.PureComponent<Props, State> {
};
}
ariaLabel = (skin: string) => {
ariaLabel = (skinTone: SkinTone) => {
return this.props.intl.formatMessage({
id: 'emoji_skin_item.emoji_aria_label',
defaultMessage: '{skinName} emoji',
},
{
skinName: Emoji.SkinTranslations.get(skin),
skinName: this.props.intl.formatMessage(skinTone.label),
});
};
@ -70,16 +117,16 @@ export class EmojiPickerSkin extends React.PureComponent<Props, State> {
id: 'emoji_skin.close',
defaultMessage: 'Close skin tones',
});
const choices = skinsList.map((skinPair) => {
const skin = skinPair[1];
const emoji = skinToneEmojis.get(skin)!;
const choices = skinTones.map((skinTone) => {
const skin = skinTone.value;
const emoji = skinTone.emoji;
const spriteClassName = classNames('emojisprite', `emoji-category-${emoji.category}`, `emoji-${emoji.unified.toLowerCase()}`);
return (
<button
className='style--none skin-tones__icon'
data-testid={`skin-pick-${skin}`}
aria-label={this.ariaLabel(skin)}
aria-label={this.ariaLabel(skinTone)}
key={skin}
onClick={() => this.hideSkinTonePicker(skin)}
>
@ -105,7 +152,7 @@ export class EmojiPickerSkin extends React.PureComponent<Props, State> {
</button>
<div className='skin-tones__close-text'>
<FormattedMessage
id={Emoji.SkinTranslations.get('default')}
{...skinTones[skinTones.length - 1].label}
/>
</div>
</div>
@ -117,7 +164,7 @@ export class EmojiPickerSkin extends React.PureComponent<Props, State> {
}
collapsed() {
const emoji = skinToneEmojis.get(this.props.userSkinTone)!;
const emoji = skinTones.find(({value}) => value === this.props.userSkinTone)!.emoji;
const spriteClassName = classNames('emojisprite', `emoji-category-${emoji?.category}`, `emoji-${emoji?.unified.toLowerCase()}`);
const expandButtonLabel = this.props.intl.formatMessage({
id: 'emoji_picker.skin_tone',

View File

@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {defineMessage} from 'react-intl';
import type {EmojiCategory} from '@mattermost/types/emojis';
import * as Emoji from 'utils/emoji';
@ -12,39 +14,114 @@ export const SEARCH_RESULTS = 'searchResults';
export const SMILEY_EMOTION = 'smileys-emotion';
export const CUSTOM = 'custom';
const categoryClass: Map<EmojiCategory, string> = new Map([
[RECENT, 'icon-clock-outline'],
[SMILEY_EMOTION, 'icon-emoticon-happy-outline'],
['people-body', 'icon-account-outline'],
['animals-nature', 'icon-leaf-outline'],
['food-drink', 'icon-food-apple'],
['activities', 'icon-basketball'],
['travel-places', 'icon-airplane-variant'],
['objects', 'icon-lightbulb-outline'],
['symbols', 'icon-heart-outline'],
['flags', 'icon-flag-outline'],
[CUSTOM, 'icon-emoticon-custom-outline'],
[SEARCH_RESULTS, ''],
]);
const emojiCategories = {
recent: {
name: 'recent',
label: defineMessage({
id: 'emoji_picker.recent',
defaultMessage: 'Recent',
}),
iconClassName: 'icon-clock-outline',
},
searchResults: {
name: 'searchResults',
label: defineMessage({
id: 'emoji_picker.searchResults',
defaultMessage: 'Search Results',
}),
iconClassName: '',
},
'smileys-emotion': {
name: 'smileys-emotion',
label: defineMessage({
id: 'emoji_picker.smileys-emotion',
defaultMessage: 'Smileys & Emotion',
}),
iconClassName: 'icon-emoticon-happy-outline',
},
'people-body': {
name: 'people-body',
label: defineMessage({
id: 'emoji_picker.people-body',
defaultMessage: 'People & Body',
}),
iconClassName: 'icon-account-outline',
},
'animals-nature': {
name: 'animals-nature',
label: defineMessage({
id: 'emoji_picker.animals-nature',
defaultMessage: 'Animals & Nature',
}),
iconClassName: 'icon-leaf-outline',
},
'food-drink': {
name: 'food-drink',
label: defineMessage({
id: 'emoji_picker.food-drink',
defaultMessage: 'Food & Drink',
}),
iconClassName: 'icon-food-apple',
},
'travel-places': {
name: 'travel-places',
label: defineMessage({
id: 'emoji_picker.travel-places',
defaultMessage: 'Travel & Places',
}),
iconClassName: 'icon-airplane-variant',
},
activities: {
name: 'activities',
label: defineMessage({
id: 'emoji_picker.activities',
defaultMessage: 'Activities',
}),
iconClassName: 'icon-basketball',
},
objects: {
name: 'objects',
label: defineMessage({
id: 'emoji_picker.objects',
defaultMessage: 'Objects',
}),
iconClassName: 'icon-lightbulb-outline',
},
symbols: {
name: 'symbols',
label: defineMessage({
id: 'emoji_picker.symbols',
defaultMessage: 'Symbols',
}),
iconClassName: 'icon-heart-outline',
},
flags: {
name: 'flags',
label: defineMessage({
id: 'emoji_picker.flags',
defaultMessage: 'Flags',
}),
iconClassName: 'icon-flag-outline',
},
custom: {
name: 'custom',
label: defineMessage({
id: 'emoji_picker.custom',
defaultMessage: 'Custom',
}),
iconClassName: 'icon-emoticon-custom-outline',
},
} satisfies Record<EmojiCategory, Category>;
function createCategory(name: EmojiCategory): Category {
return {
name,
id: Emoji.CategoryTranslations.get(name) || '',
className: categoryClass.get(name) || '',
message: Emoji.CategoryMessage.get(name)!,
};
}
export const RECENT_EMOJI_CATEGORY: Pick<Categories, 'recent'> = {recent: createCategory(RECENT)};
export const SEARCH_EMOJI_CATEGORY: Pick<Categories, typeof SEARCH_RESULTS> = {searchResults: createCategory(SEARCH_RESULTS)};
export const RECENT_EMOJI_CATEGORY: Pick<Categories, 'recent'> = {recent: emojiCategories.recent};
export const SEARCH_EMOJI_CATEGORY: Pick<Categories, typeof SEARCH_RESULTS> = {searchResults: emojiCategories.searchResults};
export const CATEGORIES: Categories = Emoji.CategoryNames.
filter((category) => !(category === 'recent' || category === 'searchResults')).
reduce((previousCategory, currentCategory) => {
return {
...previousCategory,
[currentCategory]: createCategory(currentCategory as EmojiCategory),
[currentCategory]: emojiCategories[currentCategory as EmojiCategory],
};
}, {} as Categories);

View File

@ -51,7 +51,7 @@ describe('components/emoji_picker/EmojiPicker', () => {
<EmojiPicker {...baseProps}/>,
);
expect(screen.queryByLabelText('emoji_picker.recent')).toBeNull();
expect(screen.queryByLabelText('Recent')).toBeNull();
});
test('Recent category should exist if there are recent emojis', () => {
@ -64,7 +64,7 @@ describe('components/emoji_picker/EmojiPicker', () => {
<EmojiPicker {...props}/>,
);
expect(screen.queryByLabelText('emoji_picker.recent')).not.toBeNull();
expect(screen.queryByLabelText('Recent')).not.toBeNull();
});
test('First emoji should be selected on search', () => {

View File

@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {MessageDescriptor} from 'react-intl';
import type {EmojiCategory, Emoji, SystemEmoji, CustomEmoji} from '@mattermost/types/emojis';
import type {
@ -9,11 +11,10 @@ import type {
} from 'components/emoji_picker/constants';
export type Category = {
className: string;
emojiIds?: string[];
id: string;
message: string;
name: EmojiCategory;
emojiIds?: string[];
label: MessageDescriptor;
iconClassName: string;
};
export type Categories = Record<EmojiCategory, Category>;

File diff suppressed because one or more lines are too long