diff --git a/packages/fiori/cypress/specs/Search.cy.tsx b/packages/fiori/cypress/specs/Search.cy.tsx index bd20b91d2dec..5aed43bf8551 100644 --- a/packages/fiori/cypress/specs/Search.cy.tsx +++ b/packages/fiori/cypress/specs/Search.cy.tsx @@ -2,6 +2,7 @@ import Title from "@ui5/webcomponents/dist/Title.js"; import Search from "../../src/Search.js"; import SearchItem from "../../src/SearchItem.js"; import SearchItemGroup from "../../src/SearchItemGroup.js"; +import SearchItemShowMore from "../../src/SearchItemShowMore.js"; import history from "@ui5/webcomponents-icons/dist/history.js"; import IllustratedMessage from "../../src/IllustratedMessage.js"; import searchIcon from "@ui5/webcomponents-icons/dist/search.js"; @@ -109,6 +110,28 @@ describe("Properties", () => { .should("not.exist"); }); + it("tests show more item", () => { + cy.mount( + + + + + ); + + cy.get("ui5-search") + .realClick() + .realType("s"); + + cy.get("ui5-search-item-show-more") + .should("be.visible"); + + cy.get("ui5-search-item-show-more") + .shadow() + .find("span") + .should("have.class", "ui5-search-item-show-more-text"); + + }) + it("tests loading property", () => { cy.mount( diff --git a/packages/fiori/src/Search.ts b/packages/fiori/src/Search.ts index 2c62d802f753..cf181a3bd9ce 100644 --- a/packages/fiori/src/Search.ts +++ b/packages/fiori/src/Search.ts @@ -128,7 +128,11 @@ class Search extends SearchField { * * @public */ - @slot({ type: HTMLElement, "default": true }) + @slot({ + type: HTMLElement, + "default": true, + invalidateOnChildChange: true, + }) items!: Array; /** @@ -329,17 +333,21 @@ class Search extends SearchField { } _startsWithMatchingItems(str: string): Array { - return StartsWith(str, this._flattenItems.filter(item => !this._isGroupItem(item)), "text"); + return StartsWith(str, this._flattenItems.filter(item => !this._isGroupItem(item) && !this._isShowMoreItem(item)), "text"); } _startsWithPerTermMatchingItems(str: string): Array { - return StartsWithPerTerm(str, this._flattenItems.filter(item => !this._isGroupItem(item)), "text"); + return StartsWithPerTerm(str, this._flattenItems.filter(item => !this._isGroupItem(item) && !this._isShowMoreItem(item)), "text"); } _isGroupItem(item: ISearchSuggestionItem) { return item.hasAttribute("ui5-search-item-group"); } + _isShowMoreItem(item: ISearchSuggestionItem) { + return item.hasAttribute("ui5-search-item-show-more"); + } + _deselectItems() { this._flattenItems.forEach(item => { item.selected = false; diff --git a/packages/fiori/src/SearchItemShowMore.ts b/packages/fiori/src/SearchItemShowMore.ts new file mode 100644 index 000000000000..3c0af848f54f --- /dev/null +++ b/packages/fiori/src/SearchItemShowMore.ts @@ -0,0 +1,48 @@ +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; +import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js"; +import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; +import SearchItemShowMoreTemplate from "./SearchItemShowMoreTemplate.js"; +import SearchItemCss from "./generated/themes/SearchItem.css.js"; +import SearchItemShowMoreCss from "./generated/themes/SearchItemShowMore.css.js"; + +/** + * @class + * ### Overview + * + *A `ui5-search-item-show-more` is a special type of ui5-list-item that acts as a button to progressively reveal additional (overflow) items within a group. + * + * ### ES6 Module Import + * + * `import "@ui5/webcomponents-fiori/dist/SearchItemShowMore.js";` + * + * @constructor + * @extends ListItemBase + * @public + * @since 2.13.0 + * @experimental + */ +@customElement({ + tag: "ui5-search-item-show-more", + renderer: jsxRenderer, + template: SearchItemShowMoreTemplate, + styles: [ + ListItemBase.styles, + SearchItemCss, + SearchItemShowMoreCss, + ], +}) + +class SearchItemShowMore extends ListItemBase { + /** + * Defines the heading text of the search item. + * @public + * @default undefined + */ + @property() + text?: string; +} + +SearchItemShowMore.define(); + +export default SearchItemShowMore; diff --git a/packages/fiori/src/SearchItemShowMoreTemplate.tsx b/packages/fiori/src/SearchItemShowMoreTemplate.tsx new file mode 100644 index 000000000000..3d996e8a3af2 --- /dev/null +++ b/packages/fiori/src/SearchItemShowMoreTemplate.tsx @@ -0,0 +1,15 @@ +import type SearchItemShowMore from "./SearchItemShowMore.js"; + +export default function SearchItemShowMoreTemplate(this: SearchItemShowMore) { + return ( +
  • + {this.text} +
  • + ); +} diff --git a/packages/fiori/src/bundle.esm.ts b/packages/fiori/src/bundle.esm.ts index 7a6bb7bbc743..7ab45ff077e3 100644 --- a/packages/fiori/src/bundle.esm.ts +++ b/packages/fiori/src/bundle.esm.ts @@ -36,6 +36,7 @@ import Search from "./Search.js"; import ShellBarSearch from "./ShellBarSearch.js"; import SearchMessageArea from "./SearchMessageArea.js"; import SearchItem from "./SearchItem.js"; +import SearchItemShowMore from "./SearchItemShowMore.js"; import SearchItemGroup from "./SearchItemGroup.js"; import ShellBarBranding from "./ShellBarBranding.js"; import ShellBarSpacer from "./ShellBarSpacer.js"; diff --git a/packages/fiori/src/themes/SearchItemShowMore.css b/packages/fiori/src/themes/SearchItemShowMore.css new file mode 100644 index 000000000000..92f976a14497 --- /dev/null +++ b/packages/fiori/src/themes/SearchItemShowMore.css @@ -0,0 +1,3 @@ +.ui5-search-item-show-more-text { + color: var(--sapLinkColor); +} \ No newline at end of file diff --git a/packages/fiori/test/pages/Search.html b/packages/fiori/test/pages/Search.html index 10fae96268fd..7445de6d759e 100644 --- a/packages/fiori/test/pages/Search.html +++ b/packages/fiori/test/pages/Search.html @@ -43,6 +43,86 @@ + Search with Grouped Suggestions and Show More (N) item + + + + + + + + + + + + +
    Search - Initially collapsed with Busy State
    @@ -174,7 +254,7 @@ filtering.addEventListener('ui5-input', (event) => { const value = event.target.value.toLowerCase(); const filteredData = data.filter(item => item.name.toLowerCase().includes(value)); - + // clear search items filtering.innerHTML = ''; @@ -216,7 +296,7 @@ createScopeItems(); searchScope.addEventListener('ui5-scope-change', (event) => { let scope = event.detail.scope.text === "All" ? "" : event.detail.scope.text.toLowerCase(); - + searchScope.getSlottedNodes("items").forEach(item => { item.remove(); }); @@ -240,7 +320,7 @@ }); }); } - + const filters = document.getElementById('filters'); filters.addEventListener('ui5-input', (event) => { const value = event.target.value.toLowerCase(); diff --git a/packages/website/docs/_components_pages/fiori/Search/Search.mdx b/packages/website/docs/_components_pages/fiori/Search/Search.mdx index 0a812f4983a4..e0b3a6f5de59 100644 --- a/packages/website/docs/_components_pages/fiori/Search/Search.mdx +++ b/packages/website/docs/_components_pages/fiori/Search/Search.mdx @@ -6,6 +6,7 @@ import Basic from "../../../_samples/fiori/Search/Basic/Basic.md"; import Advanced from "../../../_samples/fiori/Search/Advanced/Advanced.md"; import Byline from "../../../_samples/fiori/Search/Byline/Byline.md"; import AdvancedFilter from "../../../_samples/fiori/Search/AdvancedFilter/AdvancedFilter.md" +import ShowMore from "../../../_samples/fiori/Search/ShowMore/ShowMore.md" <%COMPONENT_OVERVIEW%> @@ -32,3 +33,9 @@ This example shows how to use a custom advanced filtering button via the `filter The example shows how to display byline items with an image and description. + +### Show More item +This example shows how to use a show more item. + + + diff --git a/packages/website/docs/_components_pages/fiori/Search/SearchItemShowMore.mdx b/packages/website/docs/_components_pages/fiori/Search/SearchItemShowMore.mdx new file mode 100644 index 000000000000..9bc8d2310465 --- /dev/null +++ b/packages/website/docs/_components_pages/fiori/Search/SearchItemShowMore.mdx @@ -0,0 +1,4 @@ + +<%COMPONENT_OVERVIEW%> + +<%COMPONENT_METADATA%> \ No newline at end of file diff --git a/packages/website/docs/_samples/fiori/Search/ShowMore/ShowMore.md b/packages/website/docs/_samples/fiori/Search/ShowMore/ShowMore.md new file mode 100644 index 000000000000..66ccdc236d97 --- /dev/null +++ b/packages/website/docs/_samples/fiori/Search/ShowMore/ShowMore.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + \ No newline at end of file diff --git a/packages/website/docs/_samples/fiori/Search/ShowMore/main.js b/packages/website/docs/_samples/fiori/Search/ShowMore/main.js new file mode 100644 index 000000000000..fe625a6a4a49 --- /dev/null +++ b/packages/website/docs/_samples/fiori/Search/ShowMore/main.js @@ -0,0 +1,72 @@ +import "@ui5/webcomponents-fiori/dist/Search.js"; +import "@ui5/webcomponents-icons/dist/globe.js"; +import "@ui5/webcomponents-icons/dist/history.js"; +import "@ui5/webcomponents-fiori/dist/SearchItem.js"; +import "@ui5/webcomponents-fiori/dist/SearchItemShowMore.js"; +import "@ui5/webcomponents-fiori/dist/SearchItemGroup.js"; + + const allItems = [ + { text: "List Item 1", icon: "history" }, + { text: "List Item 2", icon: "search" }, + { text: "List Item 3", icon: "history" }, + { text: "List Item 4", icon: "history" }, + { text: "List Item 5", icon: "search" }, + { text: "List Item 6", icon: "globe" } + ]; + + const group1 = document.getElementById("group1"); + const visibleCount = 3; + + function createSearchItem({ text, icon }) { + const el = document.createElement("ui5-search-item"); + el.setAttribute("text", text); + el.setAttribute("icon", icon); + el.setAttribute("deletable", true); + el.addEventListener("ui5-delete", () => el.remove()); + return el; + } + + function renderItemSlice(items, container, insertBefore = null) { + return items.map(item => { + const el = createSearchItem(item); + if (insertBefore) { + container.insertBefore(el, insertBefore); + } else { + container.appendChild(el); + } + return el; + }); + } + + function renderItems(items) { + const visibleItems = items.slice(0, visibleCount); + const hiddenItems = items.slice(visibleCount); + + renderItemSlice(visibleItems, group1); + + if (hiddenItems.length) { + const showMoreEl = document.createElement("ui5-search-item-show-more"); + showMoreEl.setAttribute("text", `Show more (${hiddenItems.length})`); + group1.appendChild(showMoreEl); + + function expandHiddenItems({ focusFirst = false } = {}) { + const newEls = renderItemSlice(hiddenItems, group1, showMoreEl); + showMoreEl.remove(); + if (focusFirst && newEls.length > 0) { + //wait the new items to show + setTimeout(() => { + newEls[0].focus() + }, 0); + } + } + + showMoreEl.addEventListener("click", () => expandHiddenItems()); + + showMoreEl.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + expandHiddenItems({ focusFirst: true }) + } + }); + } + } + renderItems(allItems); \ No newline at end of file diff --git a/packages/website/docs/_samples/fiori/Search/ShowMore/sample.html b/packages/website/docs/_samples/fiori/Search/ShowMore/sample.html new file mode 100644 index 000000000000..69bf83da98eb --- /dev/null +++ b/packages/website/docs/_samples/fiori/Search/ShowMore/sample.html @@ -0,0 +1,26 @@ + + + + + + + Document + + + + Search with Grouped Suggestions and Show More (N) item + + + + + + + + + + + + + + + \ No newline at end of file