Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 packages/fiori/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ such as a common header (ShellBar).
| User Settings Appearance View Item | `ui5-user-settings-appearance-view-item`| `import "@ui5/webcomponents-fiori/dist/UserSettingsAppearanceViewItem.js";`|
| User Settings Appearance View Group | `ui5-user-settings-appearance-view-group`| `import "@ui5/webcomponents-fiori/dist/UserSettingsAppearanceViewGroup.js";`|
| User Settings View | `ui5-user-settings-view` | `import "@ui5/webcomponents-fiori/dist/UserSettingsView.js";` |
| User Settings Notifications View | `ui5-user-settings-notifications-view` | `import "@ui5/webcomponents-fiori/dist/UserSettingsNotificationsView.js";` |
| User Settings Notifications View Group | `ui5-user-settings-notifications-view-group` | `import "@ui5/webcomponents-fiori/dist/UserSettingsNotificationsViewGroup.js";` |
| User Settings Notifications View Item | `ui5-user-settings-notifications-view-item` | `import "@ui5/webcomponents-fiori/dist/UserSettingsNotificationsViewItem.js";` |
| View Settings Dialog | `ui5-view-settings-dialog` | `import "@ui5/webcomponents-fiori/dist/ViewSettingsDialog.js";` |
| View Settings Dialog - Sort Item | `ui5-sort-item` | `import "@ui5/webcomponents-fiori/dist/SortItem.js";` |
| View Settings Dialog - Filter Item | `ui5-filter-item` | `import "@ui5/webcomponents-fiori/dist/FilterItem.js";` |
Expand Down
890 changes: 890 additions & 0 deletions packages/fiori/cypress/specs/UserSettingsNotificationsView.cy.tsx

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions packages/fiori/src/UserSettingsItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,15 @@ class UserSettingsItem extends UI5Element {
return !!(this._inMobileView || (this._hasSelectedPageView && this._selectedPageView.secondary));
}

/**
* @private
* @since 2.26.0
*/
_focusBackButton() {
const backButton = this.shadowRoot?.querySelector<HTMLElement>(".ui5-user-settings-item-collapse-btn");
backButton?.focus();
}

captureRef(this: UserSettingsView, ref: HTMLElement & { associatedSettingView?: UserSettingsView} | null) {
if (ref) {
ref.associatedSettingView = this;
Expand Down
185 changes: 185 additions & 0 deletions packages/fiori/src/UserSettingsNotificationsView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import UserSettingsView from "./UserSettingsView.js";
import UserSettingsNotificationsViewTemplate from "./UserSettingsNotificationsViewTemplate.js";
import UserSettingViewCss from "./generated/themes/UserSettingsView.css.js";
import UserSettingsNotificationsViewCss from "./generated/themes/UserSettingsNotificationsView.css.js";
import type UserSettingsNotificationsViewItem from "./UserSettingsNotificationsViewItem.js";
import { isInstanceOfUserSettingsNotificationsViewItem } from "./UserSettingsNotificationsViewItem.js";
import type UserSettingsNotificationsViewGroup from "./UserSettingsNotificationsViewGroup.js";
import { isInstanceOfUserSettingsNotificationsViewGroup } from "./UserSettingsNotificationsViewGroup.js";
import type UserSettingsItem from "./UserSettingsItem.js";
import type { ListItemClickEventDetail } from "@ui5/webcomponents/dist/List.js";
import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";

import {
customElement, slotStrict as slot, eventStrict as event,
} from "@ui5/webcomponents-base/dist/decorators.js";
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import type { DefaultSlot, Slot } from "@ui5/webcomponents-base/dist/UI5Element.js";

type UserSettingsNotificationsViewItemClickEventDetail = {
item: UserSettingsNotificationsViewItem;
}

/**
* @class
* ### Overview
*
* The `ui5-user-settings-notifications-view` represents a view displayed in the
* `ui5-user-settings-item` that lists notification preferences. Individual settings
* are represented by `ui5-user-settings-notifications-view-item` elements, optionally
* grouped by `ui5-user-settings-notifications-view-group`.
*
* When a navigable item is clicked, the view drills into a sibling secondary view of
* its parent `ui5-user-settings-item`. When an item's `item-key` matches a target
* view's `id`, that view is opened and keeps its own `text`. Otherwise the first
* sibling marked as `secondary` is opened and its `text` is set to the clicked
* item's `text` so the drill-in header reflects the origin.
*
* Apps can override this behavior by preventing the `item-click` event.
*
* Applications should listen to the item's `switch-change` event (which bubbles) to
* be notified when a switch is toggled.
*
* Additional content (e.g. an information message strip) can be placed via the
* `additionalContent` slot, which is rendered above the list.
*
* ### ES6 Module Import
* `import "@ui5/webcomponents-fiori/dist/UserSettingsNotificationsView.js";`
*
* @constructor
* @extends UserSettingsView
* @public
* @since 2.26.0
*/
@customElement({
tag: "ui5-user-settings-notifications-view",
renderer: jsxRenderer,
template: UserSettingsNotificationsViewTemplate,
styles: [UserSettingViewCss, UserSettingsNotificationsViewCss],
})

/**
* Fired when a navigable item in the list is clicked.
*
* The event is cancelable: preventing it skips the built-in drill-in to the
* parent's secondary view, allowing the application to take over.
*
* @param {UserSettingsNotificationsViewItem} item The clicked notifications view item.
* @public
*/
@event("item-click", {
cancelable: true,
})

class UserSettingsNotificationsView extends UserSettingsView {
eventDetails!: {
"item-click": UserSettingsNotificationsViewItemClickEventDetail;
}

/**
* Defines the items of the component. Can be a mix of
* `ui5-user-settings-notifications-view-item` and
* `ui5-user-settings-notifications-view-group` elements.
*
* @public
*/
@slot({
type: HTMLElement,
"default": true,
invalidateOnChildChange: true,
})
items!: DefaultSlot<UserSettingsNotificationsViewGroup | UserSettingsNotificationsViewItem>;

/**
* Defines additional content displayed above the items list.
*
* @public
*/
@slot({
type: HTMLElement,
})
additionalContent!: Slot<HTMLElement>;

/**
* Returns a flat list of all notification items, including items nested inside groups.
*
* @public
*/
getAllItems(): Array<UserSettingsNotificationsViewItem> {
const allItems: Array<UserSettingsNotificationsViewItem> = [];

this.items.forEach(item => {
if (isInstanceOfUserSettingsNotificationsViewGroup(item)) {
item.items.forEach(child => {
if (isInstanceOfUserSettingsNotificationsViewItem(child)) {
allItems.push(child);
}
});
} else if (isInstanceOfUserSettingsNotificationsViewItem(item)) {
allItems.push(item);
}
});

return allItems;
}

/**
* Returns the first item with the given `itemKey`, or `undefined` if none matches.
*
* @public
*/
getItemByKey(itemKey: string): UserSettingsNotificationsViewItem | undefined {
return this.getAllItems().find(item => item.itemKey === itemKey);
}

_navigateToSecondaryView(item: UserSettingsNotificationsViewItem) {
const parentItem = this.closest<UserSettingsItem>("[ui5-user-settings-item]");
const secondaryViews = parentItem?.pages?.filter(view => view !== this && view.secondary) ?? [];

const matched = item.itemKey ? secondaryViews.find(view => view.id === item.itemKey) : undefined;
const target = matched ?? secondaryViews[0];

if (!target) {
return;
}

if (!matched || !target.text) {
target.text = item.text;
}

this.selected = false;
target.selected = true;

if (item.matches(":focus-visible")) {
renderFinished().then(() => {
if (!this.isConnected) {
return;
}
parentItem?._focusBackButton?.();
});
}
}

_handleItemClick = (e: CustomEvent<ListItemClickEventDetail>) => {
const listItem = e.detail.item;
if (!isInstanceOfUserSettingsNotificationsViewItem(listItem) || !listItem.navigable) {
return;
}

const eventPrevented = !this.fireDecoratorEvent("item-click", {
item: listItem,
});

if (eventPrevented) {
return;
}

this._navigateToSecondaryView(listItem);
};
}

UserSettingsNotificationsView.define();
export default UserSettingsNotificationsView;
export type {
UserSettingsNotificationsViewItemClickEventDetail,
};
49 changes: 49 additions & 0 deletions packages/fiori/src/UserSettingsNotificationsViewGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import ListItemGroup from "@ui5/webcomponents/dist/ListItemGroup.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
import createInstanceChecker from "@ui5/webcomponents-base/dist/util/createInstanceChecker.js";
import UserSettingsNotificationsViewGroupCss from "./generated/themes/UserSettingsNotificationsViewGroup.css.js";
import type UserSettingsNotificationsViewItem from "./UserSettingsNotificationsViewItem.js";
import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";

/**
* @class
* ### Overview
*
* The `ui5-user-settings-notifications-view-group` groups `ui5-user-settings-notifications-view-item`
* elements inside a `ui5-user-settings-notifications-view`. Its header renders as a plain bold
* section title with a separator line below, per the notifications design spec.
*
* ### ES6 Module Import
* `import "@ui5/webcomponents-fiori/dist/UserSettingsNotificationsViewGroup.js";`
*
* @constructor
* @extends ListItemGroup
* @public
* @since 2.26.0
*/
@customElement({
tag: "ui5-user-settings-notifications-view-group",
styles: [ListItemGroup.styles, UserSettingsNotificationsViewGroupCss],
})
class UserSettingsNotificationsViewGroup extends ListItemGroup {
/**
* Defines the items of the `ui5-user-settings-notifications-view-group`.
* @public
*/
@slot({
"default": true,
invalidateOnChildChange: true,
type: HTMLElement,
})
declare items: DefaultSlot<UserSettingsNotificationsViewItem>;

get isUserSettingsNotificationsViewGroup(): boolean {
return true;
}
}

UserSettingsNotificationsViewGroup.define();

export const isInstanceOfUserSettingsNotificationsViewGroup = createInstanceChecker<UserSettingsNotificationsViewGroup>("isUserSettingsNotificationsViewGroup");
export default UserSettingsNotificationsViewGroup;
Loading
Loading