From f8511b21cac91d7bf3cace828ceb337848bb3ee8 Mon Sep 17 00:00:00 2001 From: Nick O'Ferrall Date: Mon, 4 Dec 2023 12:02:23 +0000 Subject: [PATCH] feat: add tooltip to activity library card (#9236) * show title and type below activity card * make activity library card dynamic * add retro background swirls * add background images for all meeting types * fix custom card size * truncate text if there is no space * clean up * use grape 100 * add premortem and postmortem imgs * remove bg from categories themes * move background img to div * add dummy tooltip * fix tailwind bg colours * show tooltip after delay * show descriptions in tooltip * clean up * update tooltip position * update CATEGORY_THEMES colours * remove tailwind config safelist --- .../ActivityLibrary/ActivityCard.tsx | 67 ++++++++++++++++++- .../ActivityLibrary/ActivityLibrary.tsx | 7 +- .../ActivityLibraryCardDescription.tsx | 45 ++++++++----- .../components/ActivityLibrary/Categories.ts | 20 +++--- 4 files changed, 105 insertions(+), 34 deletions(-) diff --git a/packages/client/components/ActivityLibrary/ActivityCard.tsx b/packages/client/components/ActivityLibrary/ActivityCard.tsx index e2699eccb51..4885cab1900 100644 --- a/packages/client/components/ActivityLibrary/ActivityCard.tsx +++ b/packages/client/components/ActivityLibrary/ActivityCard.tsx @@ -1,13 +1,21 @@ import clsx from 'clsx' -import React, {PropsWithChildren} from 'react' +import React, {PropsWithChildren, useEffect, useRef, useState} from 'react' import {upperFirst} from '../../utils/upperFirst' import {MeetingTypeEnum} from '../../__generated__/NewMeetingQuery.graphql' +import {ActivityCard_template$key} from '../../__generated__/ActivityCard_template.graphql' import {backgroundImgMap, CategoryID, MEETING_TYPE_TO_CATEGORY} from './Categories' import {twMerge} from 'tailwind-merge' +import {Tooltip} from '../../ui/Tooltip/Tooltip' +import {TooltipTrigger} from '../../ui/Tooltip/TooltipTrigger' +import {TooltipContent} from '../../ui/Tooltip/TooltipContent' +import {useFragment} from 'react-relay' +import graphql from 'babel-plugin-relay/macro' +import {ActivityLibraryCardDescription} from './ActivityLibraryCardDescription' export interface CardTheme { primary: string secondary: string + text: string } type ActivityCardImageProps = { @@ -44,14 +52,51 @@ export interface ActivityCardProps { badge?: React.ReactNode children?: React.ReactNode type?: MeetingTypeEnum + templateRef?: ActivityCard_template$key } export const ActivityCard = (props: ActivityCardProps) => { - const {className, theme, title, children, type, badge} = props + const {className, theme, title, children, type, badge, templateRef} = props const category = type && MEETING_TYPE_TO_CATEGORY[type] + const [showTooltip, setShowTooltip] = useState(false) + const hoverTimeout = useRef(null) + + const template = useFragment( + graphql` + fragment ActivityCard_template on MeetingTemplate { + ...ActivityLibraryCardDescription_template + } + `, + templateRef ?? null + ) + + const handleMouseEnter = () => { + hoverTimeout.current = setTimeout(() => { + setShowTooltip(true) + }, 500) + } + + const handleMouseLeave = () => { + if (hoverTimeout.current) { + clearTimeout(hoverTimeout.current) + } + setShowTooltip(false) + } + + useEffect(() => { + return () => { + if (hoverTimeout.current) { + clearTimeout(hoverTimeout.current) + } + } + }, []) return ( -
+
{ {children}
{badge}
+ {template && ( + + +
+ + +
{title}
+ +
+ + )}
{title && category && (
diff --git a/packages/client/components/ActivityLibrary/ActivityLibrary.tsx b/packages/client/components/ActivityLibrary/ActivityLibrary.tsx index 1cd9bbe61c0..f275cb3f36c 100644 --- a/packages/client/components/ActivityLibrary/ActivityLibrary.tsx +++ b/packages/client/components/ActivityLibrary/ActivityLibrary.tsx @@ -16,7 +16,6 @@ import IconLabel from '../IconLabel' import {ActivityBadge} from './ActivityBadge' import {ActivityCardImage} from './ActivityCard' import {ActivityLibraryCard} from './ActivityLibraryCard' -import {ActivityLibraryCardDescription} from './ActivityLibraryCardDescription' import { CategoryID, CATEGORY_ID_TO_NAME, @@ -70,6 +69,7 @@ graphql` isRecommended isFree ...ActivityLibrary_templateSearchDocument @relay(mask: false) + ...ActivityCard_template ...ActivityLibraryCardDescription_template } ` @@ -168,6 +168,7 @@ const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => { theme={CATEGORY_THEMES[template.category as CategoryID]} title={template.name} type={template.type} + templateRef={template} badge={ !template.isFree ? ( Premium @@ -179,10 +180,6 @@ const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => { src={template.illustrationUrl} category={template.category as CategoryID} /> - ) diff --git a/packages/client/components/ActivityLibrary/ActivityLibraryCardDescription.tsx b/packages/client/components/ActivityLibrary/ActivityLibraryCardDescription.tsx index 681c85a2539..e9927587efe 100644 --- a/packages/client/components/ActivityLibrary/ActivityLibraryCardDescription.tsx +++ b/packages/client/components/ActivityLibrary/ActivityLibraryCardDescription.tsx @@ -14,18 +14,20 @@ interface RetroDescriptionProps { prompts: ActivityLibraryCardDescription_template$data['prompts'] } -const RetroDescription = (props: RetroDescriptionProps) => { +export const RetroDescription = (props: RetroDescriptionProps) => { const {prompts} = props - return ( <> - {prompts!.map((prompt, index) => ( -
+ {prompts!.map((prompt) => ( +
-
{prompt.question}
+
+
{prompt.question}
+
{prompt.description}
+
))} @@ -38,16 +40,16 @@ interface PokerDescriptionProps { const PokerDescription = (props: PokerDescriptionProps) => { const {dimensions} = props - return ( <> - {dimensions!.map((dimension, index) => ( -
-
+ {dimensions!.map((dimension) => ( +
+
-
- {dimension.name}: {dimension.selectedScale.name} +
+
{dimension.name}
+
{dimension.selectedScale.name}
))} @@ -70,9 +72,11 @@ const ActionDescription = () => { return ( <> {items.map((item, index) => ( -
-
{item.icon}
-
{item.description}
+
+
{item.icon}
+
+
{item.description}
+
))} @@ -90,9 +94,11 @@ const TeamPromptDescription = () => { return ( <> {items.map((item, index) => ( -
-
{item.icon}
-
{item.description}
+
+
{item.icon}
+
+
{item.description}
+
))} @@ -113,6 +119,7 @@ export const ActivityLibraryCardDescription = (props: Props) => { type ... on PokerTemplate { dimensions { + id name description selectedScale { @@ -122,8 +129,10 @@ export const ActivityLibraryCardDescription = (props: Props) => { } ... on ReflectTemplate { prompts { + id question groupColor + description } } } diff --git a/packages/client/components/ActivityLibrary/Categories.ts b/packages/client/components/ActivityLibrary/Categories.ts index 1fa875b32d0..5c02abf0b68 100644 --- a/packages/client/components/ActivityLibrary/Categories.ts +++ b/packages/client/components/ActivityLibrary/Categories.ts @@ -19,16 +19,20 @@ export const MAIN_CATEGORIES = [ ] as const export type CategoryID = typeof MAIN_CATEGORIES[number] -export const DEFAULT_CARD_THEME: CardTheme = {primary: 'bg-slate-500', secondary: 'bg-slate-200'} +export const DEFAULT_CARD_THEME: CardTheme = { + primary: 'bg-slate-500', + secondary: 'bg-slate-200', + text: 'text-slate-500' +} export const CATEGORY_THEMES: Record = { - standup: {primary: 'aqua-400', secondary: 'aqua-100'}, - estimation: {primary: 'tomato-500', secondary: 'tomato-100'}, - retrospective: {primary: 'grape-500', secondary: '[#F2E1F7]'}, - feedback: {primary: 'jade-400', secondary: 'jade-100'}, - strategy: {primary: 'rose-500', secondary: 'rose-100'}, - premortem: {primary: 'gold-500', secondary: 'gold-100'}, - postmortem: {primary: 'grass-500', secondary: 'grass-100'} + standup: {primary: 'bg-aqua-400', secondary: 'bg-aqua-100', text: 'text-aqua-400'}, + estimation: {primary: 'bg-tomato-500', secondary: 'bg-tomato-100', text: 'text-tomato-500'}, + retrospective: {primary: 'bg-grape-500', secondary: 'bg-[#F2E1F7]', text: 'text-grape-500'}, + feedback: {primary: 'bg-jade-400', secondary: 'bg-jade-100', text: 'text-jade-400'}, + strategy: {primary: 'bg-rose-500', secondary: 'bg-rose-100', text: 'text-rose-500'}, + premortem: {primary: 'bg-gold-500', secondary: 'bg-gold-100', text: 'text-gold-500'}, + postmortem: {primary: 'bg-grass-500', secondary: 'bg-grass-100', text: 'text-grass-500'} } export const QUICK_START_CATEGORY_ID = 'recommended'