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 all 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 text="Nature"></calcite-menu-item></calcite-menu>
```
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.

```html
<calcite-menu>
<calcite-menu-item text="Nature" href="#">
<calcite-menu-item text="Mountains" slot="submenu-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;
}
83 changes: 83 additions & 0 deletions src/components/menu-item/menu-item.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { newE2EPage } from "@stencil/core/testing";
import { html } from "../../../support/formatting";
import { accessible, focusable, hidden, reflects, renders, t9n } from "../../tests/commonTests";
import { getFocusedElementProp } from "../../tests/utils";

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

it("reflects", async () =>
reflects("calcite-menu-item", [
{
propertyName: "active",
value: "true"
},

{
propertyName: "target",
value: "_blank"
}
]));

describe("honors hidden attribute", () => {
hidden("calcite-menu-item");
});

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

describe("is focusable", () => {
focusable("calcite-menu-item");
});

it("supports translations", () => t9n("calcite-menu-item"));

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

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

await menuItem.click();
await page.waitForChanges();
expect(await getFocusedElementProp(page, "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="submenu-item"> </calcite-menu-item>
<calcite-menu-item id="Rivers" text="Rivers" slot="submenu-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 getFocusedElementProp(page, "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 getFocusedElementProp(page, "id")).toBe("Nature");
expect(eventSpy).toHaveReceivedEventTimes(2);
});
});
170 changes: 170 additions & 0 deletions src/components/menu-item/menu-item.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
:host {
@apply flex
items-center
relative
box-border
h-full;
flex-shrink: 0;
& .container,
& .item-content,
& .content {
min-block-size: theme("spacing.12");
}
}

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

.container,
.item-content {
@apply flex flex-row w-full h-full items-stretch;
}

.content {
@apply flex
items-center
relative
justify-center
cursor-pointer
outline-none
text-0
text-color-2
box-border
bg-foreground-1
px-4
h-full
w-full;
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 flex w-full justify-start;
padding-block: 1rem;
border-block-end: 0;
border-inline-end: theme("spacing.1") solid transparent;
}
}

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

.icon--start {
@apply me-3;
}

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

.icon--dropdown {
@apply ms-auto me-0 ps-2 relative;
--calcite-ui-icon-color: var(--calcite-ui-text-3);
}

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

.icon--breadcrumb {
@apply ps-2 me-0;
--calcite-ui-icon-color: var(--calcite-ui-text-3);
}

:host([breadcrumb]) .content {
@apply pe-3;
}

calcite-action {
@apply relative h-auto;
border-inline-start: 1px solid var(--calcite-ui-foreground-1);
&:after {
@apply block w-px absolute -start-px;
content: "";
inset-block: theme("spacing.3");
background-color: var(--calcite-ui-border-3);
}
&:hover:after {
@apply h-full;
inset-block: 0;
}
}

.content:focus ~ calcite-action,
.content: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 {
@apply absolute h-auto flex-col hidden overflow-visible min-w-full;
border: 1px solid var(--calcite-ui-border-3);
background: var(--calcite-ui-foreground-1);
inset-block-start: 100%;
z-index: theme("zIndex.dropdown");
&.open {
@apply block;
}
&.nested {
@apply absolute;
inset-block-start: -1px;
transform: translateX(calc(100% - 2px));
}
}

.parent--vertical {
@apply flex-col;
}

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

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

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

.dropdown--vertical.dropdown-menu-items.nested.calcite--rtl {
transform: none;
}

.hover-href-icon {
@apply ps-8 ms-auto relative end-1 opacity-0;
transition: all var(--calcite-internal-animation-timing-medium) ease-in-out;
}

content:focus .hover-href-icon,
content:hover .hover-href-icon {
@apply opacity-100 -end-1;
}
78 changes: 78 additions & 0 deletions src/components/menu-item/menu-item.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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)}
/>
</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)}
/>
</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)}
/>
</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, iconNames[0])}"
icon-start="${select("icon-start", iconNames, iconNames[0])}"
${boolean("active", false)}
${boolean("breadcrumb", false)}
/>
</calcite-menu>`;

export const darkModeRTL_TestOnly = (): string =>
html`<calcite-menu-item
text="My nav item"
active
dir="rtl"
class="calcite-mode-dark"
icon-start="Layers"
icon-end="Layers"
/>`;
Loading