From 89287bc38dbcc26b5d007e057b7b8e5dc1655f38 Mon Sep 17 00:00:00 2001 From: Daniele Valverde <34126648+danielevalverde@users.noreply.github.com> Date: Wed, 8 Jan 2025 07:19:11 -0300 Subject: [PATCH] Feature/CV2-5806: implement media cluster origin button (#2239) implements the MediaOrigin This component includes a button and a tooltip that displays: The action performed that caused the media to appear in the item. The username, if the action was performed by a user. The component has been added to the sandbox for testing with fake data and includes unit tests. implements the MediaClusterOriginButton UI component. This component includes a button and a tooltip that displays: The action that was performed caused the media to appear in the item. The username, if the action was performed by a user. The component has been added to the sandbox for testing with fake data and includes unit tests. Reference: CV2-5806 --- .../src/app/components/media/MediaOrigin.json | 52 ++++++ src/app/components/cds/Sandbox.js | 30 ++++ .../components/media/MediaCardLargeFooter.js | 43 ++--- src/app/components/media/MediaOrigin.js | 152 ++++++++++++++++++ src/app/components/media/MediaOrigin.test.js | 39 +++++ 5 files changed, 296 insertions(+), 20 deletions(-) create mode 100644 localization/react-intl/src/app/components/media/MediaOrigin.json create mode 100644 src/app/components/media/MediaOrigin.js create mode 100644 src/app/components/media/MediaOrigin.test.js diff --git a/localization/react-intl/src/app/components/media/MediaOrigin.json b/localization/react-intl/src/app/components/media/MediaOrigin.json new file mode 100644 index 0000000000..e09355d054 --- /dev/null +++ b/localization/react-intl/src/app/components/media/MediaOrigin.json @@ -0,0 +1,52 @@ +[ + { + "id": "MediaOrigin.userMerged", + "description": "Message for User Merged", + "defaultMessage": "User Merged" + }, + { + "id": "MediaOrigin.userMergedTooltip", + "description": "Tooltip message for User Merged", + "defaultMessage": "{user} added this media by merging from another cluster" + }, + { + "id": "MediaOrigin.userMatched", + "description": "Message for User Matched", + "defaultMessage": "User Matched" + }, + { + "id": "MediaOrigin.userMatchedTooltip", + "description": "Tooltip message for User Matched", + "defaultMessage": "{user} accepted this media as a suggested match" + }, + { + "id": "MediaOrigin.userAdded", + "description": "This media was added by a user.", + "defaultMessage": "User Added" + }, + { + "id": "MediaOrigin.userAddedTooltip", + "description": "Tooltip message for User Added", + "defaultMessage": "{user} uploaded this media using the Check interface" + }, + { + "id": "MediaOrigin.tiplineSubmitted", + "description": "Message for Tipline Submitted", + "defaultMessage": "Tipline Submitted" + }, + { + "id": "MediaOrigin.tiplineSubmittedTooltip", + "description": "Tooltip message for Tipline Submitted", + "defaultMessage": "Original cluster media submitted by Tipline User" + }, + { + "id": "MediaOrigin.autoMatched", + "description": "Message for Auto Matched", + "defaultMessage": "Auto Matched" + }, + { + "id": "MediaOrigin.autoMatchedTooltip", + "description": "Tooltip message for Auto Matched", + "defaultMessage": "Automatically matched media by Check" + } +] \ No newline at end of file diff --git a/src/app/components/cds/Sandbox.js b/src/app/components/cds/Sandbox.js index da3f8f5cf1..2a7413d835 100644 --- a/src/app/components/cds/Sandbox.js +++ b/src/app/components/cds/Sandbox.js @@ -27,6 +27,7 @@ import TabWrapper from './menus-lists-dialogs/TabWrapper'; import Reorder from '../layout/Reorder'; import AddIcon from '../../icons/settings.svg'; import CalendarIcon from '../../icons/calendar_month.svg'; +import MediaOrigin from '../media/MediaOrigin'; import ListIcon from '../../icons/list.svg'; import FigmaColorLogo from '../../icons/figma_color.svg'; import ArticleCard from '../search/SearchResultsCards/ArticleCard'; @@ -484,6 +485,10 @@ const SandboxComponent = ({ admin }) => { label: 'Tabs', value: 'tabs', }, + { + label: 'Media Cluster Origin Buttons', + value: 'media-cluster-origin-buttons', + }, ]} value={categoryTab} wrapContent @@ -2383,6 +2388,31 @@ const SandboxComponent = ({ admin }) => { } + { (categoryTab === 'all' || categoryTab === 'media-cluster-origin-buttons') && ( +
+
+
+ Media Origin + + + + + + + + + + +
+
+
+ )} ); }; diff --git a/src/app/components/media/MediaCardLargeFooter.js b/src/app/components/media/MediaCardLargeFooter.js index 6d3771fc4e..f97c1f6fcd 100644 --- a/src/app/components/media/MediaCardLargeFooter.js +++ b/src/app/components/media/MediaCardLargeFooter.js @@ -99,26 +99,29 @@ const MediaCardLargeFooter = ({ { footerBody && !inModal ? <>{transcriptionOrExtractedFooter} : null } { !inModal ? - ), ( - - ), ( - - ), + details={[ + ( + + ), + ( + + ), + ( + + ), ]} /> : null diff --git a/src/app/components/media/MediaOrigin.js b/src/app/components/media/MediaOrigin.js new file mode 100644 index 0000000000..f28b87b894 --- /dev/null +++ b/src/app/components/media/MediaOrigin.js @@ -0,0 +1,152 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, FormattedHTMLMessage } from 'react-intl'; +import ButtonMain from '../cds/buttons-checkboxes-chips/ButtonMain'; +import Person from '../../icons/person.svg'; +import PersonAdd from '../../icons/person_add.svg'; +import PersonCheck from '../../icons/person_check.svg'; +import Bolt from '../../icons/bolt.svg'; +import Tipline from '../../icons/question_answer.svg'; +import Tooltip from '../cds/alerts-and-prompts/Tooltip'; + +const allowedTypes = new Set(['typeA', 'typeB', 'typeC', 'typeD', 'typeE']); + +const getIconAndMessage = (type, user) => { + switch (type) { + case 'typeA': + return { + icon: , + message: ( + + ), + tooltipMessage: ( + + ), + }; + case 'typeB': + return { + icon: , + message: ( + + ), + tooltipMessage: ( + + ), + }; + case 'typeC': + return { + icon: , + message: ( + + ), + tooltipMessage: ( + + ), + }; + case 'typeD': + return { + icon: , + message: ( + + ), + tooltipMessage: ( + + ), + }; + case 'typeE': + return { + icon: , + message: ( + + ), + tooltipMessage: ( + + ), + }; + default: + return { + icon: null, + message: null, + tooltipMessage: null, + }; + } +}; + +const MediaOrigin = ({ type, user }) => { + if (!allowedTypes.has(type)) { + return null; + } + + const { icon, message, tooltipMessage } = getIconAndMessage(type, user); + + return ( + + + + + + ); +}; + +MediaOrigin.defaultProps = { + user: 'Unknown User', +}; + +MediaOrigin.propTypes = { + type: PropTypes.string.isRequired, + user: PropTypes.string, +}; + +export default MediaOrigin; diff --git a/src/app/components/media/MediaOrigin.test.js b/src/app/components/media/MediaOrigin.test.js new file mode 100644 index 0000000000..5275db0bcf --- /dev/null +++ b/src/app/components/media/MediaOrigin.test.js @@ -0,0 +1,39 @@ +import React from 'react'; +import MediaOrigin from './MediaOrigin'; +import { mountWithIntl } from '../../../../test/unit/helpers/intl-test'; +import Person from '../../icons/person.svg'; +import PersonAdd from '../../icons/person_add.svg'; +import PersonCheck from '../../icons/person_check.svg'; +import Bolt from '../../icons/bolt.svg'; +import Tipline from '../../icons/question_answer.svg'; + +describe('', () => { + it('should render a button with the correct icon and message for each type', () => { + const userMerged = mountWithIntl(); + expect(userMerged.find(Person).length).toEqual(1); + expect(userMerged.html()).toMatch('User Merged'); + + const userMatched = mountWithIntl(); + expect(userMatched.find(PersonCheck).length).toEqual(1); + expect(userMatched.html()).toMatch('User Matched'); + + + const userAdded = mountWithIntl(); + expect(userAdded.find(PersonAdd).length).toEqual(1); + expect(userAdded.html()).toMatch('User Added'); + + const tiplineSubmitted = mountWithIntl(); + expect(tiplineSubmitted.find(Tipline).length).toEqual(1); + expect(tiplineSubmitted.html()).toMatch('Tipline Submitted'); + + const autoMatched = mountWithIntl(); + expect(autoMatched.find(Bolt).length).toEqual(1); + expect(autoMatched.html()).toMatch('Auto Matched'); + }); + + + it('should return null for invalid type', () => { + const wrapper = mountWithIntl(); + expect(wrapper.find('FormattedMessage').length).toEqual(0); + }); +});