From 059402c8ee642bd095d292726abc1256e4b84979 Mon Sep 17 00:00:00 2001 From: Drikus Roor Date: Wed, 12 Jun 2024 13:01:15 +0200 Subject: [PATCH] Added: Show experiment collection title & description (#1102) * chore: Remove unused code * chore: Format Logo component * feat: Show experiment collection title & description * feat: Render description as markdown & remove title * feat: Format experiment collection as markdown in get experiment collection endpoint serializer * style: Remove vertical padding as the formatted html in combination with the .prose class automatically adds margins --- backend/experiment/serializers.py | 12 +++-- .../ExperimentCollectionDashboard.test.tsx | 1 - .../ExperimentCollectionDashboard.tsx | 24 +++++----- frontend/src/components/Header/Header.tsx | 48 ++++++++++++------- frontend/src/components/Logo/Logo.tsx | 8 ++-- frontend/src/types/Theme.ts | 8 ---- 6 files changed, 56 insertions(+), 45 deletions(-) diff --git a/backend/experiment/serializers.py b/backend/experiment/serializers.py index c9cc71d97..e74b90afe 100644 --- a/backend/experiment/serializers.py +++ b/backend/experiment/serializers.py @@ -24,7 +24,10 @@ def serialize_experiment_collection( serialized = { 'slug': experiment_collection.slug, 'name': experiment_collection.name, - 'description': experiment_collection.description, + 'description': formatter( + experiment_collection.description, + filter_name='markdown' + ), } if experiment_collection.consent: @@ -32,11 +35,14 @@ def serialize_experiment_collection( if experiment_collection.theme_config: serialized['theme'] = serialize_theme( - experiment_collection.theme_config) + experiment_collection.theme_config + ) if experiment_collection.about_content: serialized['aboutContent'] = formatter( - experiment_collection.about_content, filter_name='markdown') + experiment_collection.about_content, + filter_name='markdown' + ) return serialized diff --git a/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.test.tsx b/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.test.tsx index 2305c71a8..1718f54c7 100644 --- a/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.test.tsx +++ b/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.test.tsx @@ -34,7 +34,6 @@ const collectionWithDashboard = { dashboard: [experiment1, experiment2] } const header = { nextExperimentButtonText: 'Next experiment', aboutButtonText: 'About us', - showScore: true } const collectionWithTheme = { dashboard: [experiment1, experiment2], diff --git a/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.tsx b/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.tsx index fa02a0aac..6c5b47538 100644 --- a/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.tsx +++ b/frontend/src/components/ExperimentCollection/ExperimentCollectionDashboard/ExperimentCollectionDashboard.tsx @@ -10,29 +10,31 @@ import IExperiment from "@/types/Experiment"; interface ExperimentCollectionDashboardProps { experimentCollection: ExperimentCollection; participantIdUrl: string | null; + totalScore: number; } export const ExperimentCollectionDashboard: React.FC = ({ experimentCollection, participantIdUrl, totalScore }) => { - + const dashboard = experimentCollection.dashboard; const nextExperimentSlug = experimentCollection.nextExperiment?.slug; - - const headerProps = experimentCollection.theme?.header? { - nextExperimentSlug, + + const headerProps = experimentCollection.theme?.header ? { + nextExperimentSlug, collectionSlug: experimentCollection.slug, ...experimentCollection.theme.header, - totalScore: totalScore - + totalScore, + experimentCollectionDescription: experimentCollection.description + } : undefined; - + const getExperimentHref = (slug: string) => `/${slug}${participantIdUrl ? `?participant_id=${participantIdUrl}` : ""}`; return (
- - {headerProps && ( -
- )} + + {headerProps && ( +
+ )} {/* Experiments */}
    diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 5b1c7554a..e6ad234fe 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -3,15 +3,16 @@ import { Link } from "react-router-dom"; import Rank from "../Rank/Rank"; import Social from "../Social/Social" +import HTML from '@/components/HTML/HTML'; interface HeaderProps { + experimentCollectionTitle: string; + experimentCollectionDescription: string; nextExperimentSlug: string | undefined; nextExperimentButtonText: string; collectionSlug: string; aboutButtonText: string; - showScore: boolean; totalScore: Number; - score: Score; } interface Score { @@ -20,8 +21,16 @@ interface Score { noScoreLabel: string; } -export const Header: React.FC = ({ nextExperimentSlug, nextExperimentButtonText, collectionSlug, aboutButtonText, showScore, totalScore, score }) => { - +export const Header: React.FC = ({ + experimentCollectionDescription, + nextExperimentSlug, + nextExperimentButtonText, + collectionSlug, + aboutButtonText, + totalScore, + score, +}) => { + const social = { 'apps': ['facebook', 'twitter'], 'message': `I scored ${totalScore} points`, @@ -31,40 +40,40 @@ export const Header: React.FC = ({ nextExperimentSlug, nextExperime const useAnimatedScore = (targetScore: number) => { const [score, setScore] = useState(0); - + useEffect(() => { if (targetScore === 0) { setScore(0); return; } - + let animationFrameId: number; - + const nextStep = () => { setScore((prevScore) => { const difference = targetScore - prevScore; const scoreStep = Math.max(1, Math.min(10, Math.ceil(Math.abs(difference) / 10))); - + if (difference === 0) { cancelAnimationFrame(animationFrameId); return prevScore; } - + const newScore = prevScore + Math.sign(difference) * scoreStep; animationFrameId = requestAnimationFrame(nextStep); return newScore; }); }; - + // Start the animation animationFrameId = requestAnimationFrame(nextStep); - + // Cleanup function to cancel the animation frame return () => { cancelAnimationFrame(animationFrameId); }; }, [targetScore]); - + return score; }; @@ -81,10 +90,11 @@ export const Header: React.FC = ({ nextExperimentSlug, nextExperime
); }; - + return (
+
)} {score && totalScore === 0 && (

{score.noScoreLabel}

@@ -109,4 +119,6 @@ export const Header: React.FC = ({ nextExperimentSlug, nextExperime ); } + + export default Header; diff --git a/frontend/src/components/Logo/Logo.tsx b/frontend/src/components/Logo/Logo.tsx index d93be1cb3..42b918d5e 100644 --- a/frontend/src/components/Logo/Logo.tsx +++ b/frontend/src/components/Logo/Logo.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import useBoundStore from "@/util/stores"; -const Logo: React.FC<{logoClickConfirm: string | null}> = ({logoClickConfirm=null}) => { +const Logo: React.FC<{ logoClickConfirm: string | null }> = ({ logoClickConfirm = null }) => { const theme = useBoundStore((state) => state.theme); const logoUrl = theme?.logoUrl ?? LOGO_URL; @@ -26,14 +26,14 @@ const Logo: React.FC<{logoClickConfirm: string | null}> = ({logoClickConfirm=nul "aria-label": "Logo", style: { backgroundImage: `url(${logoUrl})` }, }; - + return ( <> - { URLS.AMLHome.startsWith("http") ? ( + {URLS.AMLHome.startsWith("http") ? ( {LOGO_TITLE} - ) : ( + ) : ( {LOGO_TITLE} diff --git a/frontend/src/types/Theme.ts b/frontend/src/types/Theme.ts index 0b08c56c3..674c5a246 100644 --- a/frontend/src/types/Theme.ts +++ b/frontend/src/types/Theme.ts @@ -1,13 +1,6 @@ export interface Header { nextExperimentButtonText: string; aboutButtonText: string; - showScore: boolean; -}; - -export interface Header { - nextExperimentButtonText: string; - aboutButtonText: string; - showScore: boolean; }; export interface Logo { @@ -22,7 +15,6 @@ export interface Footer { privacy: string; } - export default interface Theme { backgroundUrl: string; bodyFontUrl: string;