Add new Tooltip component

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-03-16 09:36:52 +00:00
parent 85df833fe3
commit 4557beb646
8 changed files with 308 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/canon': minor
---
Added a new Tooltip component to Canon.
+27
View File
@@ -12,6 +12,7 @@ import { default as React_2 } from 'react';
import * as React_3 from 'react';
import { ReactNode } from 'react';
import { RefAttributes } from 'react';
import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip';
// @public (undocumented)
export type AlignItems = 'stretch' | 'start' | 'center' | 'end';
@@ -972,6 +973,32 @@ export interface TextProps {
weight?: 'regular' | 'bold' | Partial<Record<Breakpoint, 'regular' | 'bold'>>;
}
// @public (undocumented)
export const Tooltip: {
Provider: React_2.FC<Tooltip_2.Provider.Props>;
Root: React_2.FC<Tooltip_2.Root.Props>;
Trigger: React_2.ForwardRefExoticComponent<
Omit<Tooltip_2.Trigger.Props & React_2.RefAttributes<any>, 'ref'> &
React_2.RefAttributes<any>
>;
Portal: typeof Tooltip_2.Portal;
Positioner: React_2.ForwardRefExoticComponent<
Omit<
Tooltip_2.Positioner.Props & React_2.RefAttributes<HTMLDivElement>,
'ref'
> &
React_2.RefAttributes<HTMLDivElement>
>;
Popup: React_2.ForwardRefExoticComponent<
Omit<Tooltip_2.Popup.Props & React_2.RefAttributes<HTMLDivElement>, 'ref'> &
React_2.RefAttributes<HTMLDivElement>
>;
Arrow: React_2.ForwardRefExoticComponent<
Omit<Tooltip_2.Arrow.Props & React_2.RefAttributes<HTMLDivElement>, 'ref'> &
React_2.RefAttributes<HTMLDivElement>
>;
};
// @public (undocumented)
export const useIcons: () => IconContextProps;
@@ -0,0 +1,81 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Tooltip } from './Tooltip';
import { Button } from '../Button/Button';
const meta = {
title: 'Components/Tooltip',
component: Tooltip.Root,
} satisfies Meta<typeof Tooltip.Root>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: (
<>
<Tooltip.Trigger
render={props => (
<Button {...props} size="small">
Button
</Button>
)}
/>
<Tooltip.Portal>
<Tooltip.Positioner sideOffset={10}>
<Tooltip.Popup>Nice!</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
</>
),
},
};
export const Open: Story = {
args: {
...Default.args,
open: true,
},
};
export const WithArrow: Story = {
args: {
open: true,
children: (
<>
<Tooltip.Trigger
render={props => (
<Button {...props} size="small">
Button
</Button>
)}
/>
<Tooltip.Portal>
<Tooltip.Positioner sideOffset={10}>
<Tooltip.Popup>
<Tooltip.Arrow />
Nice!
</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
</>
),
},
};
@@ -0,0 +1,83 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.canon-TooltipPopup {
box-sizing: border-box;
font-size: 0.875rem;
line-height: 1.25rem;
display: flex;
flex-direction: column;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
background-color: canvas;
transform-origin: var(--transform-origin);
transition: transform 150ms, opacity 150ms;
background-color: var(--canon-bg-surface-1);
color: var(--canon-fg-primary);
outline: 1px solid var(--canon-border);
box-shadow: 0 10px 15px -3px var(--canon-border),
0 4px 6px -4px var(--canon-border);
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
transform: scale(0.9);
}
&[data-instant] {
transition-duration: 0ms;
}
}
.canon-TooltipArrow {
display: flex;
&[data-side='top'] {
bottom: -8px;
rotate: 180deg;
}
&[data-side='bottom'] {
top: -8px;
rotate: 0deg;
}
&[data-side='left'] {
right: -13px;
rotate: 90deg;
}
&[data-side='right'] {
left: -13px;
rotate: -90deg;
}
}
.canon-TooltipArrowFill {
fill: var(--canon-bg-surface-1);
}
.canon-TooltipArrowOuterStroke {
@media (prefers-color-scheme: light) {
fill: var(--canon-border);
}
}
.canon-TooltipArrowInnerStroke {
@media (prefers-color-scheme: dark) {
/* fill: var(--canon-border); */
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Tooltip as TooltipPrimitive } from '@base-ui-components/react/tooltip';
import clsx from 'clsx';
const TooltipTrigger = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TooltipPrimitive.Trigger
ref={ref}
className={clsx('canon-TooltipTrigger', className)}
{...props}
/>
));
TooltipTrigger.displayName = TooltipPrimitive.Trigger.displayName;
const TooltipPositioner = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Positioner>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Positioner>
>(({ className, ...props }, ref) => (
<TooltipPrimitive.Positioner
ref={ref}
className={clsx('canon-TooltipPositioner', className)}
{...props}
/>
));
TooltipPositioner.displayName = TooltipPrimitive.Positioner.displayName;
const TooltipPopup = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Popup>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Popup>
>(({ className, ...props }, ref) => (
<TooltipPrimitive.Popup
ref={ref}
className={clsx('canon-TooltipPopup', className)}
{...props}
/>
));
TooltipPopup.displayName = TooltipPrimitive.Popup.displayName;
const TooltipArrow = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Arrow>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Arrow>
>(({ className, ...props }, ref) => (
<TooltipPrimitive.Arrow
ref={ref}
className={clsx('canon-TooltipArrow', className)}
{...props}
>
<svg width="20" height="10" viewBox="0 0 20 10" fill="none">
<path
d="M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z"
className="canon-TooltipArrowFill"
/>
<path
d="M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z"
className="canon-TooltipArrowOuterStroke"
/>
<path
d="M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z"
className="canon-TooltipArrowInnerStroke"
/>
</svg>
</TooltipPrimitive.Arrow>
));
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName;
/** @public */
export const Tooltip = {
Provider: TooltipPrimitive.Provider,
Root: TooltipPrimitive.Root,
Trigger: TooltipTrigger,
Portal: TooltipPrimitive.Portal,
Positioner: TooltipPositioner,
Popup: TooltipPopup,
Arrow: TooltipArrow,
};
@@ -0,0 +1,17 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './Tooltip';
+1
View File
@@ -27,3 +27,4 @@
@import '../components/Input/Input.styles.css';
@import '../components/Field/Field.styles.css';
@import '../components/Link/styles.css';
@import '../components/Tooltip/Tooltip.styles.css';
+1
View File
@@ -38,6 +38,7 @@ export * from './components/Checkbox';
export * from './components/Table';
export * from './components/Input';
export * from './components/Field';
export * from './components/Tooltip';
// Types
export * from './types';