Skip to content

Commit

Permalink
Showing 9 changed files with 66 additions and 147 deletions.
20 changes: 4 additions & 16 deletions packages/main/src/Breadcrumbs.hbs
Original file line number Diff line number Diff line change
@@ -22,28 +22,16 @@
href="{{this.href}}"
target="{{this.target}}"
id="{{this._id}}-link"
design="{{this._linkDesign}}"
accessible-name="{{this._accessibleNameText}}"
data-ui5-stable="{{this.stableDomRef}}">
{{this.innerText}}
</ui5-link>

<span class="ui5-breadcrumbs-separator" aria-hidden="true"></span>
{{#unless this._isCurrentPageItem}}
<span class="ui5-breadcrumbs-separator" aria-hidden="true"></span>
{{/unless}}
</li>
{{/each}}

{{#if _endsWithCurrentLocationLabel}}
<li class="ui5-breadcrumbs-current-location" @click="{{../_onLabelPress}}">

<span aria-current="page"
aria-label="{{_currentLocationAccName}}"
role="link"
id="{{this._id}}-labelWrapper">

<ui5-label>
{{_currentLocationText}}
</ui5-label>
</span>
</li>
{{/if}}
</ol>
</nav>
118 changes: 9 additions & 109 deletions packages/main/src/Breadcrumbs.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ import ItemNavigation from "@ui5/webcomponents-base/dist/delegate/ItemNavigation
import type { ITabbable } from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import {
isEnter,
isSpace,
isShow,
} from "@ui5/webcomponents-base/dist/Keys.js";
@@ -29,7 +28,6 @@ import {
} from "./generated/i18n/i18n-defaults.js";
import Link from "./Link.js";
import type { LinkClickEventDetail } from "./Link.js";
import Label from "./Label.js";
import ResponsivePopover from "./ResponsivePopover.js";
import List from "./List.js";
import type { ListSelectionChangeEventDetail } from "./List.js";
@@ -54,10 +52,6 @@ type BreadcrumbsItemClickEventDetail = {
shiftKey: boolean;
}

type FocusAdaptor = ITabbable & {
getlabelWrapper: () => Element | null;
}

/**
* @class
*
@@ -101,7 +95,6 @@ type FocusAdaptor = ITabbable & {
dependencies: [
BreadcrumbsItem,
Link,
Label,
ResponsivePopover,
List,
StandardListItem,
@@ -194,7 +187,6 @@ class Breadcrumbs extends UI5Element {
// the width of the interactive element that opens the overflow
_dropdownArrowLinkWidth = 0;
responsivePopover?: ResponsivePopover;
_labelFocusAdaptor: FocusAdaptor;
static i18nBundle: I18nBundle;

constructor() {
@@ -206,19 +198,6 @@ class Breadcrumbs extends UI5Element {
});

this._onResizeHandler = this._updateOverflow.bind(this);

this._labelFocusAdaptor = {
id: `${this._id}-labelWrapper`,
getlabelWrapper: this.getCurrentLocationLabelWrapper.bind(this),
set _tabIndex(value: string) {
const wrapper = this.getlabelWrapper();
wrapper && wrapper.setAttribute("tabindex", value);
},
get _tabIndex() {
const wrapper = this.getlabelWrapper();
return wrapper?.getAttribute("tabindex") || "";
},
};
}

onInvalidation(changeInfo: ChangeInfo) {
@@ -275,16 +254,11 @@ class Breadcrumbs extends UI5Element {
items.unshift(this._dropdownArrowLink);
}

if (this._endsWithCurrentLocationLabel) {
items.push(this._labelFocusAdaptor);
}
return items;
}

_onfocusin(e: FocusEvent) {
const target = e.target,
labelWrapper = this.getCurrentLocationLabelWrapper(),
currentItem = (target === labelWrapper) ? this._labelFocusAdaptor : target as Link;
const currentItem = e.target as Link;

this._itemNavigation.setCurrentItem(currentItem);
}
@@ -299,10 +273,6 @@ class Breadcrumbs extends UI5Element {
}
if (isSpace(e) && isDropdownArrowFocused && !this._isOverflowEmpty && !this._isPickerOpen) {
e.preventDefault();
return;
}
if ((isEnter(e) || isSpace(e)) && this._isCurrentLocationLabelFocused) {
this._onLabelPress(e);
}
}

@@ -318,21 +288,14 @@ class Breadcrumbs extends UI5Element {
*/
_cacheWidths() {
const map = this._breadcrumbItemWidths,
items = this._getItems(),
label = this._currentLocationLabel;
items = this._getItems();

for (let i = this._overflowSize; i < items.length; i++) {
const item = items[i],
link = this.shadowRoot!.querySelector<HTMLElement>(`#${item._id}-link-wrapper`)!;
map.set(item, this._getElementWidth(link));
}

if (items.length && this._endsWithCurrentLocationLabel && label) {
const item = items[items.length - 1];

map.set(item, this._getElementWidth(label));
}

if (!this._isOverflowEmpty) {
const arrow = this.shadowRoot!.querySelector<HTMLElement>(".ui5-breadcrumbs-dropdown-arrow-link-wrapper")!;
this._dropdownArrowLinkWidth = this._getElementWidth(arrow);
@@ -413,26 +376,12 @@ class Breadcrumbs extends UI5Element {
shiftKey,
}, true)) {
e.preventDefault();
return;
}
}

_onLabelPress(e: MouseEvent | KeyboardEvent) {
const items = this._getItems(),
item = items[items.length - 1],
{
altKey,
ctrlKey,
metaKey,
shiftKey,
} = e;

this.fireEvent<BreadcrumbsItemClickEventDetail>("item-click", {
item,
altKey,
ctrlKey,
metaKey,
shiftKey,
});
if (item._isCurrentPageItem) {
window.location.reload();
}
}

_onOverflowListItemSelect(e: CustomEvent<ListSelectionChangeEventDetail>) {
@@ -505,44 +454,20 @@ class Breadcrumbs extends UI5Element {
return text;
}

getCurrentLocationLabelWrapper() {
return this.shadowRoot!.querySelector<HTMLElement>(".ui5-breadcrumbs-current-location > span");
}

get _visibleItems() {
return this._getItems()
.slice(this._overflowSize)
.filter(i => this._isItemVisible(i));
}

get _endsWithCurrentLocationLabel() {
get _endsWithCurrentPageItem() {
return this.design === BreadcrumbsDesign.Standard;
}

get _currentLocationText() {
const items = this._getItems();
if (this._endsWithCurrentLocationLabel && items.length) {
const item = items[items.length - 1];
if (this._isItemVisible(item)) {
return item.innerText;
}
}
return "";
}

get _currentLocationLabel() {
return this.shadowRoot!.querySelector<Label>(".ui5-breadcrumbs-current-location [ui5-label]");
}

get _isDropdownArrowFocused() {
return this._dropdownArrowLink._tabIndex === "0";
}

get _isCurrentLocationLabelFocused() {
const label = this.getCurrentLocationLabelWrapper();
return label && label.tabIndex === 0;
}

/**
* Returns the maximum allowed count of items in the overflow
* with respect to the UX requirement to never overflow the last visible item
@@ -576,41 +501,16 @@ class Breadcrumbs extends UI5Element {
*/
get _linksData() {
const items = this._visibleItems;
const itemsCount = items.length; // get size before removing of current location

if (this._endsWithCurrentLocationLabel) {
items.pop();
}
const itemsCount = items.length;

return items
.map((item, index) => {
item._accessibleNameText = this._getItemAccessibleName(item, index + 1, itemsCount);
item._isCurrentPageItem = index === (itemsCount - 1) && this._endsWithCurrentPageItem;
return item;
});
}

/**
* Getter for accessible name of the current location. Includes the position of the current location and the size of the breadcrumbs
*/
get _currentLocationAccName() {
const items = this._visibleItems;

const positionText = this._getItemPositionText(items.length, items.length);
const lastItem = items[items.length - 1];

if (!lastItem) {
return positionText;
}

const lastItemText = lastItem.textContent || "";

if (lastItem.accessibleName) {
return `${lastItemText.trim()} ${lastItem.accessibleName} ${positionText}`;
}

return `${lastItemText.trim()} ${positionText}`;
}

/**
* Getter for the list of links corresponding to the abstract breadcrumb items
*/
6 changes: 6 additions & 0 deletions packages/main/src/BreadcrumbsItem.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import LinkDesign from "./types/LinkDesign.js";

/**
* @class
@@ -68,10 +69,15 @@ class BreadcrumbsItem extends UI5Element {
text!: Array<Node>;

_accessibleNameText?: string;
_isCurrentPageItem?: boolean;

get stableDomRef() {
return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`;
}

get _linkDesign() {
return this._isCurrentPageItem ? LinkDesign.Emphasized : LinkDesign.Default;
}
}

BreadcrumbsItem.define();
17 changes: 1 addition & 16 deletions packages/main/src/themes/Breadcrumbs.css
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
.ui5-breadcrumbs-root {
white-space: nowrap;
outline: none;
margin: 0 0 0.5rem 0;
margin:var(--_ui5_breadcrumbs_margin);
}

.ui5-breadcrumbs-root > ol {
@@ -22,21 +22,6 @@
display: inline;
}

.ui5-breadcrumbs-current-location {
min-width: 1%;
-webkit-flex: 1;
-webkit-box-flex: 1;
flex: 1 1 auto;
/* Fix extra height in ul -> li element */
font-size: 0;
align-self: center;
}

.ui5-breadcrumbs-current-location > span:focus {
outline: var(--sapContent_FocusWidth) var(--sapContent_FocusStyle) var(--sapContent_FocusColor);
border-radius: var(--_ui5_breadcrumbs_current_location_focus_border_radius);
}

.ui5-breadcrumbs-dropdown-arrow-link-wrapper[hidden] {
display: none
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:root {
--_ui5_breadcrumbs_current_location_focus_border_radius: 0.25rem;
--_ui5_breadcrumbs_margin: 0 0 0.5rem 0;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:root {
--_ui5_breadcrumbs_current_location_focus_border_radius: 0.25rem;
--_ui5_breadcrumbs_margin: 0 0 0.5rem 0;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:root {
--_ui5_breadcrumbs_current_location_focus_border_radius: 0.25rem;
--_ui5_breadcrumbs_margin: 0 0 0.5rem 0;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:root {
--_ui5_breadcrumbs_current_location_focus_border_radius: 0.25rem;
--_ui5_breadcrumbs_margin: 0 0 0.5rem 0;
}
44 changes: 42 additions & 2 deletions packages/main/test/specs/Breadcrumbs.spec.js
Original file line number Diff line number Diff line change
@@ -149,10 +149,10 @@ describe("Breadcrumbs general interaction", () => {

it("standard breadcrumb with single item shows location", async () => {
const breadcrumbs = await browser.$("#breadcrumbsWithSingleItem"),
label = (await breadcrumbs.shadow$("ui5-label"));
link = (await breadcrumbs.shadow$$("ui5-link"))[1];

// Check
assert.strictEqual(await label.getText(), "Location", "label is displayed");
assert.strictEqual(await link.getText(), "Location", "label is displayed");
});

it("opens upon space", async () => {
@@ -363,3 +363,43 @@ describe("Breadcrumbs general interaction", () => {
});

});

describe("Breadcrumbs with item for current page", () => {
before(async () => {
await browser.url(`test/pages/Breadcrumbs.html`);
});

it("renders current page item as link", async () => {
const breadcrumbs = await browser.$("#breadcrumbs2"),
link = (await breadcrumbs.shadow$("li:last-child ui5-link"));

// assert
assert.ok(await link.isExisting(), "item for current page is a link");
assert.strictEqual(await link.getText(), "Location",
"item for current page has correct text");
});

it("sets correct design to link for current page", async () => {
const breadcrumbs = await browser.$("#breadcrumbs2"),
link = (await breadcrumbs.shadow$("li:last-child ui5-link"));

// assert
assert.strictEqual(await link.getProperty("design"), "Emphasized",
"link has correct design");
});

it("does not render separator after link to current page", async () => {
const breadcrumbWithCurrentPage = await browser.$("#breadcrumbs2"),
breadcrumbWithNoCurrentPage = await browser.$("#breadcrumbs3"),
separatorAfterCurrentPageLink =
(await breadcrumbWithCurrentPage.shadow$("li:last-child span.ui5-breadcrumbs-separator")),
separatorAfterNonCurrentPageLink =
(await breadcrumbWithNoCurrentPage.shadow$("li:last-child span.ui5-breadcrumbs-separator"));

// assert
assert.ok(await separatorAfterNonCurrentPageLink.isExisting(),
"renders separator after link to another page");
assert.notOk(await separatorAfterCurrentPageLink.isExisting(),
"does not render separator after link to current page");
});
});

0 comments on commit 01f5542

Please sign in to comment.