Skip to content

Commit

Permalink
refactor(overflow-menu): bring out as internal component and refactor…
Browse files Browse the repository at this point in the history
… breadcrumb
  • Loading branch information
clukhei committed Dec 16, 2024
1 parent 30d3d6b commit e538955
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 47 deletions.
1 change: 1 addition & 0 deletions cypress/apps/angular-app/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<dropdown-component></dropdown-component>
<fileupload-component></fileupload-component>
<input-component></input-component>
<icon-component></icon-component>
<modal-component></modal-component>
<pagination-component></pagination-component>
<progress-component></progress-component>
Expand Down
3 changes: 2 additions & 1 deletion cypress/apps/angular-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import "@webcomponents/scoped-custom-element-registry";
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
Expand All @@ -19,6 +18,7 @@ import { Drawer } from "../components/drawer/drawer.component";
import { Dropdown } from "../components/dropdown/dropdown.component";
import { FileUpload } from "../components/fileupload/fileupload.component";
import { Footer } from "../components/footer/footer.component";
import { Icon } from "../components/icon/icon.component";
import { Input } from "../components/input/input.component";
import { Mainnav } from "../components/mainnav/mainnav.component";
import { Masthead } from "../components/masthead/masthead.component";
Expand Down Expand Up @@ -56,6 +56,7 @@ import { Tooltip } from "../components/tooltip/tooltip.component";
FileUpload,
Footer,
Input,
Icon,
Mainnav,
Masthead,
Modal,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<sgds-icon name="placeholder"></sgds-icon>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Component } from "@angular/core";

@Component({
selector: "icon-component",
templateUrl: "./icon.component.html"
})
export class Icon {}
12 changes: 8 additions & 4 deletions cypress/apps/next-app/src/app/components/Breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import SgdsBreadcrumbItem from "@govtechsg/sgds-web-component/react/breadcrumb-i
export const Breadcrumb = () => {
return (
<SgdsBreadcrumb ariaLabel="breadcrumb">
<SgdsBreadcrumbItem rel="noreferrer noopener" href="undefined" target="_blank">Home</SgdsBreadcrumbItem>
<SgdsBreadcrumbItem href="https://www.google.com">Item 1</SgdsBreadcrumbItem>
<SgdsBreadcrumbItem href="https://www.google.com">Item 2</SgdsBreadcrumbItem>
<SgdsBreadcrumbItem href="https://www.google.com">Item 3</SgdsBreadcrumbItem>
<SgdsBreadcrumbItem >
<a href="https://www.google.com">first</a>
</SgdsBreadcrumbItem>
<SgdsBreadcrumbItem><a href="https://www.google.com">first</a></SgdsBreadcrumbItem>
<SgdsBreadcrumbItem><a href="https://www.google.com">first</a></SgdsBreadcrumbItem>
<SgdsBreadcrumbItem><a href="https://www.google.com">first</a></SgdsBreadcrumbItem>
<SgdsBreadcrumbItem><a href="https://www.google.com">first</a></SgdsBreadcrumbItem>
<SgdsBreadcrumbItem><a href="https://www.google.com">first</a></SgdsBreadcrumbItem>
<SgdsBreadcrumbItem>Last Item</SgdsBreadcrumbItem>
</SgdsBreadcrumb>
)
Expand Down
15 changes: 15 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,21 @@ <h2>Table w/ RemovableSort</h2>
<div class="container">
<div class="row">
<h2>Overflow</h2>
<sgds-overflow-menu>
<sgds-dropdown-item active><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-upload" viewBox="0 0 16 16">
<path
d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
<path
d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
</svg>Option label</sgds-dropdown-item>
<sgds-dropdown-item disabled>Option label</sgds-dropdown-item>
<sgds-dropdown-item href="https://www.google.com">Google</sgds-dropdown-item>
<sgds-dropdown-item>Option label</sgds-dropdown-item>
<sgds-dropdown-item>Option label</sgds-dropdown-item>
<sgds-dropdown-item>Option label</sgds-dropdown-item>
</sgds-overflow-menu>

<sgds-dropdown menuVariant="default">
<sgds-icon-button slot="toggler" variant="ghost" size="sm" role="button" aria-haspopup="menu">
<sgds-icon name="three-dots"></sgds-icon>
Expand Down
35 changes: 10 additions & 25 deletions src/components/Breadcrumb/sgds-breadcrumb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { property, query } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { html } from "lit/static-html.js";
import SgdsElement from "../../base/sgds-element";
import { warnUnregisteredElements } from "../../utils/ce-registry";
import SgdsOverflowMenu from "../../internals/OverflowMenu/sgds-overflow-menu";
import breadcrumbStyle from "./breadcrumb.css";
import type SgdsBreadcrumbItem from "./sgds-breadcrumb-item";
/**
Expand All @@ -13,53 +13,39 @@ import type SgdsBreadcrumbItem from "./sgds-breadcrumb-item";
*/
export class SgdsBreadcrumb extends SgdsElement {
static styles = [...SgdsElement.styles, breadcrumbStyle];
static dependencies = {
"sgds-overflow-menu": SgdsOverflowMenu
};
/** The aria-label of nav element within breadcrumb component. */
@property({ type: String }) ariaLabel = "breadcrumb";

/**@internal */
@query("slot") defaultSlot: HTMLSlotElement;

private _checkDependencies() {
warnUnregisteredElements("sgds-dropdown");
warnUnregisteredElements("sgds-icon-button");
warnUnregisteredElements("sgds-icon");
}
/**
* creates `<sgds-breadcrumb-item>
* <sgds-dropdown>
* <sgds-icon-button slot="toggler">
* <sgds-icon>
* </sgds-icon-button>
* <sgds-overflow-menu>
* <sgds-dropdown-item></sgds-dropdown-item>
* ...
* </sgds-dropdown>
* </sgds-overflow-menu>
* <sgds-breadcrumb-item>`
*/
private _replaceExcessItemsWithDropdown(items: SgdsBreadcrumbItem[]) {
const breadcrumbItem = document.createElement("sgds-breadcrumb-item");
const dropdown = document.createElement("sgds-dropdown");
const overflowButton = document.createElement("sgds-icon-button");
const icon = document.createElement("sgds-icon");
icon.setAttribute("name", "three-dots");
overflowButton.setAttribute("slot", "toggler");
overflowButton.setAttribute("variant", "ghost");
overflowButton.setAttribute("role", "button");
overflowButton.setAttribute("aria-haspopup", "menu");
overflowButton.appendChild(icon);
dropdown.appendChild(overflowButton);
const overflowMenu = document.createElement("sgds-overflow-menu");
overflowMenu.setAttribute("aria-haspopup", "menu");
const mapItems = items.filter((item, index) => {
if (index > 0 && index < items.length - 2) {
const clonedAnchor = item.querySelector("a");
const clonedAnchorNode = clonedAnchor.cloneNode(true);
const dropdownItem = document.createElement("sgds-dropdown-item");
dropdownItem.appendChild(clonedAnchorNode);
dropdown.appendChild(dropdownItem);
overflowMenu.appendChild(dropdownItem);
return;
} else {
return item;
}
});
breadcrumbItem.appendChild(dropdown);
breadcrumbItem.appendChild(overflowMenu);
mapItems.splice(1, 0, breadcrumbItem);

this.defaultSlot.replaceWith(...mapItems);
Expand All @@ -78,7 +64,6 @@ export class SgdsBreadcrumb extends SgdsElement {
});

if (items.length >= 5) {
this._checkDependencies();
this._replaceExcessItemsWithDropdown(items);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/internals/OverflowMenu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SgdsOverflowMenu } from "./sgds-overflow-menu";
import { register } from "../../utils/ce-registry";

register("sgds-overflow-menu", SgdsOverflowMenu);

declare global {
interface HTMLElementTagNameMap {
"sgds-overflow-menu": SgdsOverflowMenu;
}
}
23 changes: 23 additions & 0 deletions src/internals/OverflowMenu/overflow-menu.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.overflow-btn {
width: var(--sgds-dimension-32);
height: var(--sgds-dimension-32);
background-color: var(--sgds-default-bg-transparent);
border-radius: var(--sgds-border-radius-sm);
border: 0;
padding: 0;
position: relative;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.overflow-btn:hover {
background-color: var(--sgds-default-bg-translucent);
}

.overflow-btn:focus,
.overflow-btn:focus-visible {
outline: 0;
box-shadow: var(--sgds-box-shadow-focus);
background-color: var(--sgds-default-bg-translucent);
}
34 changes: 34 additions & 0 deletions src/internals/OverflowMenu/sgds-overflow-menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { html } from "lit";
import SgdsElement from "../../base/sgds-element";
import overflowMenuStyles from "./overflow-menu.css";
import { property } from "lit/decorators.js";
import { SgdsDropdown } from "../../components/Dropdown/sgds-dropdown";
import { SgdsDropdownItem } from "../../components/Dropdown/sgds-dropdown-item";
import { SgdsIcon } from "../../components/Icon/sgds-icon";
/**
* @summary An overflow menu is a UI element, often represented by three dots (⋮ or …), that opens a menu with additional actions or options.
* @slot default - The overflow menu items. Pass in sgds-dropdown-items in this slot
*/
export class SgdsOverflowMenu extends SgdsElement {
static styles = [...SgdsElement.styles, overflowMenuStyles];
static dependencies = {
"sgds-dropdown": SgdsDropdown,
"sgds-dropdown-item": SgdsDropdownItem,
"sgds-icon": SgdsIcon
};
/** Specifies a large or small button */
@property({ type: String, reflect: true }) size: "sm" | "md" = "md";

render() {
return html`
<sgds-dropdown>
<button slot="toggler" class="overflow-btn">
<sgds-icon name="three-dots" size=${this.size}></sgds-icon>
</button>
<slot></slot>
</sgds-dropdown>
`;
}
}

export default SgdsOverflowMenu;
19 changes: 2 additions & 17 deletions test/breadcrumb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,7 @@ describe("sgds-breadcrumb", () => {
size="md"
variant="primary"
>
<sgds-dropdown drop="down">
<sgds-icon-button
aria-haspopup="menu"
role="button"
size="md"
slot="toggler"
target="_self"
variant="ghost"
>
<sgds-icon
name="three-dots"
size="lg"
style="display: none;"
>
</sgds-icon>
</sgds-icon-button>
<sgds-overflow-menu aria-haspopup="menu" size="md">
<sgds-dropdown-item
aria-disabled="false"
role="menuitem"
Expand All @@ -95,7 +80,7 @@ describe("sgds-breadcrumb", () => {
Contacts
</a>
</sgds-dropdown-item>
</sgds-dropdown>
</sgds-overflow-menu>
</sgds-breadcrumb-item>
<sgds-breadcrumb-item
size="md"
Expand Down
20 changes: 20 additions & 0 deletions test/overflow-menu.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { assert, fixture } from "@open-wc/testing";
import { html } from "lit";
import "../src/internals/OverflowMenu";

describe("<sgds-overflow-menu>", () => {
it("semantically matches the DOM", async () => {
const el = await fixture(html`<sgds-overflow-menu></sgds-overflow-menu>`);
assert.shadowDom.equal(
el,
`
<sgds-dropdown drop="down">
<button slot="toggler" class="overflow-btn">
<sgds-icon name="three-dots" size="md" style="display: none;"></sgds-icon>
</button>
<slot></slot>
</sgds-dropdown>
`
);
});
});

0 comments on commit e538955

Please sign in to comment.