Skip to content

Commit

Permalink
Anchor tabs component (#960)
Browse files Browse the repository at this point in the history
## 🀨 Rationale

Addresses #479

## πŸ‘©β€πŸ’» Implementation

- Created `nimble-anchor-tabs` component with implementation starting
from the FAST foundation tabs component.
- Created `nimble-anchor-tab` component.
- Updated and moved existing styling for `nimble-tab` into shared
`patterns/tab` location. Shared styling used for `nimble-anchor-tab`,
too.
- Wrote unit tests for both new components. Because the
`nimble-anchor-tabs` component is heavily modified from the original
FAST source, I created comprehensive tests of public API and
interactions.
- Created Storybook stories
- Updated README table

Angular and Blazor support will be in follow-up PRs

## πŸ§ͺ Testing

Wrote unit tests and tested in Storybook.

## βœ… Checklist

- [x] I have updated the project documentation to reflect my changes or
determined no changes are needed.
  • Loading branch information
m-akinc authored Jan 20, 2023
1 parent 76aef4f commit 4c3c715
Show file tree
Hide file tree
Showing 17 changed files with 963 additions and 52 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ NOTE: To update the component status:
| Accordion | | [Issue](https://github.com/ni/nimble/issues/533) | :o: | :o: | :o: |
| Anchor | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/bfadf499-caf5-4ca0-9814-e777fbae0d46/) | [Issue](https://github.com/ni/nimble/issues/324) | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/docs/anchor--text-anchor) | :white_check_mark: | :white_check_mark: |
| Anchor Button | | [Issue](https://github.com/ni/nimble/issues/324) | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/docs/anchor-button--outline-anchor-button) | :white_check_mark: | :white_check_mark: |
| Anchor Tabs | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/b2aa2c0c-03b7-4571-8e0d-de88baf0814b) | [Issue](https://github.com/ni/nimble/issues/479) | [:white_check_mark: - SB](https://nimble.ni.dev/storybook/?path=/docs/anchor-tabs--tabs) | :o: | :o: |
| Banners | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/29c405f7-08ea-48b6-973f-546970b9dbab) | [Issue](https://github.com/ni/nimble/issues/305) | :o: | :o: | :o: |
| Breadcrumb | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/7b53bb3e-439b-4f13-9d5f-55adc7da8a2e) | | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/docs/breadcrumb--standard-breadcrumb) | :white_check_mark: | :white_check_mark: |
| Card | | [Issue](https://github.com/ni/nimble/issues/296) | :o: | :o: | :o: |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Anchor tabs component",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
2 changes: 2 additions & 0 deletions packages/nimble-components/src/all-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import './anchor';
import './anchor-button';
import './anchor-tab';
import './anchor-tabs';
import './anchored-region';
import './breadcrumb';
import './breadcrumb-item';
Expand Down
39 changes: 39 additions & 0 deletions packages/nimble-components/src/anchor-tab/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { attr } from '@microsoft/fast-element';
import {
DesignSystem,
FoundationElementDefinition,
StartEndOptions
} from '@microsoft/fast-foundation';
import { AnchorBase } from '../anchor-base';
import { styles } from './styles';
import { template } from './template';

declare global {
interface HTMLElementTagNameMap {
'nimble-anchor-tab': AnchorTab;
}
}

export type TabOptions = FoundationElementDefinition & StartEndOptions;

/**
* A nimble-styled link tab
*/
export class AnchorTab extends AnchorBase {
/**
* When true, the control will be immutable by user interaction. See {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled | disabled HTML attribute} for more information.
* @public
* @remarks
* HTML Attribute: disabled
*/
@attr({ mode: 'boolean' })
public disabled = false;
}

const nimbleAnchorTab = AnchorTab.compose({
baseName: 'anchor-tab',
template,
styles
});

DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleAnchorTab());
13 changes: 13 additions & 0 deletions packages/nimble-components/src/anchor-tab/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { css } from '@microsoft/fast-element';
import { styles as tabStyles } from '../patterns/tab/styles';

export const styles = css`
${tabStyles}
a {
text-decoration: none;
color: inherit;
cursor: inherit;
outline: none;
}
`;
24 changes: 24 additions & 0 deletions packages/nimble-components/src/anchor-tab/template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { html, ViewTemplate } from '@microsoft/fast-element';
import type { FoundationElementTemplate } from '@microsoft/fast-foundation';
import type { AnchorTab, TabOptions } from '.';

export const template: FoundationElementTemplate<
ViewTemplate<AnchorTab>,
TabOptions
> = () => html<AnchorTab>`
<template slot="anchortab" role="tab" aria-disabled="${x => x.disabled}">
<a
download="${x => x.download}"
href=${x => (x.disabled ? null : x.href)}
hreflang="${x => x.hreflang}"
ping="${x => x.ping}"
referrerpolicy="${x => x.referrerpolicy}"
rel="${x => x.rel}"
target="${x => x.target}"
type="${x => x.type}"
tabindex="-1"
>
<slot></slot>
</a>
</template>
`;
63 changes: 63 additions & 0 deletions packages/nimble-components/src/anchor-tab/tests/anchor-tab.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { DOM, html } from '@microsoft/fast-element';
import { AnchorTab } from '..';
import { Fixture, fixture } from '../../utilities/tests/fixture';
import { getSpecTypeByNamedList } from '../../utilities/tests/parameterized';

async function setup(): Promise<Fixture<AnchorTab>> {
return fixture<AnchorTab>(html`<nimble-anchor-tab></nimble-anchor-tab>`);
}

describe('AnchorTab', () => {
let element: AnchorTab;
let connect: () => Promise<void>;
let disconnect: () => Promise<void>;

beforeEach(async () => {
({ element, connect, disconnect } = await setup());
});

afterEach(async () => {
await disconnect();
});

it('can construct an element instance', () => {
expect(document.createElement('nimble-anchor-tab')).toBeInstanceOf(
AnchorTab
);
});

const attributeNames: { name: string }[] = [
{ name: 'download' },
{ name: 'href' },
{ name: 'hreflang' },
{ name: 'ping' },
{ name: 'referrerpolicy' },
{ name: 'rel' },
{ name: 'target' },
{ name: 'type' }
];
describe('should reflect value to the internal anchor element', () => {
const focused: string[] = [];
const disabled: string[] = [];
for (const attribute of attributeNames) {
const specType = getSpecTypeByNamedList(
attribute,
focused,
disabled
);
// eslint-disable-next-line @typescript-eslint/no-loop-func
specType(`for attribute ${attribute.name}`, async () => {
await connect();

element.setAttribute(attribute.name, 'foo');
await DOM.nextUpdate();

expect(
element
.shadowRoot!.querySelector('a')!
.getAttribute(attribute.name)
).toBe('foo');
});
}
});
});
Loading

0 comments on commit 4c3c715

Please sign in to comment.