Skip to content

Commit

Permalink
Init Tooltip component
Browse files Browse the repository at this point in the history
  • Loading branch information
tfirdaus committed Nov 20, 2024
1 parent 6eafdec commit 126b706
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/kubrick/src/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
],
})}
{...mergeProps(buttonProps, hoverProps, focusProps)}
ref={ref}
role={role}
>
{prefix && (
Expand Down
1 change: 1 addition & 0 deletions packages/kubrick/src/IconButton/IconButton.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
align-items: center;
justify-content: center;
width: max-content;
height: max-content;
aspect-ratio: 1/1;
padding: 6px;

Expand Down
1 change: 1 addition & 0 deletions packages/kubrick/src/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
})}
{...mergeProps(buttonProps, hoverProps, focusProps)}
aria-label={label}
ref={ref}
role={role}
>
{children}
Expand Down
13 changes: 13 additions & 0 deletions packages/kubrick/src/Tooltip/Tooltip.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@use '../../scss/variables/colors';

.root {
width: max-content;
max-width: 320px;
max-height: 160px;
padding: 1rem;
overflow-y: scroll;
text-wrap: pretty;
background: colors.$white;
border: 1px solid colors.$gray-100;
box-shadow: 0 1px 1px rgba(0 0 0 / 4%);
}
55 changes: 55 additions & 0 deletions packages/kubrick/src/Tooltip/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Icon, wordpress } from '@wordpress/icons';
import { IconButton } from '../IconButton';
import { Tooltip } from './Tooltip';

const meta: Meta<typeof Tooltip> = {
argTypes: {
content: {
control: 'text',
},
},
args: {
content:
'Do not put essential information in a tooltip. Tooltips have low discoverability and have usability issues on devices without hover interactions.',
},
component: Tooltip,
decorators: [
(Story) => {
return (
<div
style={{
display: 'flex',
height: 180,
maxWidth: 240,
position: 'relative',
width: '100%',
}}
>
<Story />
</div>
);
},
],
parameters: {
controls: {
include: ['content'],
},
},
tags: ['autodocs'],
title: 'Components/Tooltip',
};

type Story = StoryObj<typeof Tooltip>;

export const Default: Story = {
render: (args) => (
<Tooltip {...args}>
<IconButton label="Info">
<Icon icon={wordpress} />
</IconButton>
</Tooltip>
),
};

export default meta;
101 changes: 101 additions & 0 deletions packages/kubrick/src/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { FocusableProvider } from '@react-aria/focus';
import { mergeProps, useObjectRef } from '@react-aria/utils';
import { FocusableElement } from '@react-types/shared';
import { ReactNode, forwardRef, useRef } from 'react';
import {
AriaTooltipProps,
Overlay,
useOverlayPosition,
useTooltip,
useTooltipTrigger,
} from 'react-aria';
import { TooltipTriggerProps, useTooltipTriggerState } from 'react-stately';
import { GlobalProps } from '../types';
import { useProps } from '../useProps';
import styles from './Tooltip.module.scss';

export const DEFAULT_DELAY = 200;
export const DEFAULT_CLOSE_DELAY = 100;
export const DEFAULT_OFFSET = 5;

interface TooltipProps
extends GlobalProps,
AriaTooltipProps,
TooltipTriggerProps {
children: ReactNode;
content: ReactNode;
/**
* The additional offset applied along the main axis between the element and its anchor element.
*
* @default 5
*/
offset?: number;
}

/**
* ```js
* import { Tooltip } from '@kinsta-martech/spectra-core';
* ```
*/
export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
(props, forwardedRef) => {
const {
children,
closeDelay,
content,
delay,
offset = DEFAULT_OFFSET,
role,
} = props;
const tooltipRef = useObjectRef(forwardedRef);
const triggerRef = useRef<FocusableElement>(null);
const { componentProps, rootProps } = useProps('Tooltip', props);
const state = useTooltipTriggerState({
...componentProps,
closeDelay:
typeof closeDelay !== 'number' ? DEFAULT_CLOSE_DELAY : closeDelay,
delay: typeof delay !== 'number' ? DEFAULT_DELAY : delay,
});
const { overlayProps } = useOverlayPosition({
isOpen: state.isOpen,
offset,
overlayRef: tooltipRef,
placement: 'left',
targetRef: triggerRef,
});
const { tooltipProps: triggerTooltipProps, triggerProps } =
useTooltipTrigger(componentProps, state, triggerRef);
const { tooltipProps } = useTooltip(
{
...mergeProps(componentProps, overlayProps),
...triggerTooltipProps,
},
state
);

return (
<>
<FocusableProvider {...triggerProps} ref={triggerRef}>
{children}
</FocusableProvider>
{state.isOpen && (
<Overlay>
<div
{...rootProps({
classNames: styles.root,
})}
{...tooltipProps}
ref={tooltipRef}
role={role}
style={overlayProps.style}
>
{content}
</div>
</Overlay>
)}
</>
);
}
);

Tooltip.displayName = 'Tooltip';
1 change: 1 addition & 0 deletions packages/kubrick/src/Tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Tooltip';
1 change: 1 addition & 0 deletions packages/kubrick/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export * from './Tabs';
export * from './Tabs/Tab';
export * from './TextArea';
export * from './TextField';
export * from './Tooltip';
export * from './useClasses';

0 comments on commit 126b706

Please sign in to comment.