Skip to content

Commit

Permalink
Initial creation of component
Browse files Browse the repository at this point in the history
  • Loading branch information
brookewp committed Feb 25, 2023
1 parent a533d65 commit 6d2dd09
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/components/src/ui/ariakit-tooltip/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ToolTip

Display information related to an anchor element when the element receives keyboard focus or the mouse hovers over it. This component is based on the WAI-ARIA Tooltip Pattern.
44 changes: 44 additions & 0 deletions packages/components/src/ui/ariakit-tooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* External dependencies
*/
import { Tooltip, TooltipAnchor, useTooltipState } from 'ariakit/tooltip';

/**
* Internal dependencies
*/
import type { ToolTipProps } from './types';
import * as styles from './styles';
import { contextConnect } from '../context';
import { useCx } from '../../utils/hooks/use-cx';

function UnforwardedToolTip(
props: ToolTipProps,
forwardedRef: React.ForwardedRef< any >
) {
const { children, placement, text, timeout } = props;

const tooltipState = useTooltipState( {
placement,
timeout,
} );

const cx = useCx();
const ToolTipClassName = cx( styles.ToolTip );

return (
<>
<TooltipAnchor state={ tooltipState } ref={ forwardedRef }>
{ children }
</TooltipAnchor>
{ text && (
<Tooltip className={ ToolTipClassName } state={ tooltipState }>
{ text }
</Tooltip>
) }
</>
);
}

export const ToolTip = contextConnect( UnforwardedToolTip, 'ToolTip' );

export default ToolTip;
50 changes: 50 additions & 0 deletions packages/components/src/ui/ariakit-tooltip/stories/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* External dependencies
*/
import type { ComponentMeta, ComponentStory } from '@storybook/react';

/**
* Internal dependencies
*/
import ToolTip from '..';
import Button from '../../../button';

const meta: ComponentMeta< typeof ToolTip > = {
title: 'Components/AriaToolTip',
component: ToolTip,
argTypes: {
children: { control: { type: null } },
placement: {
control: {
type: 'select',
options: [
'BasePlacement',
'top-start',
'bottom-start',
'left-start',
'right-start',
'top-end',
'bottom-end',
'left-end',
'right-end',
],
},
},
},
parameters: {
controls: { expanded: true },
docs: { source: { state: 'open' } },
},
};
export default meta;

const Template: ComponentStory< typeof ToolTip > = ( props ) => (
<ToolTip { ...props } />
);

export const Default: ComponentStory< typeof ToolTip > = Template.bind( {} );
Default.args = {
children: <Button variant="primary">It&apos;s me, hi.</Button>,
placement: 'bottom-start',
text: 'Hi!',
};
21 changes: 21 additions & 0 deletions packages/components/src/ui/ariakit-tooltip/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* External dependencies
*/
import { css } from '@emotion/react';

/**
* Internal dependencies
*/
import { CONFIG, COLORS } from '../../utils';
import { rgba } from '../../utils/colors';
import { space } from '../../ui/utils/space';

export const ToolTip = css`
background: ${ rgba( '#1e1e1e', 1 ) };
color: ${ COLORS.ui.textDark };
text-align: center;
line-height: ${ CONFIG.fontLineHeightBase };
font-size: ${ CONFIG.fontSizeSmall };
padding: ${ space( 1 ) } ${ space( 2 ) };
z-index: 1000002;
`;
122 changes: 122 additions & 0 deletions packages/components/src/ui/ariakit-tooltip/test/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* External dependencies
*/
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

/**
* Internal dependencies
*/
import Button from '../../../button';
import { ToolTip } from '..';
import { TOOLTIP_DELAY } from '../../../tooltip/';

const props = {
children: <Button>This is a button</Button>,
text: 'tooltip text goes here',
timeout: TOOLTIP_DELAY,
};

describe( 'ToolTip', () => {
it( 'should render the tooltip as hidden if there is no focus', () => {
render( <ToolTip { ...props } /> );

expect(
screen.queryByRole( 'tooltip', { hidden: true } )
).not.toBeVisible();
} );

it( 'should render the tooltip as visible on tab', async () => {
const user = userEvent.setup();

render( <ToolTip { ...props } /> );

await user.tab();
await user.tab();

expect(
screen.getByRole( 'button', { name: /This is a button/i } )
).toHaveFocus();

await waitFor( () =>
expect(
screen.getByRole( 'tooltip', {
name: /tooltip text goes here/i,
} )
).toBeVisible()
);

await user.tab();

expect(
screen.getByRole( 'tooltip', { hidden: true } )
).not.toBeVisible();
} );

it( 'should render the tooltip as visible on hover', async () => {
const user = userEvent.setup();
render( <ToolTip { ...props } /> );

const button = screen.getByRole( 'button', {
name: /This is a button/i,
} );

await user.hover( button );

await waitFor( () =>
expect(
screen.getByRole( 'tooltip', {
name: /tooltip text goes here/i,
} )
).toBeVisible()
);

await user.unhover( button );

expect(
screen.getByRole( 'tooltip', { hidden: true } )
).not.toBeVisible();
} );

it( 'should not show tooltip on focus as result of mouse click', async () => {
const user = userEvent.setup();

render( <ToolTip { ...props } /> );

await user.click(
screen.getByRole( 'button', {
name: /This is a button/i,
} )
);

expect(
screen.queryByRole( 'tooltip', { name: /tooltip text goes here/i } )
).not.toBeInTheDocument();
} );

it( 'should render the tooltip when an element is disabled', async () => {
const user = userEvent.setup();
render(
<ToolTip text="tooltip text goes here">
<Button disabled>This is a button</Button>
</ToolTip>
);

const button = screen.getByRole( 'button', {
name: /This is a button/i,
} );

expect( button ).toBeDisabled();
expect( button ).toBeVisible();

await user.hover( button );

await waitFor( () =>
expect(
screen.getByRole( 'tooltip', {
name: /tooltip text goes here/i,
} )
).toBeVisible()
);
} );
} );
18 changes: 18 additions & 0 deletions packages/components/src/ui/ariakit-tooltip/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import type { TooltipStateProps } from 'ariakit/tooltip';

export type ToolTipProps = Pick<
TooltipStateProps,
'placement' | 'timeout'
> & {
/**
* The anchor for the tooltip. Accepts only one child element.
*/
children: React.ReactNode;
/**
* The text shown in the tooltip.
*/
text: string;
};

0 comments on commit 6d2dd09

Please sign in to comment.