mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-27 18:36:08 +00:00
Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
This commit is contained in:
parent
cf28194e1e
commit
b914afcb6a
@ -13,6 +13,7 @@
|
||||
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ReactionType } from '../../generated/type/reaction';
|
||||
import Emoji from './Emoji';
|
||||
|
||||
const onReactionSelect = jest.fn();
|
||||
@ -33,10 +34,10 @@ jest.mock('../../AppState', () => ({
|
||||
}));
|
||||
|
||||
const mockProps = {
|
||||
reaction: 'thumbsUp',
|
||||
reaction: ReactionType.ThumbsUp,
|
||||
reactionList: [
|
||||
{
|
||||
reactionType: 'thumbsUp',
|
||||
reactionType: ReactionType.ThumbsUp,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -66,7 +67,7 @@ describe('Test Emoji Component', () => {
|
||||
|
||||
expect(emojiCount).toBeInTheDocument();
|
||||
|
||||
expect(emojiCount).toHaveTextContent(mockProps.reactionList.length);
|
||||
expect(emojiCount).toHaveTextContent(`${mockProps.reactionList.length}`);
|
||||
});
|
||||
|
||||
it('Should render the tooltip component on hovering the emoji', async () => {
|
@ -15,25 +15,38 @@ import '@github/g-emoji-element';
|
||||
import { Button, Popover } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from 'mobx-react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||
import AppState from '../../AppState';
|
||||
import { REACTION_LIST } from '../../constants/reactions.constant';
|
||||
import { ReactionOperation } from '../../enums/reactions.enum';
|
||||
import { Reaction, ReactionType } from '../../generated/type/reaction';
|
||||
import useImage from '../../hooks/useImage';
|
||||
|
||||
const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
|
||||
interface EmojiProps {
|
||||
reaction: ReactionType;
|
||||
reactionList: Reaction[];
|
||||
onReactionSelect: (
|
||||
reaction: ReactionType,
|
||||
operation: ReactionOperation
|
||||
) => void;
|
||||
}
|
||||
|
||||
const Emoji: FC<EmojiProps> = ({
|
||||
reaction,
|
||||
reactionList,
|
||||
onReactionSelect,
|
||||
}) => {
|
||||
const [reactionType, setReactionType] = useState(reaction);
|
||||
const [isClicked, setIsClicked] = useState(false);
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
// get reaction object based on cureent reactionType
|
||||
// get reaction object based on current reactionType
|
||||
const reactionObject = useMemo(
|
||||
() => REACTION_LIST.find((value) => value.reaction === reactionType),
|
||||
[reactionType]
|
||||
);
|
||||
|
||||
const { image } = useImage(`emojis/${reactionObject.reaction}.png`);
|
||||
const { image } = useImage(`emojis/${reactionObject?.reaction}.png`);
|
||||
|
||||
// get current user details
|
||||
const currentUser = useMemo(
|
||||
@ -50,13 +63,13 @@ const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
|
||||
(reactionItem) => reactionItem.user.name
|
||||
);
|
||||
|
||||
const handleEmojiOnClick = (e) => {
|
||||
const handleEmojiOnClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (!isClicked) {
|
||||
const operation = isReacted
|
||||
? ReactionOperation.REMOVE
|
||||
: ReactionOperation.ADD;
|
||||
onReactionSelect(reactionObject.reaction, operation);
|
||||
onReactionSelect(reactionObject?.reaction as ReactionType, operation);
|
||||
setIsClicked(true);
|
||||
}
|
||||
};
|
||||
@ -91,21 +104,28 @@ const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
|
||||
zIndex={9999}
|
||||
onVisibleChange={setVisible}>
|
||||
<Button
|
||||
className={classNames('ant-btn-reaction tw-mr-1', {
|
||||
className={classNames('ant-btn-reaction tw-mr-1 flex', {
|
||||
'ant-btn-isReacted': isReacted,
|
||||
})}
|
||||
data-testid="emoji-button"
|
||||
shape="round"
|
||||
onClick={handleEmojiOnClick}
|
||||
onMouseOver={() => setVisible(true)}>
|
||||
<g-emoji
|
||||
alias={reactionObject.alias}
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<g-emoji
|
||||
alias={${reactionObject?.alias}}
|
||||
className="d-flex"
|
||||
data-testid="emoji"
|
||||
fallback-src={image}>
|
||||
{reactionObject.emoji}
|
||||
</g-emoji>
|
||||
<span className="tw-text-sm tw-ml-1" data-testid="emoji-count">
|
||||
fallback-src={${image}}>
|
||||
${reactionObject?.emoji}
|
||||
</g-emoji>`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<span
|
||||
className="tw-text-sm tw-ml-1 self-center"
|
||||
data-testid="emoji-count">
|
||||
{reactionList.length}
|
||||
</span>
|
||||
</Button>
|
||||
@ -113,20 +133,4 @@ const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
|
||||
);
|
||||
};
|
||||
|
||||
Emoji.propTypes = {
|
||||
reactionList: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
reactionType: PropTypes.string.isRequired,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string,
|
||||
displayName: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
}).isRequired,
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
reaction: PropTypes.string.isRequired,
|
||||
onReactionSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default observer(Emoji);
|
@ -15,15 +15,34 @@ import '@github/g-emoji-element';
|
||||
import { Button } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { uniqueId } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import { ReactionOperation } from '../../enums/reactions.enum';
|
||||
import { ReactionType } from '../../generated/type/reaction';
|
||||
import useImage from '../../hooks/useImage';
|
||||
|
||||
const Reaction = ({ reaction, isReacted, onReactionSelect, onHide }) => {
|
||||
interface ReactionProps {
|
||||
reaction: {
|
||||
emoji: string;
|
||||
reaction: ReactionType;
|
||||
alias: string;
|
||||
};
|
||||
isReacted: boolean;
|
||||
onReactionSelect: (
|
||||
reaction: ReactionType,
|
||||
operation: ReactionOperation
|
||||
) => void;
|
||||
onHide: () => void;
|
||||
}
|
||||
|
||||
const Reaction: FC<ReactionProps> = ({
|
||||
reaction,
|
||||
isReacted,
|
||||
onReactionSelect,
|
||||
onHide,
|
||||
}) => {
|
||||
const { image } = useImage(`emojis/${reaction.reaction}.png`);
|
||||
|
||||
const handleOnClick = (e) => {
|
||||
const handleOnClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const operation = isReacted
|
||||
? ReactionOperation.REMOVE
|
||||
@ -44,26 +63,19 @@ const Reaction = ({ reaction, isReacted, onReactionSelect, onHide }) => {
|
||||
title={reaction.reaction}
|
||||
type="text"
|
||||
onClick={handleOnClick}>
|
||||
<g-emoji
|
||||
alias={reaction.alias}
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<g-emoji
|
||||
alias={${reaction.alias}}
|
||||
className="d-flex"
|
||||
data-testid="emoji"
|
||||
fallback-src={image}>
|
||||
{reaction.emoji}
|
||||
</g-emoji>
|
||||
fallback-src={${image}}>
|
||||
${reaction.emoji}
|
||||
</g-emoji>`,
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
Reaction.propTypes = {
|
||||
reaction: PropTypes.shape({
|
||||
emoji: PropTypes.string.isRequired,
|
||||
reaction: PropTypes.string.isRequired,
|
||||
alias: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
isReacted: PropTypes.bool.isRequired,
|
||||
onReactionSelect: PropTypes.func.isRequired,
|
||||
onHide: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Reaction;
|
@ -13,6 +13,7 @@
|
||||
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ReactionType } from '../../generated/type/reaction';
|
||||
import Reactions from './Reactions';
|
||||
|
||||
jest.mock('./Emoji', () =>
|
||||
@ -38,7 +39,7 @@ const onReactionSelect = jest.fn();
|
||||
|
||||
const reactions = [
|
||||
{
|
||||
reactionType: 'heart',
|
||||
reactionType: ReactionType.Heart,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -49,7 +50,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'confused',
|
||||
reactionType: ReactionType.Confused,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -60,7 +61,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'laugh',
|
||||
reactionType: ReactionType.Laugh,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -71,7 +72,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'thumbsDown',
|
||||
reactionType: ReactionType.ThumbsDown,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -82,7 +83,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'thumbsUp',
|
||||
reactionType: ReactionType.ThumbsUp,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -93,7 +94,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'hooray',
|
||||
reactionType: ReactionType.Hooray,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -104,7 +105,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'rocket',
|
||||
reactionType: ReactionType.Rocket,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
||||
@ -115,7 +116,7 @@ const reactions = [
|
||||
},
|
||||
},
|
||||
{
|
||||
reactionType: 'eyes',
|
||||
reactionType: ReactionType.Eyes,
|
||||
user: {
|
||||
id: '2e424734-761a-443f-bf2a-a5b361823c80',
|
||||
type: 'user',
|
@ -15,25 +15,37 @@ import '@github/g-emoji-element';
|
||||
import { Button, Popover } from 'antd';
|
||||
import { groupBy, uniqueId } from 'lodash';
|
||||
import { observer } from 'mobx-react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { FC, useMemo, useState } from 'react';
|
||||
import AppState from '../../AppState';
|
||||
import {
|
||||
REACTION_LIST,
|
||||
REACTION_TYPE_LIST,
|
||||
} from '../../constants/reactions.constant';
|
||||
import { ReactionOperation } from '../../enums/reactions.enum';
|
||||
import {
|
||||
Reaction as ReactionProp,
|
||||
ReactionType,
|
||||
} from '../../generated/type/reaction';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import Emoji from './Emoji';
|
||||
import Reaction from './Reaction';
|
||||
|
||||
const Reactions = ({ reactions, onReactionSelect }) => {
|
||||
interface ReactionsProps {
|
||||
reactions: ReactionProp[];
|
||||
onReactionSelect: (
|
||||
reaction: ReactionType,
|
||||
operation: ReactionOperation
|
||||
) => void;
|
||||
}
|
||||
|
||||
const Reactions: FC<ReactionsProps> = ({ reactions, onReactionSelect }) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const hide = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const handleVisibleChange = (newVisible) => {
|
||||
const handleVisibleChange = (newVisible: boolean) => {
|
||||
setVisible(newVisible);
|
||||
};
|
||||
|
||||
@ -48,7 +60,7 @@ const Reactions = ({ reactions, onReactionSelect }) => {
|
||||
* @param reactionType
|
||||
* @returns true if current user has reacted with {reactionType}
|
||||
*/
|
||||
const isReacted = (reactionType) => {
|
||||
const isReacted = (reactionType: ReactionType) => {
|
||||
return reactions.some(
|
||||
(reactionItem) =>
|
||||
reactionItem.user.id === currentUser?.id &&
|
||||
@ -109,7 +121,7 @@ const Reactions = ({ reactions, onReactionSelect }) => {
|
||||
<SVGIcons
|
||||
alt="add-reaction"
|
||||
icon={Icons.ADD_REACTION}
|
||||
style={{ verticalAlign: 'text-bottom' }}
|
||||
// style={{ verticalAlign: 'text-bottom' }}
|
||||
title="Add reactions"
|
||||
width="18px"
|
||||
/>
|
||||
@ -120,19 +132,4 @@ const Reactions = ({ reactions, onReactionSelect }) => {
|
||||
);
|
||||
};
|
||||
|
||||
Reactions.propTypes = {
|
||||
reactions: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
reactionType: PropTypes.string.isRequired,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string,
|
||||
displayName: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
}).isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
onReactionSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default observer(Reactions);
|
@ -25,9 +25,12 @@ let global;
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
global.document.createRange = () => ({
|
||||
setStart: jest.fn(),
|
||||
setEnd: jest.fn(),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(global as any).document.createRange = () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
setStart: () => {},
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
setEnd: () => {},
|
||||
commonAncestorContainer: {
|
||||
nodeName: 'BODY',
|
||||
ownerDocument: document,
|
Loading…
x
Reference in New Issue
Block a user