Skip to content

Commit a720e5e

Browse files
krisantrobusnkrantzserifluouskodiakhq[bot]
authored
feat(tooltip): add keyboard shortcuts to tooltip (#4161)
* feat(keyboard-key): component init * chore(tools): plopfile syntax fix * chore(tools): plopfile syntax fix * chore(tools): plopfile syntax fix * feat(keyboard-key): styled w/ hook unit tests * feat(keyboard-key): added variants and styles * chore(plop): update tsx dependency * chore(keyboard-key): internal exports in core * chore(keyboard-key): typedocs & build * feat(keyboard-key): change to infline-flex style * chore(keyboard-key): added stroy * chore(keyboard-key): linting * fix(keyboard-key): command logic * feat(design-tokens): added new box shadows to support keyboard-keys * chore(ci-cd): added chagesets * fix(keyboard-key): boxShadow stylings * fix(keyboard-key): remove null component wrapper * fix(keyboard-key): aria-hidden * chore(keyboard-key): refactor * chore(keyboard-key): code cleanup * chore(keyboard-key): code cleanup * chore(keyboard-key): typedocs * chore(keyboard-key): fix tests * fix(keyboard-key): aria and diableBrowserShortcuts * fix(keyboard-key): props fix * chore(keyboard-key): playgorund storybook * chore(keyboard-key): formatting fix * chore(keyboard-key): stories update * chore(keyboard-key): formatting fix * chore(keyboard-key): typo * Update .changeset/sweet-mugs-admire.md Co-authored-by: Nora Krantz <[email protected]> * Update .changeset/shaggy-sheep-confess.md Co-authored-by: Nora Krantz <[email protected]> * chore(keyboard-key): address PR comments * feat(keyboard-tooltip): wip * chore(tooltip): update types * chore(tooltip): update types * chore(tooltip): better var naming * chore(tooltip): fail safe check * chore(tooltip): linting * chore(tooltip): linting * chore(tooltip): yarn lock * chore(tooltip): changeset * chore(tooltip): fix spelling * chore(tooltip): uppdate type name * feat(tooltip): add anonymous keybaord action to tooltip * Update packages/paste-core/components/tooltip/src/Tooltip.tsx Co-authored-by: Sarah <[email protected]> * fix(tooltip): update keyboardkey styles * chore(tooltip): formatting * Update packages/paste-core/components/tooltip/src/Tooltip.tsx Co-authored-by: Nora Krantz <[email protected]> --------- Co-authored-by: Nora Krantz <[email protected]> Co-authored-by: Sarah <[email protected]> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent dbd982f commit a720e5e

File tree

8 files changed

+471
-11
lines changed

8 files changed

+471
-11
lines changed

.changeset/happy-panthers-buy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@twilio-paste/tooltip": minor
3+
"@twilio-paste/core": minor
4+
---
5+
6+
[Tooltip] added the ability to put keyboard combinations using the KeyboardKey in the tooltip

packages/paste-core/components/code-block/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@twilio-paste/flex": "^8.0.0",
3737
"@twilio-paste/heading": "^11.0.0",
3838
"@twilio-paste/icons": "^12.0.0",
39+
"@twilio-paste/keyboard-key": "^0.0.0",
3940
"@twilio-paste/reakit-library": "^2.0.0",
4041
"@twilio-paste/screen-reader-only": "^13.0.0",
4142
"@twilio-paste/spinner": "^14.0.0",
@@ -68,6 +69,7 @@
6869
"@twilio-paste/flex": "^8.1.0",
6970
"@twilio-paste/heading": "^11.1.0",
7071
"@twilio-paste/icons": "^12.2.0",
72+
"@twilio-paste/keyboard-key": "^0.0.0",
7173
"@twilio-paste/reakit-library": "^2.1.1",
7274
"@twilio-paste/screen-reader-only": "^13.1.0",
7375
"@twilio-paste/spinner": "^14.1.2",

packages/paste-core/components/tooltip/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
"@twilio-paste/customization": "^8.0.0",
3232
"@twilio-paste/design-tokens": "^10.0.0",
3333
"@twilio-paste/icons": "^12.0.0",
34+
"@twilio-paste/keyboard-key": "^0.0.0",
3435
"@twilio-paste/reakit-library": "^2.0.0",
3536
"@twilio-paste/spinner": "^14.0.0",
37+
"@twilio-paste/stack": "^8.1.0",
3638
"@twilio-paste/style-props": "^9.0.0",
3739
"@twilio-paste/styling-library": "^3.0.0",
3840
"@twilio-paste/text": "^10.0.0",
@@ -52,8 +54,10 @@
5254
"@twilio-paste/customization": "^8.1.0",
5355
"@twilio-paste/design-tokens": "^10.2.0",
5456
"@twilio-paste/icons": "^12.2.0",
57+
"@twilio-paste/keyboard-key": "^0.0.0",
5558
"@twilio-paste/reakit-library": "^2.1.0",
5659
"@twilio-paste/spinner": "^14.1.0",
60+
"@twilio-paste/stack": "^8.1.0",
5761
"@twilio-paste/style-props": "^9.1.0",
5862
"@twilio-paste/styling-library": "^3.0.0",
5963
"@twilio-paste/text": "^10.1.0",

packages/paste-core/components/tooltip/src/Tooltip.tsx

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Box, safelySpreadBoxProps } from "@twilio-paste/box";
22
import type { BoxProps } from "@twilio-paste/box";
3+
import { KeyboardKey, KeyboardKeyGroup } from "@twilio-paste/keyboard-key";
4+
import { Stack } from "@twilio-paste/stack";
35
import { Text } from "@twilio-paste/text";
46
import { StyledBase } from "@twilio-paste/theme";
57
import { TooltipPrimitive, TooltipPrimitiveReference, useTooltipPrimitiveState } from "@twilio-paste/tooltip-primitive";
@@ -60,17 +62,48 @@ export interface TooltipProps extends TooltipPrimitiveInitialState {
6062
* @memberof TooltipProps
6163
*/
6264
text: string;
65+
actionHeader?: never;
66+
keyCombinationsActions?: never;
6367
}
6468

69+
interface KeyboardActions {
70+
name: string;
71+
eventKeyCombination: string[];
72+
disabled?: boolean;
73+
}
74+
75+
export interface KeyboardKeyTooltipProps
76+
extends Omit<TooltipProps, "text" | "keyCombinationsActions" | "actionHeader"> {
77+
text?: never;
78+
/**
79+
* The mapping of action names to their respective key combinations.
80+
*
81+
* @type {Array<KeyboardActions>}
82+
* @memberof KeyboardKeyTooltipProps
83+
*/
84+
keyCombinationsActions: Array<KeyboardActions>;
85+
/**
86+
* The header content of the Tooltip.
87+
*
88+
* @type {string}
89+
* @memberof KeyboardKeyTooltipProps
90+
*/
91+
actionHeader?: string;
92+
}
93+
94+
// Union will stop users from adding types from both TooltipProps and KeyboardKeyTooltipProps at the same time.
95+
export type TooltipVariantProps = TooltipProps | KeyboardKeyTooltipProps;
96+
6597
/*
6698
*Tooltip's current structure does not allow for customization of its arrow.
6799
*TODO: refactor Tooltip so that the styling of its arrow can be customized
68100
*using Customization Provider.
69101
*/
70102

71-
const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
72-
({ baseId, children, element = "TOOLTIP", state, text, ...props }, ref) => {
103+
const Tooltip = React.forwardRef<HTMLDivElement, TooltipVariantProps>(
104+
({ baseId, children, element = "TOOLTIP", state, text, actionHeader, keyCombinationsActions, ...props }, ref) => {
73105
const tooltip = state || useTooltipPrimitiveState({ baseId: `paste-tooltip-${useUID()}`, ...props });
106+
74107
return (
75108
<>
76109
{React.Children.only(
@@ -82,15 +115,62 @@ const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
82115
{/* import Paste Theme Based Styles due to portal positioning. */}
83116
<StyledBase>
84117
<TooltipArrow {...tooltip} />
85-
<Text
86-
element={`${element}_TEXT`}
87-
as="span"
88-
color="colorTextInverse"
89-
fontSize="fontSize20"
90-
lineHeight="lineHeight10"
91-
>
92-
{text}
93-
</Text>
118+
{text && !keyCombinationsActions && (
119+
<Text
120+
element={`${element}_TEXT`}
121+
as="span"
122+
color="colorTextInverse"
123+
fontSize="fontSize20"
124+
lineHeight="lineHeight10"
125+
>
126+
{text}
127+
</Text>
128+
)}
129+
{keyCombinationsActions && !text && (
130+
<Box color="colorTextInverse" fontSize="fontSize20" lineHeight="lineHeight10">
131+
<Stack orientation="vertical" spacing="space40">
132+
{actionHeader && (
133+
<Text
134+
element={`${element}_HEADER`}
135+
fontWeight="fontWeightSemibold"
136+
as="p"
137+
color="inherit"
138+
fontSize="inherit"
139+
lineHeight="inherit"
140+
>
141+
{actionHeader}
142+
</Text>
143+
)}
144+
{keyCombinationsActions.map((action, idx) => (
145+
<Box display="flex" key={`action-${idx}`} justifyContent="space-between">
146+
{action.name && (
147+
<Text
148+
element={`${element}_ACTION_TEXT`}
149+
as="span"
150+
color="inherit"
151+
fontSize="inherit"
152+
lineHeight="inherit"
153+
marginRight="space70"
154+
>
155+
{action.name}
156+
</Text>
157+
)}
158+
<KeyboardKeyGroup
159+
variant="inverse"
160+
element={`${element}_ACTION_KEY_GROUP`}
161+
disabled={action.disabled}
162+
>
163+
{action.eventKeyCombination.map((key, i) => (
164+
<KeyboardKey key={`key-${idx}-${i}`} element={`${element}_ACTION_KEY`}>
165+
{key}
166+
</KeyboardKey>
167+
))}
168+
</KeyboardKeyGroup>
169+
</Box>
170+
))}
171+
</Stack>
172+
</Box>
173+
)}
94174
</StyledBase>
95175
</TooltipPrimitive>
96176
</>

packages/paste-core/components/tooltip/src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { TooltipStateReturn } from "./Tooltip";
33
export { Tooltip, useTooltipState } from "./Tooltip";
44
export type {
55
TooltipProps,
6+
TooltipVariantProps,
7+
KeyboardKeyTooltipProps,
68
TooltipStateReturn,
79
UseTooltipInitialStateProps,
810
} from "./Tooltip";
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import type { StoryFn } from "@storybook/react";
2+
import { Anchor } from "@twilio-paste/anchor";
3+
import { Box } from "@twilio-paste/box";
4+
import { Button } from "@twilio-paste/button";
5+
import { CustomizationProvider } from "@twilio-paste/customization";
6+
import { InformationIcon } from "@twilio-paste/icons/esm/InformationIcon";
7+
import { Stack } from "@twilio-paste/stack";
8+
import { Text } from "@twilio-paste/text";
9+
import { Theme, useTheme } from "@twilio-paste/theme";
10+
import * as React from "react";
11+
12+
import { Tooltip, useTooltipState } from "../src";
13+
14+
// eslint-disable-next-line import/no-default-export
15+
export default {
16+
title: "Components/Tooltip/KeyboardKey",
17+
excludeStories: ["StateHookExample"],
18+
component: Tooltip,
19+
parameters: {
20+
chromatic: { delay: 3000, diffThreshold: 0.2 },
21+
},
22+
};
23+
24+
export const Default = (): React.ReactNode => {
25+
return (
26+
<Box as="div" minHeight="400px">
27+
<Tooltip
28+
visible
29+
actionHeader="Search shortcuts"
30+
keyCombinationsActions={[
31+
{ name: "Mac", eventKeyCombination: ["Command", "K"] },
32+
{ name: "Windows", eventKeyCombination: ["Control", "K"] },
33+
]}
34+
>
35+
<Button aria-keyshortcuts="command+k" variant="primary">
36+
Click to search
37+
</Button>
38+
</Tooltip>
39+
</Box>
40+
);
41+
};
42+
43+
export const CustomizedTooltip: StoryFn = (_args, { parameters: { isTestEnvironment } }) => {
44+
const currentTheme = useTheme();
45+
return (
46+
<CustomizationProvider
47+
disableAnimations={isTestEnvironment}
48+
theme={currentTheme}
49+
elements={{
50+
TOOLTIP: {
51+
backgroundColor: "colorBackgroundErrorWeakest",
52+
borderColor: "colorBorderDestructive",
53+
textAlign: "center",
54+
},
55+
TOOLTIP_ACTION_TEXT: {
56+
color: "colorTextErrorStrong",
57+
},
58+
TOOLTIP_HEADER: {
59+
color: "colorTextLinkDestructive",
60+
fontWeight: "fontWeightBold",
61+
},
62+
TOOLTIP_ACTION_KEY_GROUP: {
63+
backgroundColor: "colorBackgroundStrong",
64+
padding: "space30",
65+
},
66+
TOOLTIP_ACTION_KEY: {
67+
backgroundColor: "colorBackgroundPrimary",
68+
color: "colorTextInverse",
69+
},
70+
CUSTOM_TOOLTIP: {
71+
backgroundColor: "colorBackgroundSuccessWeakest",
72+
borderColor: "colorBorderSuccess",
73+
textAlign: "left",
74+
},
75+
CUSTOM_TOOLTIP_ACTION_TEXT: {
76+
color: "colorTextSuccess",
77+
},
78+
CUSTOM_TOOLTIP_HEADER: {
79+
color: "colorTextLinkStrongest",
80+
fontWeight: "fontWeightLight",
81+
},
82+
CUSTOM_TOOLTIP_ACTION_KEY_GROUP: {
83+
backgroundColor: "colorBackgroundInverse",
84+
padding: "space30",
85+
},
86+
CUSTOM_TOOLTIP_ACTION_KEY: {
87+
backgroundColor: "colorBackgroundBusy",
88+
color: "colorTextInverse",
89+
fontFamily: "fontFamilyCode",
90+
},
91+
}}
92+
>
93+
<Box as="div" display="flex" columnGap="space80">
94+
<Box minWidth="200px">
95+
<Tooltip
96+
visible
97+
actionHeader="Search shortcuts"
98+
keyCombinationsActions={[
99+
{ name: "Mac", eventKeyCombination: ["Command", "K"] },
100+
{ name: "Windows", eventKeyCombination: ["Control", "K"] },
101+
]}
102+
>
103+
<Button aria-keyshortcuts="command+k" variant="primary">
104+
Click to search
105+
</Button>
106+
</Tooltip>
107+
</Box>
108+
<Box minWidth="600px">
109+
<Tooltip
110+
element="CUSTOM_TOOLTIP"
111+
visible
112+
actionHeader="Search shortcuts"
113+
keyCombinationsActions={[
114+
{ name: "Mac", eventKeyCombination: ["Command", "K"] },
115+
{ name: "Windows", eventKeyCombination: ["Control", "K"] },
116+
]}
117+
>
118+
<Button aria-keyshortcuts="command+k" variant="primary">
119+
Click to search
120+
</Button>
121+
</Tooltip>
122+
</Box>
123+
</Box>
124+
</CustomizationProvider>
125+
);
126+
};
127+
128+
CustomizedTooltip.storyName = "Customized Tooltip";
129+
CustomizedTooltip.parameters = {
130+
parameters: {
131+
chromatic: { disableSnapshot: true },
132+
},
133+
a11y: {
134+
// no need to a11y check customization
135+
disable: true,
136+
},
137+
};

0 commit comments

Comments
 (0)