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