Skip to content

Commit

Permalink
Create storybook page for link components (#1945)
Browse files Browse the repository at this point in the history
# Pull Request

## 🤨 Rationale

There are a number of browser interactions that are expected to work in
certain ways for link components that are difficult to test in an
automated manner. Therefore, we decided that we should have a storybook
page that contains all of nimble's link components next to a native
anchor element to facilitate manual testing of link behaviors.
 
This is follow-on work from #1910, specifically [this
comment](#1910 (comment)).

## 👩‍💻 Implementation

Create storybook page that includes the following elements:
- native anchor
- nimble-anchor
- nimble-anchor-button
- nimble-anchor-tabs
- nimble-breadcrumb
- nimble-anchor-tree-item
- nimble-anchor-menu-item
- nimble-table-column-anchor
- nimble-rich-text-viewer

## 🧪 Testing

Manually verified that the storybook page rendered as expected.

## ✅ Checklist

<!--- Review the list and put an x in the boxes that apply or ~~strike
through~~ around items that don't (along with an explanation). -->

- [ ] I have updated the project documentation to reflect my changes or
determined no changes are needed.

---------

Co-authored-by: Jesse Attas <jattasNI@users.noreply.github.com>
Co-authored-by: Milan Raj <rajsite@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 20, 2024
1 parent 8bb2c33 commit 517e945
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Create anchor patterns story",
"packageName": "@ni/nimble-components",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Controls, Canvas, Meta, Title } from '@storybook/blocks';
import * as anchorPatternStories from './anchor-patterns.stories';

<Meta of={anchorPatternStories} />
<Title of={anchorPatternStories} />

Anchor components in nimble should behave like native anchors in a number of different ways that are difficult to unit test in an automated manner.
Therefore, this story contains a native anchor element along with all of the nimble components that are anchors or contain anchors.
The following behaviors should be true of the nimble components:

Mouse interactions:

- Dragging the nimble component should behave like the native anchor
- Known behavior exceptions:
- Chromium - The drag preview contains the href only, not the label of the anchor. See [chromium issue 329489154](https://issues.chromium.org/issues/329489154).
- Firefox - The drag preview does not contain the label, and in most cases, is blank. See [mozilla bug 1589364](https://bugzilla.mozilla.org/show_bug.cgi?id=1589364).
- Safari - The drag preview contains the href only, not the label of the anchor.
- CTRL + CLICK opens in a new tab (⌘ + CLICK on macOS)
- Right clicking should open the link menu
- Hovering over the component should show the URL preview at the bottom of the browser window
- Copying the link through the right-click link menu should result in the expected string to be copied to the clipboard
- The hover state of the component and mouse pointer should match the area of the component that navigates when clicked (i.e. clicking the white space around the control should not navigate)

Keyboard interactions:

- Focusing via tab should show the URL preview at the bottom of the browser window
- Known behavior exceptions:
- Firefox on macOS - Links are not focusable via tab by default. [See this stackoverflow page for how to allow tab focus of links](https://stackoverflow.com/questions/11704828/how-to-allow-keyboard-focus-of-links-in-firefox).
- Safari - Focusing via tab does not show the URL preview at the bottom of the browser, which matches the behavior of native anchors.
- Pressing ENTER when focused should activate the link
- Pressing the MENU KEY on the keyboard while the link is focused should open the same link menu as a right click

Interactions specific to anchors visualized as text:

- Selecting the link through click and drag and then copying it should result in the expected string being copied to the clipboard

<Canvas of={anchorPatternStories.anchorPatterns} />
<Controls of={anchorPatternStories.anchorPatterns} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import type { Meta, StoryObj } from '@storybook/html';
import { html, ref } from '@microsoft/fast-element';
import { createUserSelectedThemeStory } from '../../../utilities/tests/storybook';
import {
bodyFont,
bodyFontColor,
controlLabelFont,
controlLabelFontColor,
mediumPadding,
standardPadding
} from '../../../theme-provider/design-tokens';
import { anchorTag } from '../../../anchor';
import { anchorButtonTag } from '../../../anchor-button';
import { anchorTabsTag } from '../../../anchor-tabs';
import { anchorTabTag } from '../../../anchor-tab';
import { breadcrumbTag } from '../../../breadcrumb';
import { breadcrumbItemTag } from '../../../breadcrumb-item';
import { RichTextViewer, richTextViewerTag } from '../../../rich-text/viewer';
import { anchorTreeItemTag } from '../../../anchor-tree-item';
import { treeViewTag } from '../../../tree-view';
import { anchorMenuItemTag } from '../../../anchor-menu-item';
import { menuTag } from '../../../menu';
import { Table, tableTag } from '../../../table';
import { tableColumnAnchorTag } from '../../../table-column/anchor';

interface AnchorPatternsArgs {
label: string;
disabled: boolean;
tableRef: Table;
setTableData: (args: AnchorPatternsArgs) => void;
richTextViewerRef: RichTextViewer;
setRichTextViewerData: (args: AnchorPatternsArgs) => void;
}

const metadata: Meta<AnchorPatternsArgs> = {
title: 'Tests/Anchor Patterns',
parameters: {
actions: {}
},
// prettier-ignore
render: createUserSelectedThemeStory(html`
<style class='code-hide'>
.control-container {
margin: var(${standardPadding.cssCustomProperty});
}
.label {
font: var(${controlLabelFont.cssCustomProperty});
color: var(${controlLabelFontColor.cssCustomProperty});
margin-bottom: var(${mediumPadding.cssCustomProperty});
}
.text {
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
margin-top: var(${mediumPadding.cssCustomProperty});
}
</style>
<div class="control-container">
<div class="label">Native anchor</div>
<a href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=native-anchor-1')}">${x => x.label}</a>
<div class="text">Text that contains a <a href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=native-anchor-2')}">native anchor element</a>.</div>
</div>
<div class="control-container">
<div class="label">${anchorTag}</div>
<${anchorTag} href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=nimble-anchor-1')}">${x => x.label}</${anchorTag}>
<div class="text">Text that contains a <${anchorTag} href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=nimble-anchor-2')}">nimble anchor element</${anchorTag}>.</div>
</div>
<div class="control-container">
<div class="label">${anchorButtonTag}</div>
<${anchorButtonTag} href="https://nimble.ni.dev?type=nimble-anchor-button" ?disabled="${x => x.disabled}">${x => x.label}</${anchorButtonTag}>
</div>
<div class="control-container">
<div class="label">${anchorTabsTag}</div>
<${anchorTabsTag}>
<${anchorTabTag} href="https://nimble.ni.dev?type=nimble-anchor-tab-1" ?disabled="${x => x.disabled}">${x => x.label} - 1</${anchorTabTag}>
<${anchorTabTag} href="https://nimble.ni.dev?type=nimble-anchor-tab-2" ?disabled="${x => x.disabled}">${x => x.label} - 2</${anchorTabTag}>
</${anchorTabsTag}>
</div>
<div class="control-container">
<div class="label">${breadcrumbTag}</div>
<${breadcrumbTag}>
<${breadcrumbItemTag} href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=nimble-breadcrumb-item')}">${x => x.label}</${breadcrumbItemTag}>
<${breadcrumbItemTag}>Current page (no link)</${breadcrumbItemTag}>
</${breadcrumbTag}>
</div>
<div class="control-container">
<div class="label">${anchorTreeItemTag}</div>
<${treeViewTag}>
<${anchorTreeItemTag} href="https://nimble.ni.dev?type=nimble-anchor-tree-item" ?disabled="${x => x.disabled}">${x => x.label}</${anchorTreeItemTag}>
</${treeViewTag}>
</div>
<div class="control-container">
<div class="label">${anchorMenuItemTag}</div>
<${menuTag}>
<${anchorMenuItemTag} href="https://nimble.ni.dev?type=nimble-anchor-menu-item" ?disabled="${x => x.disabled}">${x => x.label}</${anchorMenuItemTag}>
</${menuTag}>
</div>
<div class="control-container">
<div class="label">${tableColumnAnchorTag}</div>
<${tableTag} ${ref('tableRef')} data-unused="${x => x.setTableData(x)}" style="height: 100px;">
<${tableColumnAnchorTag} label-field-name="label" href-field-name="href">Anchor</${tableColumnAnchorTag}>
</${tableTag}>
</div>
<div class="control-container">
<div class="label">${richTextViewerTag}</div>
<${richTextViewerTag} ${ref('richTextViewerRef')}
data-unused="${x => x.setRichTextViewerData(x)}"
>
${x => x.label}
</${richTextViewerTag}>
</div>
`),
argTypes: {
tableRef: {
table: { disable: true }
},
setTableData: {
table: { disable: true }
},
richTextViewerRef: {
table: { disable: true }
},
setRichTextViewerData: {
table: { disable: true }
}
},
args: {
label: 'link',
disabled: false,
setTableData: x => {
void (async () => {
// Safari workaround: the nimble-table element instance is made at this point
// but doesn't seem to be upgraded to a custom element yet
await customElements.whenDefined('nimble-table');
const data = [
{
label: x.label,
href: x.disabled
? undefined
: 'https://nimble.ni.dev?type=nimble-table-column-anchor'
}
];
void x.tableRef.setData(data);
})();
},
setRichTextViewerData: x => {
void (async () => {
// Safari workaround: the nimble-rich-text-viewer element instance is made at this point
// but doesn't seem to be upgraded to a custom element yet
await customElements.whenDefined('nimble-rich-text-viewer');
const data = `Absolute link: <${x.disabled ? '' : 'https://nimble.ni.dev?type=nimble-rich-text-viewer'}>`;
x.richTextViewerRef.markdown = data;
})();
}
}
};

export default metadata;

export const anchorPatterns: StoryObj<AnchorPatternsArgs> = {};
1 change: 1 addition & 0 deletions specs/templates/custom-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
- *Components which delegate focus require all global ARIA attributes to be enumerated*
- *Components should either follow an existing [ARIA Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/) or provide thorough research indicating why a new pattern is appropriate. Research should include sources like [Open UI Community Group](https://github.com/openui/open-ui) and other popular design systems.*
- *Behavior with browser configurations like "Prefers reduced motion"*
- *Support for standard link behaviors if the component is an anchor or contains an anchor. These behaviors are enumerated in the [anchor-patterns story](/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx). The story should be updated to include the new component.*

### Mobile

Expand Down
1 change: 1 addition & 0 deletions specs/templates/fast-based-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
- *Documentation: Any requirements besides standard Storybook docs and updating the Example Client App demo?*
- *Tooling: Any new tools, updates to tools, code generation, etc?*
- *Accessibility: keyboard navigation/focus, form input, use with assistive technology, etc.*
- *Support for standard link behaviors if the component is an anchor or contains an anchor. These behaviors are enumerated in the [anchor-patterns story](/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx). The story should be updated to include the new component.*
- *Mobile: small screens, touch interactions, mobile-specific integrations*
- *Globalization: special RTL handling, swapping of icons/visuals, localization, etc.*
- *Performance: does the FAST component meet Nimble's performance requirements?*
Expand Down

0 comments on commit 517e945

Please sign in to comment.