Skip to content

Commit

Permalink
feat(rich-tooltip): add new component (#698)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielduete authored Feb 17, 2025
1 parent 9fa4ba8 commit 192b380
Show file tree
Hide file tree
Showing 11 changed files with 538 additions and 20 deletions.
55 changes: 55 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,21 @@ export namespace Components {
"count": number;
"page": number;
}
interface AtomRichTooltip {
"action": 'hover' | 'click';
"actionText"?: string;
"element": string;
"label"?: string;
"open": boolean;
"placement": | 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'right'
| 'left';
}
interface AtomSelect {
"disabled"?: boolean;
"errorText"?: string;
Expand Down Expand Up @@ -379,6 +394,10 @@ export interface AtomPaginationCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLAtomPaginationElement;
}
export interface AtomRichTooltipCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLAtomRichTooltipElement;
}
export interface AtomSelectCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLAtomSelectElement;
Expand Down Expand Up @@ -628,6 +647,23 @@ declare global {
prototype: HTMLAtomPaginationElement;
new (): HTMLAtomPaginationElement;
};
interface HTMLAtomRichTooltipElementEventMap {
"buttonAction": void;
}
interface HTMLAtomRichTooltipElement extends Components.AtomRichTooltip, HTMLStencilElement {
addEventListener<K extends keyof HTMLAtomRichTooltipElementEventMap>(type: K, listener: (this: HTMLAtomRichTooltipElement, ev: AtomRichTooltipCustomEvent<HTMLAtomRichTooltipElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLAtomRichTooltipElementEventMap>(type: K, listener: (this: HTMLAtomRichTooltipElement, ev: AtomRichTooltipCustomEvent<HTMLAtomRichTooltipElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLAtomRichTooltipElement: {
prototype: HTMLAtomRichTooltipElement;
new (): HTMLAtomRichTooltipElement;
};
interface HTMLAtomSelectElementEventMap {
"atomBlur": void;
"atomCancel": void;
Expand Down Expand Up @@ -744,6 +780,7 @@ declare global {
"atom-meter": HTMLAtomMeterElement;
"atom-modal": HTMLAtomModalElement;
"atom-pagination": HTMLAtomPaginationElement;
"atom-rich-tooltip": HTMLAtomRichTooltipElement;
"atom-select": HTMLAtomSelectElement;
"atom-spinner": HTMLAtomSpinnerElement;
"atom-steps-modal": HTMLAtomStepsModalElement;
Expand Down Expand Up @@ -986,6 +1023,22 @@ declare namespace LocalJSX {
"onAtomChangePage"?: (event: AtomPaginationCustomEvent<number>) => void;
"page"?: number;
}
interface AtomRichTooltip {
"action"?: 'hover' | 'click';
"actionText"?: string;
"element"?: string;
"label"?: string;
"onButtonAction"?: (event: AtomRichTooltipCustomEvent<void>) => void;
"open"?: boolean;
"placement"?: | 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'right'
| 'left';
}
interface AtomSelect {
"disabled"?: boolean;
"errorText"?: string;
Expand Down Expand Up @@ -1143,6 +1196,7 @@ declare namespace LocalJSX {
"atom-meter": AtomMeter;
"atom-modal": AtomModal;
"atom-pagination": AtomPagination;
"atom-rich-tooltip": AtomRichTooltip;
"atom-select": AtomSelect;
"atom-spinner": AtomSpinner;
"atom-steps-modal": AtomStepsModal;
Expand Down Expand Up @@ -1175,6 +1229,7 @@ declare module "@stencil/core" {
"atom-meter": LocalJSX.AtomMeter & JSXBase.HTMLAttributes<HTMLAtomMeterElement>;
"atom-modal": LocalJSX.AtomModal & JSXBase.HTMLAttributes<HTMLAtomModalElement>;
"atom-pagination": LocalJSX.AtomPagination & JSXBase.HTMLAttributes<HTMLAtomPaginationElement>;
"atom-rich-tooltip": LocalJSX.AtomRichTooltip & JSXBase.HTMLAttributes<HTMLAtomRichTooltipElement>;
"atom-select": LocalJSX.AtomSelect & JSXBase.HTMLAttributes<HTMLAtomSelectElement>;
"atom-spinner": LocalJSX.AtomSpinner & JSXBase.HTMLAttributes<HTMLAtomSpinnerElement>;
"atom-steps-modal": LocalJSX.AtomStepsModal & JSXBase.HTMLAttributes<HTMLAtomStepsModalElement>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Meta, StoryObj } from '@storybook/web-components'

import { html } from 'lit'

import { BadgeStoryArgs } from './badge.args'
Expand Down
42 changes: 42 additions & 0 deletions packages/core/src/components/rich-tooltip/rich-tooltip.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
:host {
::part(tooltip) {
background: var(--color-neutral-light-5);
border-radius: var(--border-radius);
box-shadow: var(--elevation-4);
color: var(--color-neutral-regular);
filter: drop-shadow(var(--elevation-3));
max-width: 100%;
padding: var(--spacing-small);
padding-bottom: var(--spacing-medium);
padding-top: var(--spacing-xlarge);
transition: all 0.15s ease-in-out;
transition-property: opacity, transform, visibility;
width: 252px;
}

.title {
font: var(--text-body-small);
font-weight: var(--font-weight-bold);
letter-spacing: var(--text-body-small-letter);
margin: 0;
margin-bottom: var(--spacing-xsmall);
}

.text {
font: var(--text-body-small);
font-weight: var(--font-weight-regular);
letter-spacing: var(--text-body-small-letter);
margin: 0;
}

.action {
background: none;
border: none;
color: var(--color-brand-secondary-regular);
cursor: pointer;
font: var(--button-medium);
letter-spacing: var(--button-medium-letter);
margin: 0;
margin-top: var(--spacing-xsmall);
}
}
46 changes: 46 additions & 0 deletions packages/core/src/components/rich-tooltip/rich-tooltip.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { newSpecPage } from '@stencil/core/testing'

import { AtomRichTooltip } from './rich-tooltip'

describe('AtomRichTooltip', () => {
const createTooltip = (
id: string,
action: AtomRichTooltip['action'] = 'hover',
open = false
) => `
<button id="${id}" aria-describedby="${id}--tooltip">Hover</button>
<atom-rich-tooltip id="${id}--tooltip" element="${id}" open="${open}" action="${action}" label="Test Title" actiontext="Click me">Tooltip content</atom-rich-tooltip>
`

it('should render correctly', async () => {
const page = await newSpecPage({
components: [AtomRichTooltip],
html: createTooltip('test-element'),
})

expect(page.root).toEqualHtml(`
<atom-rich-tooltip id="test-element--tooltip" element="test-element" open="false" action="hover" label="Test Title" actiontext="Click me" role="tooltip">
<mock:shadow-root>
<atom-tooltip element="test-element" placement="top" action="hover" class="rich-tooltip">
<div class="rich-tooltip__content">
<h1 class="title">Test Title</h1>
<p class="text">
<slot></slot>
</p>
</div>
</atom-tooltip>
</mock:shadow-root>
Tooltip content
</atom-rich-tooltip>
`)
})

it('should not display action text when action is hover', async () => {
const page = await newSpecPage({
components: [AtomRichTooltip],
html: createTooltip('test-element', 'hover'),
})

expect(page.root?.shadowRoot?.querySelector('.action')).toBeNull()
})
})
61 changes: 61 additions & 0 deletions packages/core/src/components/rich-tooltip/rich-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
Component,
Element,
Event,
EventEmitter,
h,
Host,
Prop,
} from '@stencil/core'

@Component({
tag: 'atom-rich-tooltip',
styleUrl: 'rich-tooltip.scss',
shadow: true,
})
export class AtomRichTooltip {
@Element() el: HTMLElement

@Prop() element: string
@Prop() placement:
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'right'
| 'left' = 'top'
@Prop() action: 'hover' | 'click' = 'hover'
@Prop() label?: string
@Prop() actionText?: string
@Prop({ mutable: true }) open = false

@Event() buttonAction?: EventEmitter<void>

render() {
return (
<Host role='tooltip'>
<atom-tooltip
element={this.element}
placement={this.placement}
action={this.action}
open={this.open}
class='rich-tooltip'
>
<div class='rich-tooltip__content'>
{this.label && <h1 class='title'>{this.label}</h1>}
<p class='text'>
<slot />
</p>
{this.action === 'click' && this.actionText && (
<button class='action' onClick={() => this.buttonAction.emit()}>
{this.actionText}
</button>
)}
</div>
</atom-tooltip>
</Host>
)
}
}
114 changes: 114 additions & 0 deletions packages/core/src/components/rich-tooltip/stories/rich-tooltip.args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Category } from '@atomium/storybook-utils/enums/table'
import { withActions } from '@storybook/addon-actions/decorator'

export const RichTooltipStoryArgs = {
parameters: {
actions: {
handles: ['atomOpen', 'atomClose'],
},
decorators: [withActions],
docs: {
description: {
component:
'Rich Tooltip is a component that provides more information to the user and allows them to perform quick actions within the context of the problem.',
},
},
layout: 'centered',
},
argTypes: {
placement: {
control: 'select',
defaultValue: { summary: 'top' },
options: [
'top',
'top-start',
'top-end',
'bottom',
'bottom-start',
'bottom-end',
'right',
'left',
],
description: 'Determines placement for tooltip',
table: {
category: Category.PROPERTIES,
},
},
action: {
control: 'select',
defaultValue: { summary: 'hover' },
options: ['hover', 'click'],
description:
'Determines the trigger action for the tooltip: `hover` or `click`.',
table: {
category: Category.PROPERTIES,
},
},
element: {
control: 'text',
description:
'Specifies the element responsible for opening/closing the tooltip.',
table: {
category: Category.PROPERTIES,
},
},
open: {
control: 'boolean',
description: 'Controls whether the tooltip is open or closed.',
table: {
category: Category.PROPERTIES,
},
},
label: {
control: 'text',
description: 'Determines a title for tooltip.',
table: {
category: Category.PROPERTIES,
},
},
text: {
control: 'text',
description: 'Determines a text for tooltip.',
table: {
category: Category.PROPERTIES,
},
},
actionText: {
control: 'text',
description: 'Determines a text for action button.',
table: {
category: Category.PROPERTIES,
},
},
buttonAction: {
description:
'Event emitted when the action button is clicked. Action needs to be click to show the button.',
table: {
category: Category.EVENTS,
},
},
atomOpen: {
description:
'Event emitted when hover element, but for mobile when click in element.',
table: {
category: Category.EVENTS,
},
},
atomClose: {
description: 'Event emitted when the tooltip is closed.',
table: {
category: Category.EVENTS,
},
},
},
}

export const RichTooltipComponentArgs = {
element: 'atomium-element',
placement: 'top',
text: 'Supporting line text lorem ipsum dolor sit amet, consectetur',
action: 'hover',
label: 'Title',
actionText: 'Action Button',
open: false,
}
Loading

0 comments on commit 192b380

Please sign in to comment.