Skip to content

Commit

Permalink
feat: tooltip help content (#715)
Browse files Browse the repository at this point in the history
* feat: tooltip template

* feat: expose TooltipTemplate component

* refactor: follow style guidelines in react-science

* refactor: rename 'TooltipTemplate' to 'TooltipHelpContent'

* wrap the story in a tooltip

* chore(story): add a control on the intent

* refactor(storybook): define the intent arg on a component level

---------

Co-authored-by: Michaël Zasso <targos@protonmail.com>
  • Loading branch information
hamed-musallam and targos authored Apr 15, 2024
1 parent 30b6212 commit 287f1c3
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 86 deletions.
173 changes: 173 additions & 0 deletions src/components/toolbar/TooltipHelpContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import styled from '@emotion/styled';
import { CSSProperties } from 'react';

const shortcutBoxSize = 1.5;

const FlexContainer = styled.div`
display: flex;
justify-content: space-between;
flex-wrap: wrap;
align-items: center;
`;

interface TitleProps {
size: CSSProperties['fontSize'];
}

const Title = styled.span<TitleProps>`
font-size: ${({ size }) => size};
flex: 1;
padding: 5px 0;
text-align: left;
`;
const Description = styled.p`
padding-top: 1rem;
font-size: 0.7rem;
text-align: left;
`;

const ShortcutItem = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding: 0.2rem;
border: 1px solid;
border-radius: 4px;
height: ${shortcutBoxSize}rem;
min-width: ${shortcutBoxSize}rem;
margin-left: 5px;
font-size: 0.75rem;
font-weight: bold;
`;

const SubTitleItem = styled.div`
position: relative;
padding-left: 15px;
box-sizing: border-box;
&::before {
position: absolute;
top: ${shortcutBoxSize / 2}rem;
left: 0;
width: 10px;
height: 1px;
margin: auto;
content: '';
border-bottom: 1px solid;
}
&::after {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 1px;
height: 100%;
content: '';
border-left: 1px solid;
}
&:last-child::after {
height: ${shortcutBoxSize / 2}rem;
}
&:first-child {
margin: 0;
}
`;

export interface TooltipItem {
title: string;
shortcuts?: string[];
subTitles?: Array<Pick<TooltipItem, 'title' | 'shortcuts'>>;
description?: string;
link?: string;
style?: CSSProperties;
}

export function TooltipHelpContent(props: TooltipItem) {
const {
title,
shortcuts = [],
subTitles = [],
description = '',
link,
style = {},
} = props;

return (
<div
style={{
color: 'white',
width: '250px',
padding: '0.5rem',
...style,
}}
>
<FlexContainer>
<Title size="0.9rem">{title}</Title>
<ShortCuts shortcuts={shortcuts} />
</FlexContainer>
<SubTitles items={subTitles} />

{(description || link) && (
<Description>
{description}
{link && (
<a
style={description ? { paddingLeft: '5px' } : {}}
target="_blank"
href={link}
rel="noreferrer"
>
Learn more
</a>
)}
</Description>
)}
</div>
);
}

function ShortCuts({ shortcuts }: { shortcuts: string[] }) {
return (
<div
style={{
display: 'flex',
textWrap: 'nowrap',
}}
>
{shortcuts.map((key) => {
return (
<ShortcutItem key={key}>
<span>{key}</span>
</ShortcutItem>
);
})}
</div>
);
}

function SubTitles({ items }: { items: TooltipItem[] }) {
if (!items || items.length === 0) {
return null;
}

return (
<ul
style={{
paddingLeft: '5px',
listStyle: 'none',
}}
>
{items.map(({ shortcuts = [], title }) => (
<SubTitleItem key={title}>
<FlexContainer>
<Title size="0.7rem">{title}</Title>
<ShortCuts shortcuts={shortcuts} />
</FlexContainer>
</SubTitleItem>
))}
</ul>
);
}
1 change: 1 addition & 0 deletions src/components/toolbar/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './Toolbar';
export * from './PanelPreferencesToolbar';
export * from './TooltipHelpContent';
125 changes: 39 additions & 86 deletions stories/components/toolbar.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Menu, MenuItem } from '@blueprintjs/core';
import { Menu, MenuItem, Tooltip } from '@blueprintjs/core';
import { Meta } from '@storybook/react';
import { CSSProperties, JSX, useState } from 'react';
import { JSX, useState } from 'react';
import { BiClipboard, BiCreditCard, BiPaperclip } from 'react-icons/bi';
import { FaClipboard, FaCreditCard, FaPaperclip } from 'react-icons/fa6';
import { HiClipboard, HiCreditCard, HiOutlinePaperClip } from 'react-icons/hi2';
Expand All @@ -10,6 +10,8 @@ import {
ToolbarItemProps,
ToolbarProps,
PopoverInteractionType,
TooltipItem,
TooltipHelpContent,
} from '../../src/components';

export default {
Expand All @@ -22,6 +24,10 @@ export default {
},
argTypes: {
onClick: { action: 'handle' },
intent: {
options: ['none', 'primary', 'success', 'warning', 'danger'],
control: { type: 'select' },
},
},
} as Meta<ToolbarProps>;

Expand Down Expand Up @@ -402,17 +408,9 @@ MixedItems.argTypes = {
},
};

interface CustomTipItem {
// eslint-disable-next-line react/no-unused-prop-types
id: string;
// eslint-disable-next-line react/no-unused-prop-types
icon: ToolbarItemProps['icon'];
title: string;
shortcuts: string[];
description: string;
}

const itemsShortcuts: CustomTipItem[] = [
const itemsShortcuts: Array<
TooltipItem & { id: string; icon: ToolbarItemProps['icon'] }
> = [
{
id: 'copy',
icon: 'phone',
Expand Down Expand Up @@ -450,20 +448,20 @@ const itemsShortcuts: CustomTipItem[] = [
},
];

export function CustomTipContent() {
export function CustomTooltipContent({ intent }: ToolbarProps) {
return (
<div
style={{
display: 'flex',
height: 200,
}}
>
<Toolbar vertical>
<Toolbar vertical intent={intent}>
{itemsShortcuts.map((item) => (
<Toolbar.Item
key={item.id}
id={item.id}
tooltip={<CustomTip {...item} />}
tooltip={<TooltipHelpContent {...item} />}
icon={item.icon}
tooltipProps={{ minimal: true }}
/>
Expand All @@ -474,76 +472,31 @@ export function CustomTipContent() {
);
}

const styles: Record<
| 'titleContainer'
| 'title'
| 'description'
| 'shortcutContainer'
| 'shortcutItem',
CSSProperties
> = {
titleContainer: {
display: 'flex',
justifyContent: 'space-between',
flexWrap: 'wrap',
alignItems: 'center',
},
title: {
fontSize: '0.9rem',
minWidth: '50%',
padding: '5px 0',
},
description: {
paddingTop: '3rem',
fontSize: '0.7rem',
},
shortcutContainer: {
display: 'flex',
textWrap: 'nowrap',
},
shortcutItem: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '0.5em',
border: '1px solid #ccc',
borderRadius: '4px',
height: 'calc(0.5em + 15px)',
minWidth: 'calc(0.5em + 15px)' /* Adjust padding and border width */,
marginLeft: '5px',
fontSize: '0.6rem',
fontWeight: 'bolder',
},
};

function CustomTip({ title, shortcuts, description }: CustomTipItem) {
export function TooltipHelpContentStory({ intent }: ToolbarProps) {
return (
<div style={{ width: 250, padding: '0.5rem' }}>
<div style={styles.titleContainer}>
<span style={styles.title}>{title}</span>
<div style={styles.shortcutContainer}>
{shortcuts?.map((key, index) => {
return (
<div
key={key}
style={{
...styles.shortcutItem,
...(index === 0 && { margin: 0 }),
}}
>
<span>{key}</span>{' '}
</div>
);
})}
</div>
</div>

<p style={styles.description}>
{description}{' '}
<a target="_blank" href="">
Learn more
</a>{' '}
</p>
</div>
<Tooltip
minimal
intent={intent}
isOpen
content={
<TooltipHelpContent
title="Cut text tool"
shortcuts={['Ctrl', 'x']}
subTitles={[
{
title: 'sub title 1',
shortcuts: ['x'],
},
{
title: 'sub title 2',
shortcuts: ['z'],
},
]}
description="Cut selected item to clipboard."
/>
}
/>
);
}

TooltipHelpContentStory.storyName = 'TooltipHelpContent';

0 comments on commit 287f1c3

Please sign in to comment.