Skip to content

Commit

Permalink
Nimble tooltip implementation (#608)
Browse files Browse the repository at this point in the history
# Pull Request

## 🀨 Rationale
Initial implementation for nimble-tooltip, intended to get the default tooltip working and implemented. See #309 

## πŸ‘©β€πŸ’» Implementation
Includes only the default tooltip state, with the other states to be implemented in the future. See tooltip spec, [API section](https://github.com/ni/nimble/blob/c1031e33f9f62748f48ec0ce69ea895dde39327a/packages/nimble-components/src/tooltip/tooltip.spec.md). Styling and implementation will need to be updated in the future in order to make the error and information states functional. Other questions listed in the [open issues section](https://github.com/ni/nimble/blob/c1031e33f9f62748f48ec0ce69ea895dde39327a/packages/nimble-components/src/tooltip/tooltip.spec.md) will also be addressed in future updates.

## πŸ§ͺ Testing
Added unit tests and storybook matrix pages.

## βœ… Checklist

- [x] I have updated the project documentation to reflect my changes or determined no changes are needed.
  • Loading branch information
aidendk authored Jun 24, 2022
1 parent 1ff011a commit 70e8d55
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Implementation, styling, and tests for the 'default' state of the nimble-tooltip.",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
1 change: 1 addition & 0 deletions packages/nimble-components/src/all-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ import './text-field';
import './theme-provider';
import './toggle-button';
import './toolbar';
import './tooltip';
import './tree-item';
import './tree-view';
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ export const comments: { readonly [key in TokenName]: string | null } = {
tooltipCaptionFontWeight: null,
tooltipCaptionFontLineHeight: null,
tooltipCaptionFallbackFontFamily: null,
tooltipBackgroundColor: 'Default background color for tooltips',
errorTextFont: 'Font shorthand for the "Error_LightUi" base token',
errorTextFontColor: 'Font color for "Error_LightUi" base token',
errorTextDisabledFontColor:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export const tokenNames: { readonly [key in TokenName]: string } = {
tooltipCaptionFontWeight: 'tooltip-caption-font-weight',
tooltipCaptionFontLineHeight: 'tooltip-caption-font-line-height',
tooltipCaptionFallbackFontFamily: 'tooltip-caption-fallback-font-family',
tooltipBackgroundColor: 'tooltip-background-color',
errorTextFont: 'error-text-font',
errorTextFontColor: 'error-text-font-color',
errorTextDisabledFontColor: 'error-text-disabled-font-color',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ export const popupBorderColor = DesignToken.create<string>(
styleNameFromTokenName(tokenNames.popupBorderColor)
).withDefault((element: HTMLElement) => hexToRgbaCssColor(getColorForTheme(element, Black91, Black15, White), 0.3));

export const tooltipBackgroundColor = DesignToken.create<string>(
styleNameFromTokenName(tokenNames.tooltipBackgroundColor)
).withDefault((element: HTMLElement) => getColorForTheme(element, Black15, Black85, ForestGreen));

// Component Sizing Tokens
export const controlHeight = DesignToken.create<string>(
styleNameFromTokenName(tokenNames.controlHeight)
Expand Down
26 changes: 26 additions & 0 deletions packages/nimble-components/src/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
DesignSystem,
Tooltip as FoundationTooltip,
tooltipTemplate as template
} from '@microsoft/fast-foundation';
import { styles } from './styles';

declare global {
interface HTMLElementTagNameMap {
'nimble-tooltip': Tooltip;
}
}

/**
* A nimble-styled tooltip control.
*/
export class Tooltip extends FoundationTooltip {}

const nimbleTooltip = Tooltip.compose({
baseName: 'tooltip',
baseClass: FoundationTooltip,
template,
styles
});

DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleTooltip());
36 changes: 36 additions & 0 deletions packages/nimble-components/src/tooltip/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { css } from '@microsoft/fast-element';
import { display } from '@microsoft/fast-foundation';
import {
borderRgbPartialColor,
tooltipCaptionFont,
tooltipCaptionFontColor,
borderWidth,
tooltipBackgroundColor,
smallPadding,
standardPadding,
popupBoxShadowColor
} from '../theme-provider/design-tokens';

export const styles = css`
${display('inline-flex')}
:host {
font: ${tooltipCaptionFont};
color: ${tooltipCaptionFontColor};
text-align: left;
}
.tooltip {
box-sizing: border-box;
flex-shrink: 0;
max-width: 440px;
border: ${borderWidth} solid rgba(${borderRgbPartialColor}, 0.3);
box-shadow: 0px 3px 4px ${popupBoxShadowColor};
background-color: ${tooltipBackgroundColor};
padding-bottom: 6px;
padding-left: calc(${standardPadding} / 2);
padding-right: calc(${standardPadding} / 2);
padding-top: ${smallPadding};
display: inline-flex;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { Meta, Story } from '@storybook/html';
import { withXD } from 'storybook-addon-xd-designs';
import { html, ViewTemplate } from '@microsoft/fast-element';
import {
createMatrix,
sharedMatrixParameters
} from '../../utilities/tests/matrix';
import { createFixedThemeStory } from '../../utilities/tests/storybook';
import { backgroundStates } from '../../utilities/tests/states';
import '../../all-components';
import {
bodyFont,
bodyFontColor,
borderColor
} from '../../theme-provider/design-tokens';

const metadata: Meta = {
title: 'Tests/Tooltip',
decorators: [withXD],
parameters: {
...sharedMatrixParameters(),
design: {
artboardUrl:
'https://xd.adobe.com/view/8ce280ab-1559-4961-945c-182955c7780b-d9b1/screen/044414d7-1714-40f2-9679-2ce2c8202d1c/specs/'
}
}
};

export default metadata;

const valueStates = [
['shortText', 'Hello'],
[
'longText',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.'
]
] as const;
type ValueState = typeof valueStates[number];

const component = ([valueName, valueValue]: ValueState): ViewTemplate => html`
<style>
.container {
display: inline-flex;
padding: 120px;
justify-content: center;
text-align: center;
}
.anchorDiv {
border: 1px solid var(${borderColor.cssCustomProperty});
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
}
.tooltip {
justify-content: center;
}
</style>
<div class="container">
<div class="anchorDiv" id="${() => `${valueName}`}">${valueName}</div>
<nimble-tooltip
anchor="${() => `${valueName}`}"
visible
position="bottom"
auto-update-mode="auto"
>
${() => valueValue}
</nimble-tooltip>
</div>
`;

const [
lightThemeWhiteBackground,
colorThemeDarkGreenBackground,
darkThemeBlackBackground
] = backgroundStates;

export const tooltipLightThemeWhiteBackground: Story = createFixedThemeStory(
createMatrix(component, [valueStates]),
lightThemeWhiteBackground
);

export const tooltipColorThemeDarkGreenBackground: Story = createFixedThemeStory(
createMatrix(component, [valueStates]),
colorThemeDarkGreenBackground
);

export const tooltipDarkThemeBlackBackground: Story = createFixedThemeStory(
createMatrix(component, [valueStates]),
darkThemeBlackBackground
);
17 changes: 17 additions & 0 deletions packages/nimble-components/src/tooltip/tests/tooltip.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
DesignSystem,
Tooltip as FoundationTooltip
} from '@microsoft/fast-foundation';
import { Tooltip } from '..';

describe('Tooltip', () => {
it('should have its tag returned by tagFor(FoundationTooltip)', () => {
expect(DesignSystem.tagFor(FoundationTooltip)).toBe('nimble-tooltip');
});

it('can construct an element instance', () => {
expect(document.createElement('nimble-tooltip')).toBeInstanceOf(
Tooltip
);
});
});
89 changes: 89 additions & 0 deletions packages/nimble-components/src/tooltip/tests/tooltip.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { html } from '@microsoft/fast-element';
import type { Meta, StoryObj } from '@storybook/html';
import { withXD } from 'storybook-addon-xd-designs';
import type { AutoUpdateMode } from '@microsoft/fast-foundation';
import { createUserSelectedThemeStory } from '../../utilities/tests/storybook';
import '../../all-components';
import {
borderColor,
bodyFont,
bodyFontColor
} from '../../theme-provider/design-tokens';

interface TooltipArgs {
visible: boolean;
delay: number;
tooltip: string;
autoUpdateMode: AutoUpdateMode;
}

const metadata: Meta<TooltipArgs> = {
title: 'Tooltip',
decorators: [withXD],
parameters: {
docs: {
description: {
component:
'<b>Experimental - The tooltip is still in development. It will be ready for app use soon.</b>Per [W3C](https://w3c.github.io/aria-practices/#tooltip) – A tooltip is a popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it. It typically appears after a small delay and disappears when Escape is pressed or on mouse out. <br><br> It is recommended to set up aria-describedby, an accessibility feature that uses element IDs to set the description of one element based on another element. To leverage this, the anchor element (button, text, icon, etc.) of the tooltip must have `aria-describedby= "name"` in its attributes, where `name` is the ID of the nimble-tooltip. More information can be found in the [aria-describedby docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby).'
}
},
design: {
artboardUrl:
'https://xd.adobe.com/view/8ce280ab-1559-4961-945c-182955c7780b-d9b1/screen/044414d7-1714-40f2-9679-2ce2c8202d1c/specs/'
},
actions: {
handles: ['dismiss']
}
},
render: createUserSelectedThemeStory(html<TooltipArgs>`
<style>
.container {
width: 100px;
height: 50px;
}
.anchorDiv {
border: 1px solid var(${borderColor.cssCustomProperty});
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
}
</style>
<div class="container">
<div class="anchorDiv" id="anchor" aria-describedby="ariaAnchor">
Text, Button, Icon, etc.
</div>
<nimble-tooltip
anchor="anchor"
?visible="${x => x.visible}"
delay="${x => x.delay}"
auto-update-mode="${x => x.autoUpdateMode}"
id="ariaAnchor"
>
${x => x.tooltip}
</nimble-tooltip>
</div>
`),
args: {
visible: false,
tooltip: 'Tooltip label',
delay: 300,
autoUpdateMode: 'auto'
},
argTypes: {
autoUpdateMode: {
options: { anchor: 'anchor', auto: 'auto' },
control: { type: 'radio' },
description:
'Controls when the tooltip updates its position. The default is `anchor`, which only updates when the anchor is resized. `auto` will update on scroll/resize events.'
},
delay: {
description:
'The delay in milliseconds before a tooltip is shown after a hover event'
}
}
};

export default metadata;

export const tooltip: StoryObj<TooltipArgs> = {};

0 comments on commit 70e8d55

Please sign in to comment.