Skip to content

Commit

Permalink
feat: add tooltip to activity library card (#9236)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nickoferrall committed Dec 4, 2023
1 parent 3bf4b81 commit f8511b2
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 34 deletions.
67 changes: 64 additions & 3 deletions packages/client/components/ActivityLibrary/ActivityCard.tsx
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -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<NodeJS.Timeout | null>(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 (
<div className='flex w-full flex-col'>
<div
className='flex w-full flex-col'
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div
className={twMerge(
'relative flex h-full min-w-0 flex-col overflow-hidden rounded-lg',
Expand All @@ -63,6 +108,22 @@ export const ActivityCard = (props: ActivityCardProps) => {
{children}
<div className='absolute bottom-0 right-0'>{badge}</div>
</div>
{template && (
<Tooltip open={showTooltip}>
<TooltipTrigger asChild>
<div className='h-50 w-50 text-sky-500 hover:text-sky-700' />
</TooltipTrigger>
<TooltipContent
side='bottom'
align='center'
sideOffset={20}
className='max-w-md whitespace-normal rounded-lg bg-white p-4 text-left text-slate-700 shadow-lg hover:cursor-pointer sm:max-w-sm'
>
<div className='mb-2 text-left text-lg font-semibold'>{title}</div>
<ActivityLibraryCardDescription templateRef={template} />
</TooltipContent>
</Tooltip>
)}
</div>
{title && category && (
<div className='mt-2 px-2 pb-2'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -70,6 +69,7 @@ graphql`
isRecommended
isFree
...ActivityLibrary_templateSearchDocument @relay(mask: false)
...ActivityCard_template
...ActivityLibraryCardDescription_template
}
`
Expand Down Expand Up @@ -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 ? (
<ActivityBadge className='m-2 bg-gold-300 text-grape-700'>Premium</ActivityBadge>
Expand All @@ -179,10 +180,6 @@ const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => {
src={template.illustrationUrl}
category={template.category as CategoryID}
/>
<ActivityLibraryCardDescription
className='hidden group-hover/card:flex'
templateRef={template}
/>
</ActivityLibraryCard>
</Link>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
<div key={index} className='flex items-center gap-x-2'>
{prompts!.map((prompt) => (
<div key={prompt.id} className='mb-1 flex flex-col items-start py-1 sm:flex-row'>
<div
className='mt-1 h-3 w-3 shrink-0 self-start rounded-full'
className='mt-1 mr-4 h-3 w-3 shrink-0 self-start rounded-full'
style={{backgroundColor: prompt.groupColor}}
/>
<div className='text-sm font-medium'>{prompt.question}</div>
<div className='flex min-w-0 flex-grow flex-col'>
<div className='text-sm font-semibold'>{prompt.question}</div>
<div className='text-sm font-normal'>{prompt.description}</div>
</div>
</div>
))}
</>
Expand All @@ -38,16 +40,16 @@ interface PokerDescriptionProps {

const PokerDescription = (props: PokerDescriptionProps) => {
const {dimensions} = props

return (
<>
{dimensions!.map((dimension, index) => (
<div key={index} className='flex items-center gap-x-2'>
<div className='mt-1 shrink-0 self-start'>
{dimensions!.map((dimension) => (
<div key={dimension.id} className='mb-1 flex items-center py-1 sm:flex-row'>
<div className='mr-4 shrink-0 self-start'>
<LinearScale className='h-4 w-4' />
</div>
<div className='text-sm font-medium'>
{dimension.name}: <span className='font-semibold'>{dimension.selectedScale.name}</span>
<div className='flex min-w-0 flex-grow flex-col'>
<div className='text-sm font-semibold'>{dimension.name}</div>
<div className='text-sm font-normal'>{dimension.selectedScale.name}</div>
</div>
</div>
))}
Expand All @@ -70,9 +72,11 @@ const ActionDescription = () => {
return (
<>
{items.map((item, index) => (
<div key={index} className='flex items-center gap-x-2'>
<div className='mt-1 shrink-0 self-start'>{item.icon}</div>
<div className='text-sm font-medium'>{item.description}</div>
<div key={index} className='mb-1 flex items-start py-1 sm:flex-row'>
<div className='mr-4 flex shrink-0 items-center self-start'>{item.icon}</div>
<div className='flex min-w-0 flex-grow flex-col'>
<div className='text-sm font-normal'>{item.description}</div>
</div>
</div>
))}
</>
Expand All @@ -90,9 +94,11 @@ const TeamPromptDescription = () => {
return (
<>
{items.map((item, index) => (
<div key={index} className='flex items-center gap-x-2'>
<div className='mt-1 shrink-0 self-start'>{item.icon}</div>
<div className='text-sm font-medium'>{item.description}</div>
<div key={index} className='mb-1 flex items-start py-1 sm:flex-row'>
<div className='mr-4 flex shrink-0 items-center self-start'>{item.icon}</div>
<div className='flex min-w-0 flex-grow flex-col'>
<div className='text-sm font-normal'>{item.description}</div>
</div>
</div>
))}
</>
Expand All @@ -113,6 +119,7 @@ export const ActivityLibraryCardDescription = (props: Props) => {
type
... on PokerTemplate {
dimensions {
id
name
description
selectedScale {
Expand All @@ -122,8 +129,10 @@ export const ActivityLibraryCardDescription = (props: Props) => {
}
... on ReflectTemplate {
prompts {
id
question
groupColor
description
}
}
}
Expand Down
20 changes: 12 additions & 8 deletions packages/client/components/ActivityLibrary/Categories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CategoryID, CardTheme> = {
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'
Expand Down

0 comments on commit f8511b2

Please sign in to comment.