Skip to content
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

feat(menu, menu-item): Adds menu & menu-item components. #6901

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4a60b88
feat(menu,menu-item): Adds menu & menu-item components.
anveshmekala May 2, 2023
29a9822
update dom util method
anveshmekala May 3, 2023
cdb6238
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 3, 2023
15392ce
update common util tests
anveshmekala May 3, 2023
a30cd27
refactor
anveshmekala May 3, 2023
a8ba656
fix test error
anveshmekala May 4, 2023
47f6e44
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 4, 2023
cb9c37f
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 4, 2023
d972bdb
refactor
anveshmekala May 4, 2023
5f21d0b
Add demo css
macandcheese May 4, 2023
4a72f5f
fix css issue
anveshmekala May 4, 2023
de88018
refactor
anveshmekala May 5, 2023
96298be
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 5, 2023
e8a25a8
add usage files
anveshmekala May 5, 2023
d153f37
update readme files
anveshmekala May 5, 2023
b09d8aa
add waitForChanges in tests
anveshmekala May 5, 2023
9ef8b37
remove calcite-nav in stories
anveshmekala May 5, 2023
fc38b1f
add missing iconEnd and fix stoties
anveshmekala May 5, 2023
8725ed3
remove reflect on data rich props
anveshmekala May 5, 2023
71a58e6
update index file
anveshmekala May 5, 2023
eed1631
reflect layout prop
anveshmekala May 5, 2023
d2bfa3d
doc feedback changes
anveshmekala May 5, 2023
bf4a06a
Clean up
macandcheese May 5, 2023
7dbfc7e
feedback changes
anveshmekala May 6, 2023
e0f8c5f
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 8, 2023
7bf6557
feedback changes
anveshmekala May 8, 2023
a98d8f0
replace tailwind classes with logical props
anveshmekala May 8, 2023
a8bd6d9
update tests
anveshmekala May 8, 2023
a4a7f94
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 8, 2023
f44308c
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 9, 2023
9353872
fix screenshot test
anveshmekala May 9, 2023
4d09582
refactor common util tests
anveshmekala May 9, 2023
5167d55
Add darkMode & RTL stories
anveshmekala May 9, 2023
3d09c64
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 9, 2023
56d9d26
fix merge conflict error
anveshmekala May 9, 2023
43f9076
more feedback changes
anveshmekala May 11, 2023
9dcafc1
fix test failures and refactor switch statements
anveshmekala May 12, 2023
cff5c89
Merge branch 'master' into anveshmekala/6531-feat-add-menu-and-menuIt…
anveshmekala May 12, 2023
aed03cf
fix tests
anveshmekala May 12, 2023
5fe121d
make anchor element in menu-item occupy full width
anveshmekala May 12, 2023
142e4fe
rename SubMenu to Submenu
anveshmekala May 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/menu-item/Usage/Basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```html
<calcite-menu> <calcite-menu-item id="Nature" text="Nature"> </calcite-menu-item></calcite-menu>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: maybe we should take out the id attributes from the usage files

```
9 changes: 9 additions & 0 deletions src/components/menu-item/Usage/Nested-With-Href.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Nested SubMenu with href.
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved

```html
<calcite-menu>
<calcite-menu-item id="Nature" text="Nature" href="#">
<calcite-menu-item id="Mountains" text="Mountains" slot="sub-menu-item"> </calcite-menu-item>
</calcite-menu-item>
</calcite-menu>
```
5 changes: 5 additions & 0 deletions src/components/menu-item/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface MenuItemCustomEvent {
event: KeyboardEvent;
children?: HTMLCalciteMenuItemElement[];
isSubMenuOpen?: boolean;
}
96 changes: 96 additions & 0 deletions src/components/menu-item/menu-item.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { newE2EPage } from "@stencil/core/testing";
import { html } from "../../../support/formatting";
import { accessible, focusable, hidden, reflects, renders } from "../../tests/commonTests";

describe("calcite-menu-item", () => {
describe("renders", () => {
renders("calcite-menu-item", { display: "flex" });
});

it("reflects", async () =>
reflects("calcite-menu-item", [
{
propertyName: "active",
value: "true"
},
{
propertyName: "iconStart",
value: "layers"
},
{
propertyName: "iconEnd",
value: "layers"
},
{
propertyName: "href",
value: "www.esri.com"
},
{
propertyName: "target",
value: "_blank"
},
{
propertyName: "text",
value: "Calcite"
}
]));

it("honors hidden attribute", async () => hidden("calcite-menu-item"));

describe("accessible", () => {
accessible(html`<calcite-menu> <calcite-menu-item text="calcite"> </calcite-menu-item> </calcite-menu>`);
});

it("is focusable", () => focusable("calcite-menu-item"));
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved

//todo : debug why calcite-menu-item requires calite-menu as parent for the click to emit event in test.
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being caused by the click-handling element (anchor) not stretching along with its host and therefore not being in the center when the click happens (E2EElement.click() scrolls and clicks on the center of the element). If you set inline-size: 100% on the anchor, it fixes the test, but not sure if that will cause layout issues (design-wise).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense. we can mark inline-size:100% for anchor element and the design will be similar.

it("should emit calciteMenuItemSelect event on user click", async () => {
const page = await newE2EPage();
await page.setContent(
html`<calcite-menu>
<calcite-menu-item id="Nature" text="Nature" href="#nature"> </calcite-menu-item>
</calcite-menu>`
);

const menuItem = await page.find("calcite-menu-item");
const eventSpy = await menuItem.spyOnEvent("calciteMenuItemSelect");

await menuItem.click();
await page.waitForChanges();
expect(await page.evaluate(() => document.activeElement.id)).toBe("Nature");
expect(eventSpy).toHaveReceivedEventTimes(1);
});

it("should emit calciteMenuItemSelect event when user select the text area of the component using Enter or Space key", async () => {
const page = await newE2EPage();
await page.setContent(html`
<calcite-menu>
<calcite-menu-item id="Nature" text="Nature" href="#nature">
<calcite-menu-item id="Mountains" text="Mountains" slot="sub-menu-item"> </calcite-menu-item>
<calcite-menu-item id="Rivers" text="Rivers" slot="sub-menu-item"> </calcite-menu-item>
</calcite-menu-item>
</calcite-menu>
`);

const element = await page.find("calcite-menu-item");
const eventSpy = await element.spyOnEvent("calciteMenuItemSelect");

await page.keyboard.press("Tab");
await page.waitForChanges();
expect(await page.evaluate(() => document.activeElement.id)).toBe("Nature");
expect(eventSpy).not.toHaveReceivedEvent();

await page.keyboard.press("Enter");
await page.waitForChanges();
expect(eventSpy).toHaveReceivedEventTimes(1);

await page.keyboard.press("Space");
await page.waitForChanges();
expect(eventSpy).toHaveReceivedEventTimes(2);

await page.keyboard.press("Tab");
await page.waitForChanges();
expect(await page.evaluate(() => document.activeElement.id)).toBe("Nature");
expect(eventSpy).toHaveReceivedEventTimes(2);
});
});
200 changes: 200 additions & 0 deletions src/components/menu-item/menu-item.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
:host {
@apply flex
items-center
relative
h-full
box-border;
}

:host(:not([layout="vertical"])) {
flex-shrink: 0;
& .container,
& .item-content,
& a {
min-block-size: theme("spacing.12");
}
}

:host([layout="vertical"]) {
@apply w-full;
}

.container,
.item-content {
display: flex;
flex-direction: row;
block-size: 100%;
inline-size: 100%;
align-items: stretch;
}

a {
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
@apply flex
h-full
items-center
relative
justify-center
cursor-pointer
px-4
outline-none
text-0
text-color-2 box-border bg-foreground-1;
text-decoration: none;
border-block-end: theme("spacing[0.5]") solid transparent;
padding-block-start: theme("spacing[0.5]");
&:hover {
@apply bg-foreground-2 text-color-2;
}
&:focus {
@apply bg-foreground-2 text-color-2 focus-inset;
}
&:active {
@apply bg-foreground-3 text-color-1;
}
& span {
display: inline-flex;
}
&.layout--vertical {
@apply py-4 w-full;
display: flex;
justify-content: flex-start;
border-block-end: 0;
border-inline-end: theme("spacing.1") solid transparent;
}
}

:host([active]) a {
@apply text-color-1;
border-color: var(--calcite-ui-brand);
.icon {
--calcite-ui-icon-color: var(--calcite-ui-brand);
}
}

.icon--start {
@apply mr-3;
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
}

.icon--end {
@apply ml-3;
}

.icon--dropdown {
@apply pl-2 mr-0 ml-auto;
--calcite-ui-icon-color: var(--calcite-ui-text-3);
position: relative;
}

:host([layout="vertical"]) .icon--dropdown {
inset-inline-start: theme("spacing.1");
}

.icon--breadcrumb {
@apply pl-2 mr-0;
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
--calcite-ui-icon-color: var(--calcite-ui-text-3);
}

:host([breadcrumb]) a {
@apply pr-3;
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
}

calcite-action {
block-size: auto;
position: relative;
border-inline-start: 1px solid var(--calcite-ui-foreground-1);
&:after {
display: block;
inline-size: 1px;
content: "";
inset-block: theme("spacing.3");
background-color: var(--calcite-ui-border-3);
position: absolute;
inset-inline-start: -1px;
z-index: 9;
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
}
&:hover:after {
block-size: 100%;
inset-block: 0;
}
}

a:focus ~ calcite-action,
a:hover ~ calcite-action {
@apply text-color-1;
border-inline-start: 1px solid var(--calcite-ui-border-3);
}

.container:hover .dropdown-action {
@apply bg-foreground-2;
}

.dropdown-menu-items {
border: 1px solid var(--calcite-ui-border-3);
position: absolute;
background: var(--calcite-ui-foreground-1);
z-index: 9;
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
block-size: auto;
flex-direction: column;
min-inline-size: 100%;
display: none;
transition: all var(--calcite-internal-animation-timing-slow) ease-in-out;
overflow: visible;
inset-block-start: 100%;

&.open {
display: block;
}
&.nested {
z-index: 9;
anveshmekala marked this conversation as resolved.
Show resolved Hide resolved
inset-block-start: -1px;
position: absolute;
transform: translateX(calc(100% - 2px));
}
}

.parent--vertical {
flex-direction: column;
}

.dropdown--vertical.dropdown-menu-items {
position: relative;
border-radius: 0;
box-shadow: none;
inset-block-start: 0;
transform: none;
&:last-of-type {
border-inline: 0;
}
}

:host([slot="sub-menu-item"]) .parent--vertical {
padding-inline-start: theme("spacing.7");
}

.dropdown-menu-items.is-rtl.nested {
transform: translateX(calc(-100% + 2px));
}

.dropdown--vertical.dropdown-menu-items.is-rtl.nested {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should revisit this later to reduce specificity. This also bypasses the way we've been applying our BEM-like™ classes.

transform: none;
}

.dropdown-action {
z-index: 0;
}

.hover-href-icon {
opacity: 0;
padding-inline-start: 2rem;
margin-inline-start: auto;
transition: opacity var(--calcite-internal-animation-timing-medium) ease-in-out,
inset-inline-end var(--calcite-internal-animation-timing-slow) ease-in-out;
inset-inline-end: 0.25rem;
position: relative;
}

a:focus .hover-href-icon,
a:hover .hover-href-icon {
opacity: 1;
inset-inline-end: -0.25rem;
}
72 changes: 72 additions & 0 deletions src/components/menu-item/menu-item.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { boolean, iconNames, storyFilters } from "../../../.storybook/helpers";
import readme from "./readme.md";
import { html } from "../../../support/formatting";
import { select, text } from "@storybook/addon-knobs";

export default {
title: "Components/Menu Item",
parameters: {
notes: readme
},
...storyFilters()
};

export const simple = (): string => html` <calcite-menu>
<calcite-menu-item
text="${text("text", "My nav item")}"
src="${text("src", "")}"
href="${text("href", "")}"
rel="${text("rel", "")}"
target="${text("target", "")}"
label="${text("label", "")}"
${boolean("active", false)}
${boolean("breadcrumb", false)}
${boolean("text-enabled", true)}
/>
</calcite-menu>`;

export const iconStart = (): string => html` <calcite-menu>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't the iconsBoth story cover both iconStart and iconEnd?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for keeping this is to verify how the iconEnd alone works in terms of spacing.

<calcite-menu-item
text="${text("text", "My nav item")}"
src="${text("src", "")}"
href="${text("href", "")}"
rel="${text("rel", "")}"
target="${text("target", "")}"
label="${text("label", "")}"
icon-start="${select("icon-start", iconNames, iconNames[0])}"
${boolean("active", false)}
${boolean("breadcrumb", false)}
${boolean("text-enabled", true)}
/>
</calcite-menu>`;

export const iconEnd = (): string => html` <calcite-menu>
<calcite-menu-item
text="${text("text", "My nav item")}"
src="${text("src", "")}"
href="${text("href", "")}"
rel="${text("rel", "")}"
target="${text("target", "")}"
label="${text("label", "")}"
icon-end="${select("icon-end", iconNames, iconNames[0])}"
${boolean("active", false)}
${boolean("breadcrumb", false)}
${boolean("text-enabled", true)}
/>
</calcite-menu>`;

export const iconsBoth = (): string => html` <calcite-menu>
<calcite-menu-item
text="${text("text", "My nav item")}"
src="${text("src", "")}"
href="${text("href", "")}"
rel="${text("rel", "")}"
target="${text("target", "")}"
label="${text("label", "")}"
icon-end="${select("icon-end", iconNames, "")}"
icon-start="${select("icon-start", iconNames, "")}"
${boolean("active", false)}
${boolean("breadcrumb", false)}
${boolean("text-enabled", true)}
/>
</calcite-menu>`;
Loading