Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
74 changes: 74 additions & 0 deletions packages/main/cypress/specs/List.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ResponsivePopover from "../../src/ResponsivePopover.js";
import Select from "../../src/Select.js";
import Option from "../../src/Option.js";
import CheckBox from "../../src/CheckBox.js";
import Bar from "../../src/Bar.js";

function getGrowingWithScrollList(length: number, height: string = "100px") {
return (
Expand Down Expand Up @@ -2516,4 +2517,77 @@ describe("List keyboard drag and drop tests", () => {

cy.get("#item9").prev().should("have.id", "item4");
});
});

describe("List sticky header", () => {
it("sticks default header", () => {
cy.mount(
<div id="scrollContainer" style="height: 100px;overflow: auto;">
<List headerText="Sticky Header" stickyHeader>
<ListItemStandard>Item 1</ListItemStandard>
<ListItemStandard>Item 2</ListItemStandard>
<ListItemStandard>Item 3</ListItemStandard>
</List>
<div id="bottomSpacer" style={{ height: "1000px" }}></div>
</div>
);

cy.get("#scrollContainer")
.as("container");

cy.get("[ui5-list]")
.shadow()
.find(".ui5-list-header")
.as("header");

cy.get("@header")
.then(($headerBefore) => {
const headerTopBefore = $headerBefore[0].getBoundingClientRect().top;

cy.get("@container")
.scrollTo(0, 50);

cy.get("@header")
.should(($headerAfter) => {
const headerTopAfter = $headerAfter[0].getBoundingClientRect().top;
expect(headerTopAfter).to.eq(headerTopBefore);
});
});
});

it("sticks custom header", () => {
cy.mount(
<div id="scrollContainer" style="height: 100px;overflow: auto;">
<List stickyHeader>
<Bar slot="header">
<Title>Sticky Header Bar</Title>
</Bar>
<ListItemStandard>Item 1</ListItemStandard>
<ListItemStandard>Item 2</ListItemStandard>
<ListItemStandard>Item 3</ListItemStandard>
</List>
<div id="bottomSpacer" style={{ height: "1000px" }}></div>
</div>
);

cy.get("#scrollContainer")
.as("container");

cy.get("[ui5-bar]")
.as("header");

cy.get("@header")
.then(($headerBefore) => {
const headerTopBefore = $headerBefore[0].getBoundingClientRect().top;

cy.get("@container")
.scrollTo(0, 50);

cy.get("@header")
.should(($headerAfter) => {
const headerTopAfter = $headerAfter[0].getBoundingClientRect().top;
expect(headerTopAfter).to.eq(headerTopBefore);
});
});
});
});
11 changes: 11 additions & 0 deletions packages/main/src/List.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,17 @@ class List extends UI5Element {
@property({ type: Number })
loadingDelay = 1000;

/**
* Indicates whether the List header is sticky or not.
* If stickyHeader is set to true, then whenever you scroll the content or
* the application, the header of the list will be always visible.
* @default false
* @public
* @since 2.18.0
*/
@property({ type: Boolean })
stickyHeader = false;

/**
* Defines the accessible name of the component.
* @default undefined
Expand Down
63 changes: 32 additions & 31 deletions packages/main/src/ListTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ export default function ListTemplate(this: List) {
active={this.showBusyIndicatorOverlay}
class="ui5-list-busy-indicator"
>

<div class="ui5-list-scroll-container">
<span tabindex={-1} aria-hidden="true" class="ui5-list-start-marker"></span>
<div class="ui5-list-container">

{this.header.length > 0 && <slot name="header" />}

Expand All @@ -41,41 +39,44 @@ export default function ListTemplate(this: List) {
</header>
}

{this.hasData &&
<div id={`${this._id}-before`} tabindex={0} role="none" class="ui5-list-focusarea"></div>
}
<div class="ui5-list-scroll-container">
<span tabindex={-1} aria-hidden="true" class="ui5-list-start-marker"></span>

<span id={`${this._id}-modeLabel`} class="ui5-hidden-text">{this.ariaLabelModeText}</span>
{this.hasData &&
<div id={`${this._id}-before`} tabindex={0} role="none" class="ui5-list-focusarea"></div>
}

<ul id={`${this._id}-listUl`}
class="ui5-list-ul"
role={this.listAccessibleRole}
aria-label={this.ariaLabelTxt}
aria-labelledby={this.ariaLabelledBy}
aria-description={this.ariaDescriptionText}
>
<slot></slot>
<span id={`${this._id}-modeLabel`} class="ui5-hidden-text">{this.ariaLabelModeText}</span>

{this.showNoDataText &&
<li tabindex={0} id={`${this._id}-nodata`} class="ui5-list-nodata" role="listitem">
<div id={`${this._id}-nodata-text`} class="ui5-list-nodata-text">
{this.noDataText}
</div>
</li>
}
</ul>
<ul id={`${this._id}-listUl`}
class="ui5-list-ul"
role={this.listAccessibleRole}
aria-label={this.ariaLabelTxt}
aria-labelledby={this.ariaLabelledBy}
aria-description={this.ariaDescriptionText}
>
<slot></slot>

{ this.growsWithButton && moreRow.call(this) }
{this.showNoDataText &&
<li tabindex={0} id={`${this._id}-nodata`} class="ui5-list-nodata" role="listitem">
<div id={`${this._id}-nodata-text`} class="ui5-list-nodata-text">
{this.noDataText}
</div>
</li>
}
</ul>

{this.footerText &&
<footer id={`${this._id}-footer`} class="ui5-list-footer">{this.footerText}</footer>
}
{ this.growsWithButton && moreRow.call(this) }

{this.hasData &&
<div id={`${this._id}-after`} tabindex={0} role="none" class="ui5-list-focusarea"></div>
}
{this.footerText &&
<footer id={`${this._id}-footer`} class="ui5-list-footer">{this.footerText}</footer>
}

<span tabindex={-1} aria-hidden="true" class="ui5-list-end-marker"></span>
{this.hasData &&
<div id={`${this._id}-after`} tabindex={0} role="none" class="ui5-list-focusarea"></div>
}
<span tabindex={-1} aria-hidden="true" class="ui5-list-end-marker"></span>
</div>
</div>
<DropIndicator orientation="Horizontal" ownerReference={this}/>
</BusyIndicator>
Expand Down
10 changes: 9 additions & 1 deletion packages/main/src/themes/List.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
}

.ui5-list-root,
.ui5-list-busy-indicator {
.ui5-list-busy-indicator,
.ui5-list-container {
width: 100%;
height: 100%;
position: relative;
Expand Down Expand Up @@ -111,4 +112,11 @@
:host([growing="Scroll"]) .ui5-list-end-marker {
/* Ensure the list-end-marker has a block property to always be stretched and "visible" on the screen */
display: inline-block;
}

:host([sticky-header]) ::slotted([slot="header"]),
:host([sticky-header]) .ui5-list-header {
position: sticky;
top: 0;
z-index: 100;
}
42 changes: 42 additions & 0 deletions packages/main/test/pages/List.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,46 @@
</ui5-list>

<br/><br/>
<h2>List with sticky header</h2>
<ui5-list class="list6auto" sticky-header>
<ui5-bar slot="header">
<ui5-title>Sticky Header Bar</ui5-title>
<ui5-button slot="endContent" icon="action">Action</ui5-button>
</ui5-bar>
<ui5-li >Item 1</ui5-li>
<ui5-li >Item 2</ui5-li>
<ui5-li >Item 3</ui5-li>
<ui5-li >Item 4</ui5-li>
<ui5-li >Item 5</ui5-li>
<ui5-li >Item 6</ui5-li>
<ui5-li >Item 7</ui5-li>
<ui5-li >Item 8</ui5-li>
<ui5-li >Item 9</ui5-li>
<ui5-li >Item 10</ui5-li>
<ui5-li >Item 11</ui5-li>
<ui5-li >Item 12</ui5-li>
<ui5-li >Item 13</ui5-li>
<ui5-li >Item 14</ui5-li>
<ui5-li >Item 15</ui5-li>
</ui5-list>

<ui5-list class="list6auto" header-text="Sticky Header" sticky-header>
<ui5-li >Item 1</ui5-li>
<ui5-li >Item 2</ui5-li>
<ui5-li >Item 3</ui5-li>
<ui5-li >Item 4</ui5-li>
<ui5-li >Item 5</ui5-li>
<ui5-li >Item 6</ui5-li>
<ui5-li >Item 7</ui5-li>
<ui5-li >Item 8</ui5-li>
<ui5-li >Item 9</ui5-li>
<ui5-li >Item 10</ui5-li>
<ui5-li >Item 11</ui5-li>
<ui5-li >Item 12</ui5-li>
<ui5-li >Item 13</ui5-li>
<ui5-li >Item 14</ui5-li>
<ui5-li >Item 15</ui5-li>
</ui5-list>

<h2>List growing="Button" and growing-button-text property used</h2>
<ui5-list id="infiniteScrollEx2" growing="Button" growing-button-text="Custom growing-button-text" class="list3auto">
Expand Down Expand Up @@ -688,6 +728,8 @@ <h2>With accessible description</h2>
</ui5-li-custom>
</ui5-list>



<script>
'use strict';

Expand Down
4 changes: 4 additions & 0 deletions packages/main/test/pages/styles/List.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
justify-content: space-between
}

.list6auto {
height: 500px;
}

.largeTopMargin {
margin-top: 2rem;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/website/docs/_components_pages/main/List/List.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import SeparationTypes from "../../../_samples/main/List/SeparationTypes/Separat
import DragAndDrop from "../../../_samples/main/List/DragAndDrop/DragAndDrop.md";
import MultipleDrag from "../../../_samples/main/List/MultipleDrag/MultipleDrag.md";
import WrappingBehavior from "../../../_samples/main/List/WrappingBehavior/WrappingBehavior.md";
import StickyHeader from "../../../_samples/main/List/StickyHeader/StickyHeader.md";

<%COMPONENT_OVERVIEW%>

Expand Down Expand Up @@ -75,3 +76,8 @@ This feature improves readability when displaying lengthy content in lists.
The `<ui5-li-custom>` is intentionally designed as a generic container to provide maximum flexibility. It can be used alongside `ui5-expandable-text` for long text content.

<WrappingBehavior />

### Sticky Header
Use the <b>stickyHeader</b> property to keep the header visible during scrolling.

<StickyHeader/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

<Editor html={html} js={js} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.scrollContainer {
height: 300px;
overflow: auto;
}

.heading {
margin: 2rem 0 2rem 1rem;
}
4 changes: 4 additions & 0 deletions packages/website/docs/_samples/main/List/StickyHeader/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import "@ui5/webcomponents/dist/List.js";
import "@ui5/webcomponents/dist/ListItemStandard.js";

import "@ui5/webcomponents-icons/dist/nutrition-activity.js";
46 changes: 46 additions & 0 deletions packages/website/docs/_samples/main/List/StickyHeader/sample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sticky Header Sample</title>
<link rel="stylesheet" href="./main.css">
</head>

<body style="background-color: var(--sapBackgroundColor)">
<!-- playground-fold-end -->

<div class="scrollContainer">
<ui5-title size="H3" class="heading">Scroll down to see the sticky header in action</ui5-title>
<ui5-list header-text="Sticky Header" sticky-header>
<ui5-li icon="nutrition-activity" description="Tropical plant with an edible fruit" additional-text="In-stock"
additional-text-state="Positive">Pineapple</ui5-li>
<ui5-li icon="nutrition-activity" description="Occurs between red and yellow" additional-text="Expires"
additional-text-state="Critical">Orange</ui5-li>
<ui5-li icon="nutrition-activity" description="The yellow lengthy fruit" additional-text="Re-stock"
additional-text-state="Information">Blueberry</ui5-li>
<ui5-li icon="nutrition-activity" description="The tropical stone fruit" additional-text="Re-stock"
additional-text-state="Negative">Mango</ui5-li>
<ui5-li icon="nutrition-activity" description="A sweet red or green pomaceous fruit" additional-text="In-stock"
additional-text-state="Positive">Apple</ui5-li>
<ui5-li icon="nutrition-activity" description="A long curved fruit with soft sweet flesh" additional-text="Expires"
additional-text-state="Critical">Banana</ui5-li>
<ui5-li icon="nutrition-activity" description="A small red fruit with seeds on the outside" additional-text="In-stock"
additional-text-state="Positive">Strawberry</ui5-li>
<ui5-li icon="nutrition-activity" description="A small juicy fruit growing in clusters" additional-text="Re-stock"
additional-text-state="Information">Grape</ui5-li>
<ui5-li icon="nutrition-activity" description="A tropical fruit with orange flesh and black seeds" additional-text="Re-stock"
additional-text-state="Negative">Papaya</ui5-li>
<ui5-li icon="nutrition-activity" description="A small brown fruit with bright green flesh" additional-text="In-stock"
additional-text-state="Positive">Kiwi</ui5-li>
</ui5-list>
</div>

<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->
Loading