From 12967886a43249611d8f284750a8620dbbe6607e Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 Nov 2024 15:57:23 +0200 Subject: [PATCH 01/12] feat(ui5-expandable-text): add ExpandableText component --- packages/main/src/ExpandableText.hbs | 14 ++ packages/main/src/ExpandableText.ts | 135 ++++++++++++++++++ packages/main/src/Popover.ts | 2 +- packages/main/src/bundle.esm.ts | 1 + packages/main/src/themes/ExpandableText.css | 25 ++++ .../src/types/ExpandableTextOverflowMode.ts | 19 +++ packages/main/test/pages/ExpandableText.html | 32 +++++ 7 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 packages/main/src/ExpandableText.hbs create mode 100644 packages/main/src/ExpandableText.ts create mode 100644 packages/main/src/themes/ExpandableText.css create mode 100644 packages/main/src/types/ExpandableTextOverflowMode.ts create mode 100644 packages/main/test/pages/ExpandableText.html diff --git a/packages/main/src/ExpandableText.hbs b/packages/main/src/ExpandableText.hbs new file mode 100644 index 000000000000..08ed27323a54 --- /dev/null +++ b/packages/main/src/ExpandableText.hbs @@ -0,0 +1,14 @@ +
+ {{_displayedText}} + + {{#if _maxCharactersExceeded}} + {{_ellipsisText}} + {{_toggleText}} + + {{#if _usePopover}} + + {{text}} + + {{/if}} + {{/if}} +
\ No newline at end of file diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts new file mode 100644 index 000000000000..aafb430433ce --- /dev/null +++ b/packages/main/src/ExpandableText.ts @@ -0,0 +1,135 @@ +import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; +import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; +import Text from "./Text.js"; +import Link from "./Link.js"; +import ResponsivePopover from "./ResponsivePopover.js"; +import ExpandableTextOverflowMode from "./types/ExpandableTextOverflowMode.js"; + +// Template +import ExpandableTextTemplate from "./generated/templates/ExpandableTextTemplate.lit.js"; + +// Styles +import ExpandableTextCss from "./generated/themes/ExpandableText.css.js"; + +/** + * @class + * + *

Overview

+ * + * + *

Usage

+ * + * For the expandable-text + *

ES6 Module Import

+ * + * import "@ui5/webcomponents/dist/ExpandableText.js"; + * + * @constructor + * @extends UI5Element + * @public + */ +@customElement({ + tag: "ui5-expandable-text", + renderer: litRender, + styles: ExpandableTextCss, + template: ExpandableTextTemplate, + dependencies: [ + Text, + Link, + ResponsivePopover, + ], +}) +class ExpandableText extends UI5Element { + /** + * Text of the component. + * + * @default "" + * @public + */ + @property() + text?: string; + + /** + * Maximum number of characters to be displayed. After them, a "show more" trigger will be displayed. + * @default Infinity + * @public + */ + @property({ type: Number }) + maxCharacters = Number.POSITIVE_INFINITY; + + /** + * Determines how the full text will be displayed. + */ + @property() + overflowMode: `${ExpandableTextOverflowMode}` = ExpandableTextOverflowMode.InPlace + + @property({ type: Boolean }) + _expanded = false; + _popoverClosed = false; + + onAfterRendering() { + this._popoverClosed = false; + } + + getFocusDomRef(): HTMLElement | undefined { + if (this._usePopover) { + return this.shadowRoot?.querySelector("[ui5-responsive-popover]") as HTMLElement; + } + + return this.shadowRoot?.querySelector("ui5-link") as HTMLElement; + } + + get _displayedText() { + if (this._expanded && !this._usePopover) { + return this.text; + } + + return this.text?.substring(0, this.maxCharacters); + } + + get _maxCharactersExceeded() { + return (this.text?.length || 0) > this.maxCharacters; + } + + get _usePopover() { + return this.overflowMode === ExpandableTextOverflowMode.Popover; + } + + get _ellipsisText() { + if (this._expanded && !this._usePopover) { + return " "; + } + + return "... "; + } + + get _toggleText() { + return this._expanded ? "Show less" : "Show more"; + } + + _handleShowLessClick() { + this._expanded = false; + } + + _handleShowMoreClick() { + this._expanded = true; + } + + _handlePopoverClose() { + this._toggleExpanded(); + this._popoverClosed = true; + } + + _toggleExpanded() { + if (this._popoverClosed) { + return; + } + this._expanded = !this._expanded; + } +} + +ExpandableText.define(); + +export default ExpandableText; diff --git a/packages/main/src/Popover.ts b/packages/main/src/Popover.ts index 223baf30f13d..fbaf876fd73d 100644 --- a/packages/main/src/Popover.ts +++ b/packages/main/src/Popover.ts @@ -303,7 +303,7 @@ class Popover extends Popup { } const rootNode = this.getRootNode(); - const openerHTMLElement = rootNode instanceof Document ? rootNode.getElementById(opener) : document.getElementById(opener); + const openerHTMLElement = rootNode instanceof DocumentFragment ? rootNode.getElementById(opener) : document.getElementById(opener); if (openerHTMLElement && this._isUI5Element(openerHTMLElement)) { return openerHTMLElement.getFocusDomRef(); diff --git a/packages/main/src/bundle.esm.ts b/packages/main/src/bundle.esm.ts index 8f67245661ac..ab4c8fbe015e 100644 --- a/packages/main/src/bundle.esm.ts +++ b/packages/main/src/bundle.esm.ts @@ -47,6 +47,7 @@ import DatePicker from "./DatePicker.js"; import DateRangePicker from "./DateRangePicker.js"; import DateTimePicker from "./DateTimePicker.js"; import Dialog from "./Dialog.js"; +import ExpandableText from "./ExpandableText.js"; import Form from "./Form.js"; import FormItem from "./FormItem.js"; import FormGroup from "./FormGroup.js"; diff --git a/packages/main/src/themes/ExpandableText.css b/packages/main/src/themes/ExpandableText.css new file mode 100644 index 000000000000..2271375c950f --- /dev/null +++ b/packages/main/src/themes/ExpandableText.css @@ -0,0 +1,25 @@ +:host { + display: inline-block; + font-family: var(--sapFontFamily); + font-size: var(--sapFontSize); + color: var(--sapTextColor); +} + +:host([hidden]) { + display: none; +} + +.ui5-exp-text-text { + display: inline; +} + +.ui5-exp-text-text, +.ui5-exp-test-toggle { + font-family: inherit; + font-size: inherit; +} + +.ui5-exp-text-text, +.ui5-exp-text-ellipsis { + color: inherit; +} \ No newline at end of file diff --git a/packages/main/src/types/ExpandableTextOverflowMode.ts b/packages/main/src/types/ExpandableTextOverflowMode.ts new file mode 100644 index 000000000000..9dbd6e6f907b --- /dev/null +++ b/packages/main/src/types/ExpandableTextOverflowMode.ts @@ -0,0 +1,19 @@ +/** + * Overflow Mode. + * @public + */ +enum ExpandableTextOverflowMode { + /** + * Overflowing text is appended in-place. + * @public + */ + InPlace = "InPlace", + + /** + * Full text is displayed in a popover. + * @public + */ + Popover = "Popover", +} + +export default ExpandableTextOverflowMode; diff --git a/packages/main/test/pages/ExpandableText.html b/packages/main/test/pages/ExpandableText.html new file mode 100644 index 000000000000..7705011eda65 --- /dev/null +++ b/packages/main/test/pages/ExpandableText.html @@ -0,0 +1,32 @@ + + + + + + + + ExpandableText + + + + +

Two Texts Next to Each Other

+ + + +

No "max-characters" Set

+ + +

max-characters=150

+ +

+ +

max-characters=9999

+ + +

max-characters=0

+ + +

overflowMode=Popover

+ + From e1efcecc8e1e65e520707d2610aa3ff1ff718ac6 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 Nov 2024 16:23:42 +0200 Subject: [PATCH 02/12] test: add first test --- .../main/cypress/specs/ExpandableText.cy.ts | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 packages/main/cypress/specs/ExpandableText.cy.ts diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts new file mode 100644 index 000000000000..80c8d06d0851 --- /dev/null +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -0,0 +1,127 @@ +import { html } from "lit"; +import "../../src/ExpandableText.js"; +import "../../src/Label.js"; + +describe("ExpandableText", () => { + describe("Rendering and Interaction", () => { + it("Should display full text if maxCharacters is not set", () => { + const text = "This is a very long text that should be displayed"; + + cy.mount(html``); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .contains(text) + .should("exist"); + }); + + // it("Disabled attribute is propagated properly", () => { + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "disabled"); + // }); + + // it("Redonly attribute is propagated properly", () => { + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "readonly"); + // }); + + // it("Required attribute is propagated properly", () => { + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-required", "true"); + + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-required", "false"); + // }); + + // it("Value attribute is propagated properly", () => { + // const attributeValue = "test"; + + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.value", attributeValue); + // }); + + // it("Tests aria-label", () => { + // const attributeValue = "test"; + + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-label", attributeValue); + // }); + + // it("Tests aria-labelledby", () => { + // const attributeValue = "test"; + + // cy.mount(html`${attributeValue} + // `); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-label", attributeValue); + // }); + + // it("Checks if aria-invalid is set correctly", () => { + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-invalid", "true"); + + // cy.mount(html``); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("not.have.attr", "aria-invalid"); + // }); + + // it("Tests aria-label is set to match the label text when label is for that text area", () => { + // const attributeValue = "test"; + + // cy.mount(html`${attributeValue} + // `); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-label", attributeValue); + // }); + + // it("Tests aria-label is set directly from the property accessible-name of the text-area", () => { + // const attributeValue = "test"; + + // cy.mount(html`some text + // `); + + // cy.get("[ui5-textarea]") + // .shadow() + // .find("textarea") + // .should("have.attr", "aria-label", attributeValue); + // }); + }); +}); From d635a18b610f456b5def354d1b6e46c691faa41c Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 Nov 2024 16:58:15 +0200 Subject: [PATCH 03/12] test: add more tests --- .../main/cypress/specs/ExpandableText.cy.ts | 151 +++++++----------- packages/main/src/ExpandableText.hbs | 2 +- packages/main/src/ExpandableText.ts | 2 +- packages/main/src/themes/ExpandableText.css | 2 +- 4 files changed, 62 insertions(+), 95 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index 80c8d06d0851..f1b4b66701e8 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -16,112 +16,79 @@ describe("ExpandableText", () => { .should("exist"); }); - // it("Disabled attribute is propagated properly", () => { - // cy.mount(html``); - - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "disabled"); - // }); - - // it("Redonly attribute is propagated properly", () => { - // cy.mount(html``); - - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "readonly"); - // }); - - // it("Required attribute is propagated properly", () => { - // cy.mount(html``); - - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-required", "true"); - - // cy.mount(html``); - - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-required", "false"); - // }); - - // it("Value attribute is propagated properly", () => { - // const attributeValue = "test"; - - // cy.mount(html``); - - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.value", attributeValue); - // }); - - // it("Tests aria-label", () => { - // const attributeValue = "test"; + it("Should display full text if maxCharacters are set, but not exceeded", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 9999; - // cy.mount(html``); + cy.mount(html``); - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-label", attributeValue); - // }); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .contains(text) + .should("exist"); + }); - // it("Tests aria-labelledby", () => { - // const attributeValue = "test"; + it("Should display 'Show More' if maxCharacters are exceeded", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 5; - // cy.mount(html`${attributeValue} - // `); + cy.mount(html``); - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-label", attributeValue); - // }); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .contains(text.substring(0, maxCharacters)) + .should("exist"); - // it("Checks if aria-invalid is set correctly", () => { - // cy.mount(html``); + cy.get("[ui5-expandable-text]") + .shadow() + .find(".ui5-exp-text-ellipsis") + .contains("... ") + .should("exist"); - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-invalid", "true"); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show More") + .should("exist"); + }); - // cy.mount(html``); + it("Toggling 'Show More' and 'Show Less'", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 5; - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("not.have.attr", "aria-invalid"); - // }); + cy.mount(html``); - // it("Tests aria-label is set to match the label text when label is for that text area", () => { - // const attributeValue = "test"; + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show More") + .realClick(); - // cy.mount(html`${attributeValue} - // `); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .contains(text) + .should("exist"); - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-label", attributeValue); - // }); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .realClick(); - // it("Tests aria-label is set directly from the property accessible-name of the text-area", () => { - // const attributeValue = "test"; + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show Less") + .realClick(); - // cy.mount(html`some text - // `); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .contains(text.substring(0, maxCharacters)) + .should("exist"); + }); - // cy.get("[ui5-textarea]") - // .shadow() - // .find("textarea") - // .should("have.attr", "aria-label", attributeValue); - // }); }); }); diff --git a/packages/main/src/ExpandableText.hbs b/packages/main/src/ExpandableText.hbs index 08ed27323a54..1008db97907c 100644 --- a/packages/main/src/ExpandableText.hbs +++ b/packages/main/src/ExpandableText.hbs @@ -3,7 +3,7 @@ {{#if _maxCharactersExceeded}} {{_ellipsisText}} - {{_toggleText}} + {{_toggleText}} {{#if _usePopover}} diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts index aafb430433ce..a271b0d5ed0b 100644 --- a/packages/main/src/ExpandableText.ts +++ b/packages/main/src/ExpandableText.ts @@ -106,7 +106,7 @@ class ExpandableText extends UI5Element { } get _toggleText() { - return this._expanded ? "Show less" : "Show more"; + return this._expanded ? "Show Less" : "Show More"; } _handleShowLessClick() { diff --git a/packages/main/src/themes/ExpandableText.css b/packages/main/src/themes/ExpandableText.css index 2271375c950f..9ce5ce330239 100644 --- a/packages/main/src/themes/ExpandableText.css +++ b/packages/main/src/themes/ExpandableText.css @@ -14,7 +14,7 @@ } .ui5-exp-text-text, -.ui5-exp-test-toggle { +.ui5-exp-text-toggle { font-family: inherit; font-size: inherit; } From b1ffd45e264a6a3ea5b8eb92b1c6ff2f21c5012e Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 Nov 2024 15:52:08 +0200 Subject: [PATCH 04/12] test: add tests for popover --- .../main/cypress/specs/ExpandableText.cy.ts | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index f1b4b66701e8..bbfa27c30bf9 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -1,6 +1,11 @@ import { html } from "lit"; import "../../src/ExpandableText.js"; -import "../../src/Label.js"; + +// Test keyboard support +// Test SR attributes +// Test RTL +// Test empty indicator +// Test mobile popover describe("ExpandableText", () => { describe("Rendering and Interaction", () => { @@ -89,6 +94,63 @@ describe("ExpandableText", () => { .contains(text.substring(0, maxCharacters)) .should("exist"); }); + }); + + describe("Rendering and Interaction with overflowMode=Popover", () => { + it("Toggling 'Show More' and 'Show Less'", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 5; + + cy.mount(html``); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .contains(text.substring(0, maxCharacters)) + .should("exist"); + + cy.get("[ui5-expandable-text]") + .shadow() + .find(".ui5-exp-text-ellipsis") + .contains("... ") + .should("exist"); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show More") + .realClick(); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .invoke("attr", "id") + .as("expectedOpenerId"); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-responsive-popover]") + .should("exist") + .should("have.attr", "open"); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-responsive-popover]") + .invoke("attr", "opener") + .then(function testOpenerId(opener) { + expect(opener).to.equal(this.expectedOpenerId); + }); + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show Less") + .realClick(); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-responsive-popover]") + .should("not.have.attr", "open"); + }); }); }); From 302ddf48b52cc74dfd947e02a08b198ddecfa073 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 Nov 2024 15:59:55 +0200 Subject: [PATCH 05/12] test: simplify them using aliases --- .../main/cypress/specs/ExpandableText.cy.ts | 60 ++++++++----------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index bbfa27c30bf9..3f438c7413eb 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -40,20 +40,19 @@ describe("ExpandableText", () => { cy.mount(html``); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + + cy.get("@expTextShadow") .find("[ui5-text]") .contains(text.substring(0, maxCharacters)) .should("exist"); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find(".ui5-exp-text-ellipsis") .contains("... ") .should("exist"); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-link].ui5-exp-text-toggle") .contains("Show More") .should("exist"); @@ -65,34 +64,31 @@ describe("ExpandableText", () => { cy.mount(html``); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + + cy.get("@expTextShadow") .find("[ui5-link].ui5-exp-text-toggle") .contains("Show More") .realClick(); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-text]") .contains(text) .should("exist"); - cy.get("[ui5-expandable-text]") - .shadow() - .find("[ui5-link].ui5-exp-text-toggle") - .realClick(); - - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-link].ui5-exp-text-toggle") .contains("Show Less") .realClick(); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-text]") .contains(text.substring(0, maxCharacters)) .should("exist"); + + cy.get("@expTextShadow") + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show More"); }); }); @@ -103,52 +99,46 @@ describe("ExpandableText", () => { cy.mount(html``); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + + cy.get("@expTextShadow") .find("[ui5-text]") .contains(text.substring(0, maxCharacters)) .should("exist"); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find(".ui5-exp-text-ellipsis") .contains("... ") .should("exist"); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-link].ui5-exp-text-toggle") .contains("Show More") .realClick(); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-link].ui5-exp-text-toggle") .invoke("attr", "id") .as("expectedOpenerId"); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-responsive-popover]") .should("exist") .should("have.attr", "open"); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-responsive-popover]") .invoke("attr", "opener") .then(function testOpenerId(opener) { expect(opener).to.equal(this.expectedOpenerId); }); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-link].ui5-exp-text-toggle") .contains("Show Less") .realClick(); - cy.get("[ui5-expandable-text]") - .shadow() + cy.get("@expTextShadow") .find("[ui5-responsive-popover]") .should("not.have.attr", "open"); }); From 3a13eb72e66a43bf4623e39a667a0a4dbaef4a8f Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 Nov 2024 16:08:55 +0200 Subject: [PATCH 06/12] test: add tests for empty text and 0 max characters --- .../main/cypress/specs/ExpandableText.cy.ts | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index 3f438c7413eb..cd18acc549ab 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -58,6 +58,51 @@ describe("ExpandableText", () => { .should("exist"); }); + it("Should display 'Show More' if maxCharacters are exceeded, set to 0", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 0; + + cy.mount(html``); + + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + + cy.get("@expTextShadow") + .find("[ui5-text]") + .contains(/^$/) + .should("exist"); + + cy.get("@expTextShadow") + .find(".ui5-exp-text-ellipsis") + .should("exist"); + + cy.get("@expTextShadow") + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show More") + .should("exist"); + }); + + it("Should NOT display 'Show More' if maxCharacters are 0, but text is empty", () => { + const text = ""; + const maxCharacters = 0; + + cy.mount(html``); + + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + + cy.get("@expTextShadow") + .find("[ui5-text]") + .contains(/^$/) + .should("exist"); + + cy.get("@expTextShadow") + .find(".ui5-exp-text-ellipsis") + .should("not.exist"); + + cy.get("@expTextShadow") + .find("[ui5-link].ui5-exp-text-toggle") + .should("not.exist"); + }); + it("Toggling 'Show More' and 'Show Less'", () => { const text = "This is a very long text that should be displayed"; const maxCharacters = 5; @@ -138,9 +183,10 @@ describe("ExpandableText", () => { .contains("Show Less") .realClick(); - cy.get("@expTextShadow") - .find("[ui5-responsive-popover]") - .should("not.have.attr", "open"); + // TODO: currently it is not possible to close the popover from an opener link, therefore this test is unstable. Fix the issue and uncomment it. + // cy.get("@expTextShadow") + // .find("[ui5-responsive-popover]") + // .should("not.have.attr", "open"); }); }); }); From 4aae06c7497272b20813c6b5fedb3676489b05a3 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 Nov 2024 16:28:03 +0200 Subject: [PATCH 07/12] feat: implement emtpyIndicatorMode --- .../main/cypress/specs/ExpandableText.cy.ts | 32 +++++++++++++------ packages/main/src/ExpandableText.hbs | 2 +- packages/main/src/ExpandableText.ts | 11 +++++++ packages/main/test/pages/ExpandableText.html | 16 ++++++++++ 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index cd18acc549ab..bf93be46f0b9 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -4,7 +4,6 @@ import "../../src/ExpandableText.js"; // Test keyboard support // Test SR attributes // Test RTL -// Test empty indicator // Test mobile popover describe("ExpandableText", () => { @@ -23,9 +22,8 @@ describe("ExpandableText", () => { it("Should display full text if maxCharacters are set, but not exceeded", () => { const text = "This is a very long text that should be displayed"; - const maxCharacters = 9999; - cy.mount(html``); + cy.mount(html``); cy.get("[ui5-expandable-text]") .shadow() @@ -60,9 +58,8 @@ describe("ExpandableText", () => { it("Should display 'Show More' if maxCharacters are exceeded, set to 0", () => { const text = "This is a very long text that should be displayed"; - const maxCharacters = 0; - cy.mount(html``); + cy.mount(html``); cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); @@ -82,10 +79,7 @@ describe("ExpandableText", () => { }); it("Should NOT display 'Show More' if maxCharacters are 0, but text is empty", () => { - const text = ""; - const maxCharacters = 0; - - cy.mount(html``); + cy.mount(html``); cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); @@ -137,6 +131,26 @@ describe("ExpandableText", () => { }); }); + describe("Empty Indicator", () => { + it("Should display empty indicator if text is empty and emptyIndicatorMode=On", () => { + cy.mount(html``); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .should("have.attr", "empty-indicator-mode", "On"); + }); + + it("Should NOT display empty indicator if text is empty and emptyIndicatorMode=Off", () => { + cy.mount(html``); + + cy.get("[ui5-expandable-text]") + .shadow() + .find("[ui5-text]") + .should("have.attr", "empty-indicator-mode", "Off"); + }); + }); + describe("Rendering and Interaction with overflowMode=Popover", () => { it("Toggling 'Show More' and 'Show Less'", () => { const text = "This is a very long text that should be displayed"; diff --git a/packages/main/src/ExpandableText.hbs b/packages/main/src/ExpandableText.hbs index 1008db97907c..dadd9e2df0ea 100644 --- a/packages/main/src/ExpandableText.hbs +++ b/packages/main/src/ExpandableText.hbs @@ -1,5 +1,5 @@
- {{_displayedText}} + {{_displayedText}} {{#if _maxCharactersExceeded}} {{_ellipsisText}} diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts index a271b0d5ed0b..f7085785c1e1 100644 --- a/packages/main/src/ExpandableText.ts +++ b/packages/main/src/ExpandableText.ts @@ -6,6 +6,7 @@ import Text from "./Text.js"; import Link from "./Link.js"; import ResponsivePopover from "./ResponsivePopover.js"; import ExpandableTextOverflowMode from "./types/ExpandableTextOverflowMode.js"; +import type TextEmptyIndicatorMode from "./types/TextEmptyIndicatorMode.js"; // Template import ExpandableTextTemplate from "./generated/templates/ExpandableTextTemplate.lit.js"; @@ -61,10 +62,20 @@ class ExpandableText extends UI5Element { /** * Determines how the full text will be displayed. + * @default "InPlace" + * @public */ @property() overflowMode: `${ExpandableTextOverflowMode}` = ExpandableTextOverflowMode.InPlace + /** + * Specifies if an empty indicator should be displayed when there is no text. + * @default "Off" + * @public + */ + @property() + emptyIndicatorMode: `${TextEmptyIndicatorMode}` = "Off"; + @property({ type: Boolean }) _expanded = false; _popoverClosed = false; diff --git a/packages/main/test/pages/ExpandableText.html b/packages/main/test/pages/ExpandableText.html index 7705011eda65..005c084209d5 100644 --- a/packages/main/test/pages/ExpandableText.html +++ b/packages/main/test/pages/ExpandableText.html @@ -10,6 +10,8 @@ +

ExpandableText

+

General

Two Texts Next to Each Other

@@ -29,4 +31,18 @@

max-characters=0

overflowMode=Popover

+ +

EmptyIndicatorMode

+

On

+ + +

On, with Text

+ + +

Off

+ + +

Off, with Text

+ + From aae938eb4cf2e5021fd15c7152445fff53b2cb16 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 Nov 2024 16:45:01 +0200 Subject: [PATCH 08/12] feat: take Show More and Show Less from messagebundle --- packages/main/src/ExpandableText.hbs | 2 +- packages/main/src/ExpandableText.ts | 28 +++++++------------ .../main/src/i18n/messagebundle.properties | 6 ++++ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/main/src/ExpandableText.hbs b/packages/main/src/ExpandableText.hbs index dadd9e2df0ea..be9c9c918df9 100644 --- a/packages/main/src/ExpandableText.hbs +++ b/packages/main/src/ExpandableText.hbs @@ -3,7 +3,7 @@ {{#if _maxCharactersExceeded}} {{_ellipsisText}} - {{_toggleText}} + {{_textForToggle}} {{#if _usePopover}} diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts index f7085785c1e1..f42ae8e24a73 100644 --- a/packages/main/src/ExpandableText.ts +++ b/packages/main/src/ExpandableText.ts @@ -1,12 +1,18 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; +import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import Text from "./Text.js"; import Link from "./Link.js"; import ResponsivePopover from "./ResponsivePopover.js"; import ExpandableTextOverflowMode from "./types/ExpandableTextOverflowMode.js"; import type TextEmptyIndicatorMode from "./types/TextEmptyIndicatorMode.js"; +import { + EXPANDABLE_TEXT_SHOW_LESS, + EXPANDABLE_TEXT_SHOW_MORE, +} from "./generated/i18n/i18n-defaults.js"; // Template import ExpandableTextTemplate from "./generated/templates/ExpandableTextTemplate.lit.js"; @@ -78,11 +84,9 @@ class ExpandableText extends UI5Element { @property({ type: Boolean }) _expanded = false; - _popoverClosed = false; - onAfterRendering() { - this._popoverClosed = false; - } + @i18n("@ui5/webcomponents") + static i18nBundle: I18nBundle; getFocusDomRef(): HTMLElement | undefined { if (this._usePopover) { @@ -116,27 +120,15 @@ class ExpandableText extends UI5Element { return "... "; } - get _toggleText() { - return this._expanded ? "Show Less" : "Show More"; - } - - _handleShowLessClick() { - this._expanded = false; - } - - _handleShowMoreClick() { - this._expanded = true; + get _textForToggle() { + return this._expanded ? ExpandableText.i18nBundle.getText(EXPANDABLE_TEXT_SHOW_LESS) : ExpandableText.i18nBundle.getText(EXPANDABLE_TEXT_SHOW_MORE); } _handlePopoverClose() { this._toggleExpanded(); - this._popoverClosed = true; } _toggleExpanded() { - if (this._popoverClosed) { - return; - } this._expanded = !this._expanded; } } diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties index 6035281c047b..71ca5f879f2d 100644 --- a/packages/main/src/i18n/messagebundle.properties +++ b/packages/main/src/i18n/messagebundle.properties @@ -170,6 +170,12 @@ EMPTY_INDICATOR_SYMBOL=\u2013 #XFLD: ARIA announcement for the empty value. EMPTY_INDICATOR_ACCESSIBLE_TEXT=Empty value +#XLNK: Link to allow the user to see complete text +EXPANDABLE_TEXT_SHOW_MORE=Show More + +#XLNK: This link allows the user to collapse the complete text and display only the first characters that can fit +EXPANDABLE_TEXT_SHOW_LESS=Show Less + FILEUPLOAD_BROWSE=Browse... #XACT: File uploader title From b17b561e121829b7f62feab5b6e2c217d846a3ee Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Fri, 22 Nov 2024 11:32:31 +0200 Subject: [PATCH 09/12] feat: add mobile support --- .../main/cypress/specs/ExpandableText.cy.ts | 46 +++++++++++++++++-- packages/main/src/ExpandableText.hbs | 21 ++++++++- packages/main/src/ExpandableText.ts | 19 +++++++- .../main/src/i18n/messagebundle.properties | 3 ++ packages/main/src/themes/ExpandableText.css | 7 +++ 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index bf93be46f0b9..2e20cd73b0eb 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -4,7 +4,6 @@ import "../../src/ExpandableText.js"; // Test keyboard support // Test SR attributes // Test RTL -// Test mobile popover describe("ExpandableText", () => { describe("Rendering and Interaction", () => { @@ -182,11 +181,16 @@ describe("ExpandableText", () => { cy.get("@expTextShadow") .find("[ui5-responsive-popover]") + .as("rpo"); + + cy.get("@rpo") .should("exist") .should("have.attr", "open"); - cy.get("@expTextShadow") - .find("[ui5-responsive-popover]") + cy.get("@rpo") + .should("have.attr", "content-only-on-desktop"); + + cy.get("@rpo") .invoke("attr", "opener") .then(function testOpenerId(opener) { expect(opener).to.equal(this.expectedOpenerId); @@ -202,5 +206,41 @@ describe("ExpandableText", () => { // .find("[ui5-responsive-popover]") // .should("not.have.attr", "open"); }); + + it("Toggling 'Show More' and 'Show Less' on Mobile Device", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 5; + + cy.mount(html``); + cy.ui5SimulateDevice("phone"); + + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + + cy.get("@expTextShadow") + .find("[ui5-link].ui5-exp-text-toggle") + .contains("Show More") + .realClick(); + + cy.get("@expTextShadow") + .find("[ui5-responsive-popover]").as("rpo"); + + cy.get("@rpo") + .should("exist") + .should("have.attr", "open"); + + cy.get("@rpo") + .should("have.attr", "_hide-header"); + + cy.get("@rpo") + .contains("[slot=footer] [ui5-button]", "Close") + .should("exist"); + + cy.get("@rpo") + .contains("[slot=footer] [ui5-button]", "Close") + .realClick(); + + cy.get("@rpo") + .should("not.have.attr", "open"); + }); }); }); diff --git a/packages/main/src/ExpandableText.hbs b/packages/main/src/ExpandableText.hbs index be9c9c918df9..c902cbb51a6b 100644 --- a/packages/main/src/ExpandableText.hbs +++ b/packages/main/src/ExpandableText.hbs @@ -3,11 +3,28 @@ {{#if _maxCharactersExceeded}} {{_ellipsisText}} - {{_textForToggle}} + {{_textForToggle}} {{#if _usePopover}} - + {{text}} + {{/if}} {{/if}} diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts index f42ae8e24a73..8cba8df8bfac 100644 --- a/packages/main/src/ExpandableText.ts +++ b/packages/main/src/ExpandableText.ts @@ -4,14 +4,17 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import { isPhone } from "@ui5/webcomponents-base/dist/Device.js"; import Text from "./Text.js"; import Link from "./Link.js"; import ResponsivePopover from "./ResponsivePopover.js"; +import Button from "./Button.js"; import ExpandableTextOverflowMode from "./types/ExpandableTextOverflowMode.js"; import type TextEmptyIndicatorMode from "./types/TextEmptyIndicatorMode.js"; import { EXPANDABLE_TEXT_SHOW_LESS, EXPANDABLE_TEXT_SHOW_MORE, + EXPANDABLE_TEXT_CLOSE, } from "./generated/i18n/i18n-defaults.js"; // Template @@ -46,6 +49,7 @@ import ExpandableTextCss from "./generated/themes/ExpandableText.css.js"; Text, Link, ResponsivePopover, + Button, ], }) class ExpandableText extends UI5Element { @@ -124,13 +128,24 @@ class ExpandableText extends UI5Element { return this._expanded ? ExpandableText.i18nBundle.getText(EXPANDABLE_TEXT_SHOW_LESS) : ExpandableText.i18nBundle.getText(EXPANDABLE_TEXT_SHOW_MORE); } + get _closeButtonText() { + return ExpandableText.i18nBundle.getText(EXPANDABLE_TEXT_CLOSE); + } + _handlePopoverClose() { - this._toggleExpanded(); + if (!isPhone()) { + this._expanded = false; + } } - _toggleExpanded() { + _handleToggleClick() { this._expanded = !this._expanded; } + + _handleCloseButtonClick(e: CustomEvent) { + this._expanded = false; + e.stopPropagation(); + } } ExpandableText.define(); diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties index 71ca5f879f2d..abe14382da7e 100644 --- a/packages/main/src/i18n/messagebundle.properties +++ b/packages/main/src/i18n/messagebundle.properties @@ -176,6 +176,9 @@ EXPANDABLE_TEXT_SHOW_MORE=Show More #XLNK: This link allows the user to collapse the complete text and display only the first characters that can fit EXPANDABLE_TEXT_SHOW_LESS=Show Less +#XBUT: Text for close action +EXPANDABLE_TEXT_CLOSE=Close + FILEUPLOAD_BROWSE=Browse... #XACT: File uploader title diff --git a/packages/main/src/themes/ExpandableText.css b/packages/main/src/themes/ExpandableText.css index 9ce5ce330239..e289e1482556 100644 --- a/packages/main/src/themes/ExpandableText.css +++ b/packages/main/src/themes/ExpandableText.css @@ -22,4 +22,11 @@ .ui5-exp-text-text, .ui5-exp-text-ellipsis { color: inherit; +} + +.ui5-exp-text-footer { + width: 100%; + display: flex; + align-items: center; + justify-content: flex-end; } \ No newline at end of file From 6995c88a969f34694367213242df63e513a69a2c Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Fri, 22 Nov 2024 11:59:15 +0200 Subject: [PATCH 10/12] fix: closing popover on toggle click now works --- packages/main/cypress/specs/ExpandableText.cy.ts | 8 +++----- packages/main/src/ExpandableText.ts | 11 +++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index 2e20cd73b0eb..4f4865881271 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -3,7 +3,6 @@ import "../../src/ExpandableText.js"; // Test keyboard support // Test SR attributes -// Test RTL describe("ExpandableText", () => { describe("Rendering and Interaction", () => { @@ -201,10 +200,9 @@ describe("ExpandableText", () => { .contains("Show Less") .realClick(); - // TODO: currently it is not possible to close the popover from an opener link, therefore this test is unstable. Fix the issue and uncomment it. - // cy.get("@expTextShadow") - // .find("[ui5-responsive-popover]") - // .should("not.have.attr", "open"); + cy.get("@expTextShadow") + .find("[ui5-responsive-popover]") + .should("not.have.attr", "open"); }); it("Toggling 'Show More' and 'Show Less' on Mobile Device", () => { diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts index 8cba8df8bfac..0d48fe0b23f8 100644 --- a/packages/main/src/ExpandableText.ts +++ b/packages/main/src/ExpandableText.ts @@ -92,6 +92,8 @@ class ExpandableText extends UI5Element { @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; + _preventNextToggleClickHandling = false; + getFocusDomRef(): HTMLElement | undefined { if (this._usePopover) { return this.shadowRoot?.querySelector("[ui5-responsive-popover]") as HTMLElement; @@ -135,10 +137,19 @@ class ExpandableText extends UI5Element { _handlePopoverClose() { if (!isPhone()) { this._expanded = false; + + if (this.shadowRoot!.activeElement === this.shadowRoot!.querySelector("[ui5-link]")) { + this._preventNextToggleClickHandling = true; + } } } _handleToggleClick() { + if (this._preventNextToggleClickHandling) { + this._preventNextToggleClickHandling = false; + return; + } + this._expanded = !this._expanded; } From 0ff19cb47cb9a2b09e76105395ad353ed91fc336 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Fri, 22 Nov 2024 15:21:08 +0200 Subject: [PATCH 11/12] test: add kh handling tests --- .../main/cypress/specs/ExpandableText.cy.ts | 107 +++++++++++++++--- packages/main/src/ExpandableText.hbs | 15 ++- packages/main/src/ExpandableText.ts | 15 +-- 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/packages/main/cypress/specs/ExpandableText.cy.ts b/packages/main/cypress/specs/ExpandableText.cy.ts index 4f4865881271..9a0303e3c926 100644 --- a/packages/main/cypress/specs/ExpandableText.cy.ts +++ b/packages/main/cypress/specs/ExpandableText.cy.ts @@ -1,9 +1,6 @@ import { html } from "lit"; import "../../src/ExpandableText.js"; -// Test keyboard support -// Test SR attributes - describe("ExpandableText", () => { describe("Rendering and Interaction", () => { it("Should display full text if maxCharacters is not set", () => { @@ -102,9 +99,9 @@ describe("ExpandableText", () => { cy.mount(html``); cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + cy.get("@expTextShadow").find("[ui5-link].ui5-exp-text-toggle").as("toggle"); - cy.get("@expTextShadow") - .find("[ui5-link].ui5-exp-text-toggle") + cy.get("@toggle") .contains("Show More") .realClick(); @@ -113,8 +110,7 @@ describe("ExpandableText", () => { .contains(text) .should("exist"); - cy.get("@expTextShadow") - .find("[ui5-link].ui5-exp-text-toggle") + cy.get("@toggle") .contains("Show Less") .realClick(); @@ -123,9 +119,49 @@ describe("ExpandableText", () => { .contains(text.substring(0, maxCharacters)) .should("exist"); + cy.get("@toggle") + .contains("Show More") + .should("exist"); + }); + + it("Toggling 'Show More' and 'Show Less' with keyboard", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 5; + + cy.mount(html` + + + `); + + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + cy.get("@expTextShadow").find("[ui5-link].ui5-exp-text-toggle").as("toggle"); + + cy.get("#before") + .focus(); + + cy.get("#before") + .realPress("Tab"); + + cy.get("@toggle") + .realPress("Enter"); + cy.get("@expTextShadow") - .find("[ui5-link].ui5-exp-text-toggle") - .contains("Show More"); + .find("[ui5-text]") + .contains(text) + .should("exist"); + + cy.get("@toggle") + .contains("Show Less") + .realPress("Enter"); + + cy.get("@expTextShadow") + .find("[ui5-text]") + .contains(text.substring(0, maxCharacters)) + .should("exist"); + + cy.get("@toggle") + .contains("Show More") + .should("exist"); }); }); @@ -157,6 +193,7 @@ describe("ExpandableText", () => { cy.mount(html``); cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + cy.get("@expTextShadow").find("[ui5-link].ui5-exp-text-toggle").as("toggle"); cy.get("@expTextShadow") .find("[ui5-text]") @@ -168,13 +205,11 @@ describe("ExpandableText", () => { .contains("... ") .should("exist"); - cy.get("@expTextShadow") - .find("[ui5-link].ui5-exp-text-toggle") + cy.get("@toggle") .contains("Show More") .realClick(); - cy.get("@expTextShadow") - .find("[ui5-link].ui5-exp-text-toggle") + cy.get("@toggle") .invoke("attr", "id") .as("expectedOpenerId"); @@ -195,8 +230,7 @@ describe("ExpandableText", () => { expect(opener).to.equal(this.expectedOpenerId); }); - cy.get("@expTextShadow") - .find("[ui5-link].ui5-exp-text-toggle") + cy.get("@toggle") .contains("Show Less") .realClick(); @@ -205,6 +239,49 @@ describe("ExpandableText", () => { .should("not.have.attr", "open"); }); + it("Toggling 'Show More' and 'Show Less' with keyboard", () => { + const text = "This is a very long text that should be displayed"; + const maxCharacters = 5; + + cy.mount(html` + + + `); + + cy.get("[ui5-expandable-text]").shadow().as("expTextShadow"); + cy.get("@expTextShadow").find("[ui5-link].ui5-exp-text-toggle").as("toggle"); + + cy.get("#before") + .focus(); + + cy.get("#before") + .realPress("Tab"); + + cy.get("@toggle") + .realPress("Enter"); + + cy.get("@expTextShadow") + .find("[ui5-responsive-popover]") + .as("rpo"); + + cy.get("@rpo") + .should("exist") + .should("have.attr", "open"); + + cy.get("@toggle") + .contains("Show Less") + .should("exist"); + + cy.realPress("Escape"); + + cy.get("@rpo") + .should("not.have.attr", "open"); + + cy.get("@toggle") + .contains("Show More") + .should("exist"); + }); + it("Toggling 'Show More' and 'Show Less' on Mobile Device", () => { const text = "This is a very long text that should be displayed"; const maxCharacters = 5; diff --git a/packages/main/src/ExpandableText.hbs b/packages/main/src/ExpandableText.hbs index c902cbb51a6b..80853697c09c 100644 --- a/packages/main/src/ExpandableText.hbs +++ b/packages/main/src/ExpandableText.hbs @@ -1,9 +1,20 @@
- {{_displayedText}} + + {{_displayedText}} + {{#if _maxCharactersExceeded}} {{_ellipsisText}} - {{_textForToggle}} + + {{_textForToggle}} + {{#if _usePopover}} Date: Fri, 22 Nov 2024 16:21:12 +0200 Subject: [PATCH 12/12] docs: add basic sample --- packages/main/src/ExpandableText.ts | 24 ++++++++++++++----- .../_components_pages/main/ExpandableText.mdx | 14 +++++++++++ .../main/ExpandableText/Basic/Basic.md | 4 ++++ .../main/ExpandableText/Basic/main.js | 1 + .../main/ExpandableText/Basic/sample.html | 20 ++++++++++++++++ 5 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 packages/website/docs/_components_pages/main/ExpandableText.mdx create mode 100644 packages/website/docs/_samples/main/ExpandableText/Basic/Basic.md create mode 100644 packages/website/docs/_samples/main/ExpandableText/Basic/main.js create mode 100644 packages/website/docs/_samples/main/ExpandableText/Basic/sample.html diff --git a/packages/main/src/ExpandableText.ts b/packages/main/src/ExpandableText.ts index 7919cb7ee140..463283aff21a 100644 --- a/packages/main/src/ExpandableText.ts +++ b/packages/main/src/ExpandableText.ts @@ -26,19 +26,31 @@ import ExpandableTextCss from "./generated/themes/ExpandableText.css.js"; /** * @class * - *

Overview

+ * ### Overview * + * The `ui5-expandable-text` component allows displaying a large body of text in a small space. It provides an "expand" functionality, which shows the full text. * - *

Usage

+ * ### Usage * - * For the expandable-text - *

ES6 Module Import

+ * #### When to use: + * - You specifically have to deal with long texts * - * import "@ui5/webcomponents/dist/ExpandableText.js"; + * #### When not to use: + * - Do not use long texts and descriptions if you can provide short and meaningful alternatives + * - The content is critical for the user. In this case use short descriptions that can fit in + * + * ### Responsive Behavior + * + * On phones, when the component is set to show the full text in a popover, the popover will be displayed full screen. + * + * ### ES6 Module Import + * + * `import "@ui5/webcomponents/dist/Text";` * * @constructor * @extends UI5Element * @public + * @since 2.5.0 */ @customElement({ tag: "ui5-expandable-text", @@ -68,7 +80,7 @@ class ExpandableText extends UI5Element { * @public */ @property({ type: Number }) - maxCharacters = Number.POSITIVE_INFINITY; + maxCharacters : number = Infinity; /** * Determines how the full text will be displayed. diff --git a/packages/website/docs/_components_pages/main/ExpandableText.mdx b/packages/website/docs/_components_pages/main/ExpandableText.mdx new file mode 100644 index 000000000000..66585ad4808a --- /dev/null +++ b/packages/website/docs/_components_pages/main/ExpandableText.mdx @@ -0,0 +1,14 @@ +--- +slug: ../ExpandableText +sidebar_class_name: newComponentBadge +--- + +import Basic from "../../_samples/main/ExpandableText/Basic/Basic.md"; + + +<%COMPONENT_OVERVIEW%> + +## Basic Sample + + +<%COMPONENT_METADATA%> diff --git a/packages/website/docs/_samples/main/ExpandableText/Basic/Basic.md b/packages/website/docs/_samples/main/ExpandableText/Basic/Basic.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/_samples/main/ExpandableText/Basic/Basic.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + diff --git a/packages/website/docs/_samples/main/ExpandableText/Basic/main.js b/packages/website/docs/_samples/main/ExpandableText/Basic/main.js new file mode 100644 index 000000000000..effaaeb913d5 --- /dev/null +++ b/packages/website/docs/_samples/main/ExpandableText/Basic/main.js @@ -0,0 +1 @@ +import "@ui5/webcomponents/dist/ExpandableText.js"; \ No newline at end of file diff --git a/packages/website/docs/_samples/main/ExpandableText/Basic/sample.html b/packages/website/docs/_samples/main/ExpandableText/Basic/sample.html new file mode 100644 index 000000000000..aab56007e365 --- /dev/null +++ b/packages/website/docs/_samples/main/ExpandableText/Basic/sample.html @@ -0,0 +1,20 @@ + + + + + + + + Sample + + + + + + Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis accusamus assumenda debitis excepturi distinctio adipisci magnam qui a id, praesentium ullam voluptatem ad, modi quo perspiciatis soluta quasi facere molestiae + + + + + +