Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a3bbb82
feat(ui5-user-settings-notifications-view): introduce NotificationsVi…
dobromiraboycheva Aug 3, 2026
03205ac
fix(ui5-user-settings-notifications-view): align spacing, navigation,…
dobromiraboycheva Aug 3, 2026
e6f8876
fix(ui5-user-settings-notifications-view): update @since to 2.26.0
dobromiraboycheva Aug 3, 2026
8d09aa1
fix(ui5-user-settings-notifications-view): address review findings
dobromiraboycheva Aug 3, 2026
d9f9877
fix(ui5-user-settings-notifications-view): remove unnecessary type as…
dobromiraboycheva Aug 3, 2026
17cfbdd
fix(ui5-user-settings-notifications-view): remove unused ListItemBase…
dobromiraboycheva Aug 3, 2026
9796256
fix(patterns/UXCIntegration): use className instead of class on nativ…
dobromiraboycheva Aug 3, 2026
6059c92
feat(ui5-user-settings-notifications-view): implement ACC spec
dobromiraboycheva Aug 20, 2026
9560a4e
feat(ui5-user-settings-notifications-view): implement ACC spec
dobromiraboycheva Aug 20, 2026
eb07839
fix(ui5-user-settings-notifications-view): fix switch focus ring clip…
dobromiraboycheva Aug 20, 2026
3e97631
fix(ui5-user-settings-notifications-view): align switches across navi…
dobromiraboycheva Aug 20, 2026
695ae54
fix(ui5-user-settings-notifications-view): fix switch alignment in mi…
dobromiraboycheva Aug 20, 2026
a8f5a07
fix(ui5-user-settings-notifications-view): clean up code and fix tests
dobromiraboycheva Aug 20, 2026
bd7fdb2
chore(ui5-user-settings-notifications-view): update @since to 2.27.0
dobromiraboycheva Aug 20, 2026
56240fd
chore(ui5-user-settings-notifications-view): sync samples and add web…
dobromiraboycheva Aug 20, 2026
de96fb0
fix(ui5-user-settings-notifications-view): ACC spec form rendering, f…
dobromiraboycheva Sep 2, 2026
3ab5bff
fix(ui5-user-settings-notifications-view): remove inline comment from…
dobromiraboycheva Sep 2, 2026
afb9bf2
refactor(ui5-user-settings-notifications-view): simplify and deduplic…
dobromiraboycheva Sep 2, 2026
2446bff
test(ui5-user-settings-notifications-view): update test for header it…
dobromiraboycheva Sep 3, 2026
7ad8df3
fix(ui5-user-settings-notifications-view): add slim-arrow-right icon …
dobromiraboycheva Sep 3, 2026
9f9807f
refactor(ui5-user-settings-notifications-view): use explicit param in…
dobromiraboycheva Sep 3, 2026
d5d678b
fix(ui5-user-settings-notifications-view): remove Default Landing Pag…
dobromiraboycheva Sep 3, 2026
966f59e
Merge branch 'main' into user-settings-notifications
dobromiraboycheva Sep 3, 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,032 changes: 1,032 additions & 0 deletions packages/fiori/cypress/specs/UserSettingsNotificationsView.cy.tsx

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions packages/fiori/src/UserSettingsItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,18 @@ class UserSettingsItem extends UI5Element {

if (!eventPrevented) {
selectedPageView.selected = false;
const primaryView = this.pages?.find(view => !view.secondary && view !== selectedPageView);
renderFinished().then(() => {
if (!this.isConnected) {
return;
}
const lastItem = (primaryView as any)?._lastNavigatedItem;
if (lastItem?.getFocusDomRef) {
lastItem.getFocusDomRef()?.focus();
} else {
this._focusFirstContentElement();
}
});
}
} else {
this.fireDecoratorEvent("_collapse");
Expand Down Expand Up @@ -312,6 +324,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
214 changes: 214 additions & 0 deletions packages/fiori/src/UserSettingsNotificationsView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
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 { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.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;
}

_lastNavigatedItem: UserSettingsNotificationsViewItem | null = null;

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;
this._lastNavigatedItem = item;
target.selected = true;

renderFinished().then(async () => {
if (!this.isConnected) {
return;
}
await target._waitForDomRef();
const focusable = await getFirstFocusableElement(target.getDomRef()!);
focusable?.focus();
});
}

_processItemClick(item: UserSettingsNotificationsViewItem) {
if (!isInstanceOfUserSettingsNotificationsViewItem(item) || !item.navigable) {
return;
}
if (!this.fireDecoratorEvent("item-click", { item })) {
return;
}
this._navigateToSecondaryView(item);
}

_handleItemClick = (e: CustomEvent<ListItemClickEventDetail>) => {
this._processItemClick(e.detail.item as UserSettingsNotificationsViewItem);
};

_handleFormItemClick = (e: CustomEvent<{ item: UserSettingsNotificationsViewItem }>) => {
this._processItemClick(e.detail.item);
};
}

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