-
Notifications
You must be signed in to change notification settings - Fork 80
feat(navigation, navigation-logo, navigation-user): Add navigation, navigation-logo & navigation-user components. #6873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
8bdfbc3
b5906c1
c2b64e0
ad24c69
d91619b
991691c
bf16349
199f634
95d13b9
ffeb67e
db19d11
b2f3254
a97cb56
cc8224c
80966ae
40793f4
cf105fb
7a0769f
2fecbe5
25573e1
675dd86
109a141
1ec3c32
ef32860
cef8b07
b53d6ca
98fc70c
777ee50
ab36471
357fee2
245728b
d2d0216
4323093
d07aab6
8aed351
1f6e312
98f816f
784d6c8
230e102
a0d18b1
a1c3700
4ca74e1
31515b1
4255077
52be92c
fbfb464
1f98e24
9343a9e
13fe1c3
a731b90
6912e84
ad2eb87
27ddef7
db51915
6708a1c
8b22471
2f76ede
01ce5d0
266da9c
856933b
af705d6
86180ad
e30fa6f
82331d0
e424aea
f453574
042e763
27550ac
b563d75
92830f7
2c7dcd5
bfaf90e
6095842
bd29cf8
7b88d1a
680ab82
c920b5f
66da54a
58943da
58269bc
11f42a2
07d01cb
7e462f8
9b1ad85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { newE2EPage } from "@stencil/core/testing"; | ||
import { html } from "../../../support/formatting"; | ||
import { accessible, hidden, reflects, renders } from "../../tests/commonTests"; | ||
|
||
describe("calcite-nav-logo", () => { | ||
describe("renders", () => { | ||
renders("calcite-nav-logo", { display: "inline-flex" }); | ||
}); | ||
|
||
it("honors hidden attribute", () => hidden("calcite-nav-logo")); | ||
|
||
describe("accessible", () => { | ||
accessible("calcite-nav-logo"); | ||
}); | ||
|
||
it("reflects", () => | ||
reflects("calcite-nav-logo", [ | ||
{ | ||
propertyName: "active", | ||
value: "true" | ||
}, | ||
{ | ||
propertyName: "href", | ||
value: "#logo" | ||
}, | ||
{ | ||
propertyName: "textEnabled", | ||
value: "true" | ||
} | ||
])); | ||
|
||
it("should emit calciteNavLogoSelect event on user click", async () => { | ||
const page = await newE2EPage(); | ||
await page.setContent(html`<calcite-nav-logo text="esri" id="esri" text-enabled></calcite-nav-logo>`); | ||
|
||
const element = await page.find("calcite-nav-logo"); | ||
const eventSpy = await element.spyOnEvent("calciteNavLogoSelect"); | ||
|
||
await element.click(); | ||
await page.waitForChanges(); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("esri"); | ||
expect(eventSpy).toHaveReceivedEventTimes(1); | ||
}); | ||
|
||
it("should emit calciteNavLogoSelect event when user select using Enter & Space key", async () => { | ||
const page = await newE2EPage(); | ||
await page.setContent(html` <calcite-nav-logo text="esri" id="esri" text-enabled></calcite-nav-logo> `); | ||
|
||
const element = await page.find("calcite-nav-logo"); | ||
const eventSpy = await element.spyOnEvent("calciteNavLogoSelect"); | ||
|
||
await page.keyboard.press("Tab"); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("esri"); | ||
expect(eventSpy).not.toHaveReceivedEvent(); | ||
|
||
await page.keyboard.press("Enter"); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("esri"); | ||
expect(eventSpy).toHaveReceivedEventTimes(1); | ||
|
||
await page.keyboard.press("Space"); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("esri"); | ||
expect(eventSpy).toHaveReceivedEventTimes(2); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
:host { | ||
@apply inline-flex outline-none; | ||
& a { | ||
@apply flex m-0 items-center justify-center cursor-pointer transition-default focus-base no-underline; | ||
border-block-end: 2px solid transparent; | ||
} | ||
& img { | ||
@apply flex h-7 m-0; | ||
} | ||
} | ||
|
||
a:hover, | ||
a:focus { | ||
@apply bg-foreground-2; | ||
} | ||
a:focus { | ||
@apply focus-inset; | ||
} | ||
a:active { | ||
@apply bg-foreground-3; | ||
} | ||
img { | ||
@apply px-4 pl-4; | ||
} | ||
img ~ .text-container { | ||
@apply pl-0; | ||
} | ||
:host(:active) a { | ||
@apply text-color-1; | ||
} | ||
:host([active]) a { | ||
z-index: 9; | ||
|
||
@apply text-color-1; | ||
border-color: var(--calcite-ui-brand); | ||
--calcite-ui-icon-color: var(--calcite-ui-brand); | ||
} | ||
|
||
.text-container { | ||
@apply flex px-4 truncate; | ||
flex-direction: column; | ||
text-align: start; | ||
} | ||
|
||
.logo-text { | ||
@apply text-0 ml-0 truncate text-color-1; | ||
font-weight: 500; | ||
margin-block-start: 2px; | ||
} | ||
|
||
.logo-subtext { | ||
@apply text-color-2 truncate; | ||
font-size: var(--calcite-font-size--1); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { boolean, storyFilters } from "../../../.storybook/helpers"; | ||
import { placeholderImage } from "../../../.storybook/placeholderImage"; | ||
import readme from "./readme.md"; | ||
import { html } from "../../../support/formatting"; | ||
import { text } from "@storybook/addon-knobs"; | ||
|
||
export default { | ||
title: "Components/Nav/Nav Logo", | ||
parameters: { | ||
notes: readme | ||
}, | ||
...storyFilters() | ||
}; | ||
|
||
export const simple = (): string => | ||
html`<calcite-nav> | ||
<calcite-nav-logo | ||
slot="logo" | ||
sub-text="${text("sub-text", "City of AcmeCo")}" | ||
text="${text("text", "ArcGIS Online")}" | ||
thumbnail="${placeholderImage({ width: 50, height: 50 })}" | ||
${boolean("active", false)} | ||
${boolean("text-enabled", true)} | ||
/> | ||
</calcite-nav-logo></calcite-nav>`; | ||
|
||
export const text_TestOnly = (): string => html` <calcite-shell> | ||
<calcite-nav slot="header"> | ||
<calcite-nav-logo slot="logo" text="ArcGIS Online" /> | ||
</calcite-nav> | ||
</calcite-shell>`; | ||
|
||
export const subText_TestOnly = (): string => html` <calcite-shell> | ||
<calcite-nav slot="header"> | ||
<calcite-nav-logo slot="logo" sub-text="City of AcmeCo" /> | ||
</calcite-nav> | ||
</calcite-shell>`; | ||
|
||
export const thumbnail_TestOnly = (): string => html` <calcite-shell> | ||
<calcite-nav slot="header"> | ||
<calcite-nav-logo slot="logo" thumbnail="${placeholderImage({ width: 50, height: 50 })}" /> | ||
</calcite-nav> | ||
</calcite-shell>`; | ||
|
||
export const textAndThumbnail_TestOnly = (): string => html` <calcite-shell> | ||
<calcite-nav slot="header"> | ||
<calcite-nav-logo slot="logo" text="ArcGIS Online" thumbnail="${placeholderImage({ width: 50, height: 50 })}" /> | ||
</calcite-nav> | ||
</calcite-shell>`; | ||
|
||
export const subTextAndThumbnail_TestOnly = (): string => html` <calcite-shell> | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<calcite-nav slot="header"> | ||
<calcite-nav-logo | ||
slot="logo" | ||
sub-text="City of AcmeCo" | ||
thumbnail="${placeholderImage({ width: 50, height: 50 })}" | ||
/> | ||
</calcite-nav> | ||
</calcite-shell>`; | ||
|
||
export const All_TestOnly = (): string => html` <calcite-shell> | ||
<calcite-nav slot="header"> | ||
<calcite-nav-logo | ||
slot="logo" | ||
text="ArcGIS Online" | ||
sub-text="City of AcmeCo" | ||
thumbnail="${placeholderImage({ width: 50, height: 50 })}" | ||
/> | ||
</calcite-nav> | ||
</calcite-shell>`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { Component, Element, EventEmitter, h, Host, Prop, Event } from "@stencil/core"; | ||
import { CSS } from "./resources"; | ||
|
||
@Component({ | ||
tag: "calcite-nav-logo", | ||
styleUrl: "nav-logo.scss", | ||
shadow: { | ||
delegatesFocus: true | ||
} | ||
}) | ||
export class CalciteNavLogo { | ||
//-------------------------------------------------------------------------- | ||
// | ||
// Element | ||
// | ||
//-------------------------------------------------------------------------- | ||
@Element() el: HTMLCalciteNavLogoElement; | ||
|
||
//-------------------------------------------------------------------------- | ||
// | ||
// Public Properties | ||
// | ||
//-------------------------------------------------------------------------- | ||
/** When `true`, visually highlight the component */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop({ reflect: true }) active: boolean; | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** Specifies the href destination of the component */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop({ reflect: true }) href: string; | ||
|
||
/** Specifies accesible label for the component */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop() label: string; | ||
|
||
/** Specifies the subtext to display, for example an organization or application description */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop() subText: string; | ||
|
||
/** Specifies the text to display, for example a product name */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop() text: string; | ||
|
||
/** When `true`, makes `text` and `subText` visible */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop({ reflect: true }) textEnabled: boolean; | ||
|
||
/** Specifies the `src` to an image */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Prop() thumbnail: string; | ||
|
||
//-------------------------------------------------------------------------- | ||
// | ||
// Events | ||
// | ||
//-------------------------------------------------------------------------- | ||
|
||
/** Emits when user select the component. */ | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Event() calciteNavLogoSelect: EventEmitter<void>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since there is no selection state, how is this (and other select events for navigation and menu components) different from a "click" event? If there's no distinction, I'd suggest removing them and have developers rely on the event target or provide specific event handlers to these components. For background: we did something similar some time ago with |
||
|
||
// -------------------------------------------------------------------------- | ||
// | ||
// Private Methods | ||
// | ||
// -------------------------------------------------------------------------- | ||
|
||
private clickHandler = (): void => { | ||
this.calciteNavLogoSelect.emit(); | ||
}; | ||
|
||
private keyDownHandler = (event: KeyboardEvent): void => { | ||
if (event.key === "Enter" || event.key === " ") { | ||
this.calciteNavLogoSelect.emit(); | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
event.preventDefault(); | ||
} | ||
}; | ||
|
||
// -------------------------------------------------------------------------- | ||
// | ||
// Render Methods | ||
// | ||
// -------------------------------------------------------------------------- | ||
render() { | ||
return ( | ||
<Host> | ||
<a | ||
aria-label={this.label} | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
href={this.href} | ||
onClick={this.clickHandler} | ||
onKeyDown={this.keyDownHandler} | ||
tabIndex={0} | ||
> | ||
{this.thumbnail && <img src={this.thumbnail} />} | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{(this.text || this.subText) && this.textEnabled && ( | ||
<div class={CSS.textContainer}> | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{this.text && this.textEnabled ? ( | ||
<span class={CSS.logoText} key={CSS.logoText}> | ||
{this.text} | ||
</span> | ||
) : null} | ||
{this.subText && this.textEnabled ? ( | ||
<span class={CSS.logoSubtext} key={CSS.logoSubtext}> | ||
{this.subText} | ||
</span> | ||
) : null} | ||
</div> | ||
)} | ||
</a> | ||
</Host> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# calcite-nav-logo | ||
|
||
<!-- Auto Generated Below --> | ||
|
||
## Properties | ||
|
||
| Property | Attribute | Description | Type | Default | | ||
| ------------- | -------------- | ---------------------------------------------------------------------------------------- | --------- | ----------- | | ||
| `active` | `active` | When `true`, visually highlight the component | `boolean` | `undefined` | | ||
| `href` | `href` | Specifies the href destination of the component | `string` | `undefined` | | ||
| `label` | `label` | Specifies accesible label for the component | `string` | `undefined` | | ||
| `subText` | `sub-text` | Specifies the subtext to display, for example an organization or application description | `string` | `undefined` | | ||
| `text` | `text` | Specifies the text to display, for example a product name | `string` | `undefined` | | ||
| `textEnabled` | `text-enabled` | When `true`, makes `text` and `subText` visible | `boolean` | `undefined` | | ||
| `thumbnail` | `thumbnail` | Specifies the `src` to an image | `string` | `undefined` | | ||
|
||
## Events | ||
|
||
| Event | Description | Type | | ||
| ---------------------- | ------------------------------------- | ------------------- | | ||
| `calciteNavLogoSelect` | Emits when user select the component. | `CustomEvent<void>` | | ||
|
||
--- | ||
|
||
_Built with [StencilJS](https://stenciljs.com/)_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const CSS = { | ||
textContainer: "text-container", | ||
logoText: "logo-text", | ||
logoSubtext: "logo-subtext" | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```html | ||
<calcite-nav-logo active thumbnail="./_assets/images/esri-logo.svg"></calcite-nav-user> | ||
anveshmekala marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { newE2EPage } from "@stencil/core/testing"; | ||
import { html } from "../../../support/formatting"; | ||
import { accessible, hidden, reflects, renders } from "../../tests/commonTests"; | ||
|
||
describe("calcite-nav-user", () => { | ||
describe("renders", () => { | ||
renders("calcite-nav-user", { display: "inline-flex" }); | ||
}); | ||
|
||
it("honors hidden attribute", () => hidden("calcite-nav-user")); | ||
|
||
describe("accessible", () => { | ||
accessible("calcite-nav-user"); | ||
}); | ||
|
||
it("reflects", () => | ||
reflects("calcite-nav-user", [ | ||
{ | ||
propertyName: "active", | ||
value: "true" | ||
}, | ||
{ | ||
propertyName: "hideText", | ||
value: "" | ||
} | ||
])); | ||
|
||
it("should emit calciteNavUserSelect event on user click", async () => { | ||
const page = await newE2EPage(); | ||
await page.setContent(html` <calcite-nav-user id="batman" username="batman"></calcite-nav-user> `); | ||
|
||
const element = await page.find("calcite-nav-user"); | ||
const eventSpy = await element.spyOnEvent("calciteNavUserSelect"); | ||
|
||
await element.click(); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("batman"); | ||
expect(eventSpy).toHaveReceivedEventTimes(1); | ||
}); | ||
|
||
it("should emit calciteNavUserSelect event when user select using Enter or Space key", async () => { | ||
const page = await newE2EPage(); | ||
await page.setContent(html` <calcite-nav-user username="batman" id="batman"></calcite-nav-user> `); | ||
|
||
const element = await page.find("calcite-nav-user"); | ||
const eventSpy = await element.spyOnEvent("calciteNavUserSelect"); | ||
|
||
await page.keyboard.press("Tab"); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("batman"); | ||
expect(eventSpy).not.toHaveReceivedEvent(); | ||
|
||
await page.keyboard.press("Enter"); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("batman"); | ||
expect(eventSpy).toHaveReceivedEventTimes(1); | ||
|
||
await page.keyboard.press("Space"); | ||
expect(await page.evaluate(() => document.activeElement.id)).toBe("batman"); | ||
expect(eventSpy).toHaveReceivedEventTimes(2); | ||
}); | ||
}); |
Uh oh!
There was an error while loading. Please reload this page.