Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7f2e393
feat(ui5-user-settings-notifications-view): introduce NotificationsVi…
dobromiraboycheva Aug 3, 2026
1265eae
fix(ui5-user-settings-notifications-view): align spacing, navigation,…
dobromiraboycheva Aug 3, 2026
4cfda93
fix(ui5-user-settings-notifications-view): update @since to 2.26.0
dobromiraboycheva Aug 3, 2026
ab31695
fix(ui5-user-settings-notifications-view): address review findings
dobromiraboycheva Aug 3, 2026
ed33e95
fix(ui5-user-settings-notifications-view): remove unnecessary type as…
dobromiraboycheva Aug 3, 2026
d96205e
fix(ui5-user-settings-notifications-view): remove unused ListItemBase…
dobromiraboycheva Aug 3, 2026
b4e1d7e
fix(patterns/UXCIntegration): use className instead of class on nativ…
dobromiraboycheva Aug 3, 2026
bad85a8
feat(ui5-user-settings-notifications-view): implement ACC spec
dobromiraboycheva Aug 20, 2026
8ee5361
feat(ui5-user-settings-notifications-view): implement ACC spec
dobromiraboycheva Aug 20, 2026
63aa4ee
fix(ui5-user-settings-notifications-view): fix switch focus ring clip…
dobromiraboycheva Aug 20, 2026
61bda5e
fix(ui5-user-settings-notifications-view): align switches across navi…
dobromiraboycheva Aug 20, 2026
d8c4bd9
fix(ui5-user-settings-notifications-view): fix switch alignment in mi…
dobromiraboycheva Aug 20, 2026
7d7a5fd
fix(ui5-user-settings-notifications-view): clean up code and fix tests
dobromiraboycheva Aug 20, 2026
1e1927c
chore(ui5-user-settings-notifications-view): update @since to 2.27.0
dobromiraboycheva Aug 20, 2026
9a18aeb
Merge branch 'main' into user-settings-notifications
dobromiraboycheva Aug 20, 2026
ae7e161
chore(ui5-user-settings-notifications-view): sync samples and add web…
dobromiraboycheva Aug 20, 2026
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
1,052 changes: 1,052 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 @@ -312,6 +312,15 @@ class UserSettingsItem extends UI5Element {
focusable?.focus();
}

/**
* @private
* @since 2.27.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
209 changes: 209 additions & 0 deletions packages/fiori/src/UserSettingsNotificationsView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
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 i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.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";
import {
USER_SETTINGS_NOTIFICATIONS_LIST_LABEL,
USER_SETTINGS_NOTIFICATIONS_PREFERENCES_LIST_LABEL,
} from "./generated/i18n/i18n-defaults.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.27.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 header items rendered above the grouped items list.
* Each item is wrapped in a `role="form"` landmark — a separate Tab stop.
* Use this slot for product-level toggles (e.g. "Allow Notifications") that appear
* above the notification-type groups.
*
* @public
*/
@slot({
type: HTMLElement,
invalidateOnChildChange: true,
individualSlots: true,
})
headerItems!: Slot<UserSettingsNotificationsViewItem>;

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

@i18n("@ui5/webcomponents-fiori")
static i18nBundle: I18nBundle;

getAllItems(): Array<UserSettingsNotificationsViewItem> {
const allItems: Array<UserSettingsNotificationsViewItem> = [...this.headerItems];

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;
}

getItemByKey(itemKey: string): UserSettingsNotificationsViewItem | undefined {
return this.getAllItems().find(item => item.itemKey === itemKey);
}

get _hasHeaderItems(): boolean {
return this.headerItems.length > 0;
}

get _listAccessibleName(): string {
return this.secondary
? UserSettingsNotificationsView.i18nBundle.getText(USER_SETTINGS_NOTIFICATIONS_PREFERENCES_LIST_LABEL)
: UserSettingsNotificationsView.i18nBundle.getText(USER_SETTINGS_NOTIFICATIONS_LIST_LABEL);
}

_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?.focusFirstContentElement();
});
}
}

_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.27.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