diff --git a/packages/main/cypress/specs/AINoticeIndicator.cy.tsx b/packages/main/cypress/specs/AINoticeIndicator.cy.tsx new file mode 100644 index 000000000000..53938a84e324 --- /dev/null +++ b/packages/main/cypress/specs/AINoticeIndicator.cy.tsx @@ -0,0 +1,114 @@ +import { html } from "lit"; +import AINoticeIndicator from "../../src/AINoticeIndicator.js"; +import ResponsivePopover from "../../src/ResponsivePopover.js"; +import Button from "../../src/Button.js"; + +describe("AINoticeIndicator", () => { + describe("Rendering", () => { + it("should render attribution link and verification text only in Default mode", () => { + cy.mount( + + ); + cy.get("#ai-default").should("exist"); + cy.get("#ai-default").shadow().find(".ui5-ai-notice-indicator-root").should("exist"); + cy.get("#ai-default").shadow().find("ui5-link").should("exist"); + cy.get("#ai-default").shadow().find("ui5-label").should("exist"); + cy.get("#ai-default").shadow().find("ui5-icon").should("not.exist"); + }); + + it("should render attribution link only in Shortened mode", () => { + cy.mount( + + ); + cy.get("#ai-shortened").should("exist"); + cy.get("#ai-shortened").shadow().find(".ui5-ai-notice-indicator-root").should("exist"); + cy.get("#ai-shortened").shadow().find("ui5-link").should("exist"); + cy.get("#ai-shortened").shadow().find("ui5-icon").should("not.exist"); + cy.get("#ai-shortened").shadow().find("ui5-label").should("not.exist"); + }); + + it("should render icon, attribution link, and verification text in Emphasized mode", () => { + cy.mount( + + ); + cy.get("#ai-emphasized").should("exist"); + cy.get("#ai-emphasized").shadow().find(".ui5-ai-notice-indicator-root").should("exist"); + cy.get("#ai-emphasized").shadow().find("ui5-link").should("exist"); + cy.get("#ai-emphasized").shadow().find("ui5-icon").should("exist"); + cy.get("#ai-emphasized").shadow().find("ui5-label").should("exist"); + }); + + it("it should render icon only in IconOnly mode", () => { + cy.mount( + + ); + cy.get("#ai-icononly").should("exist"); + cy.get("#ai-icononly").shadow().find(".ui5-ai-notice-indicator-root").should("exist"); + cy.get("#ai-icononly").shadow().find("ui5-icon").should("exist"); + cy.get("#ai-icononly").shadow().find("ui5-link").should("not.exist"); + cy.get("#ai-icononly").shadow().find("ui5-label").should("not.exist"); + }); + }); + + describe("Properties", () => { + it("displays custom attributionText and verificationText when provided", () => { + const attribution = "Generated by AI"; + const verification = "Please verify before use"; + cy.mount( + + ); + cy.get("#ai-custom-text").shadow().find("ui5-link").should("contain.text", attribution); + cy.get("#ai-custom-text").shadow().find("ui5-label").should("contain.text", verification); + }); + + it("falls back to default i18n texts when properties are not set", () => { + cy.mount( + + ); + cy.get("#ai-default-text").shadow().find("ui5-link").should("not.be.empty"); + cy.get("#ai-default-text").shadow().find("ui5-label").should("not.be.empty"); + }); + }); + + describe("Popover Slot", () => { + it("renders slotted popover content", () => { + cy.mount( + + + + + + ); + cy.get("#ai-popover").shadow().find("slot[name='aiNoticePopover']").should("exist"); + cy.get("#test-popover").should("exist"); + }); + + it("clicking the attribution link opens the popover", () => { + cy.mount( + + + + + + ); + cy.get("#ai-popover-click").shadow().find("ui5-link").click(); + cy.get("#test-popover-click").should("have.prop", "open", true); + }); + }); + + +}); + \ No newline at end of file diff --git a/packages/main/src/AINoticeIndicator.ts b/packages/main/src/AINoticeIndicator.ts new file mode 100644 index 000000000000..19d8cf93477c --- /dev/null +++ b/packages/main/src/AINoticeIndicator.ts @@ -0,0 +1,92 @@ +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 slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; +import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; + +import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; +// Template +import AINoticeIndicatorTemplate from "./AINoticeIndicatorTemplate.js"; +// Styles +import AINoticeIndicatorCss from "./generated/themes/AINoticeIndicator.css.js"; + +import type Link from "./Link.js"; +import type AINoticeIndicatorMode from "./types/AINoticeIndicatorMode.js"; +import type { LinkClickEventDetail } from "./Link.js"; +import type ResponsivePopover from "./ResponsivePopover.js"; +import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; + +import { AI_NOTICE_ATTRIBUTION_TEXT, AI_NOTICE_VERIFICATION_TEXT } from "./generated/i18n/i18n-defaults.js"; + +@customElement({ + tag: "ui5-ai-notice-indicator", + languageAware: true, + renderer: jsxRenderer, + styles: AINoticeIndicatorCss, + template: AINoticeIndicatorTemplate, +}) +class AINoticeIndicator extends UI5Element { + /** + * AI attribution notice text (e.g., "Created with AI"). + */ + @property() + attributionText?: string; + + /** + * AI verification prompt text (e.g., "Verify results before use"). + */ + @property() + verificationText?: string; + + /** + * The mode of the AI Notice Indicator. + * It can be set to "Default", "Shortened", "Emphasized", or "IconOnly". + */ + @property({ type: String }) + mode: `${AINoticeIndicatorMode}` = "Default"; + + /** + * Slot for popover content. + */ + @slot({ type: HTMLElement, invalidateOnChildChange: true }) + aiNoticePopover!: Array; + + @i18n("@ui5/webcomponents") + static i18nBundle: I18nBundle; + + constructor() { + super(); + } + + get attributionTextValue() { + if (this.attributionText) { + return this.attributionText; + } + + const defaultLabel = AINoticeIndicator.i18nBundle.getText(AI_NOTICE_ATTRIBUTION_TEXT); + + return defaultLabel; + } + + get verificationTextValue() { + if (this.verificationText) { + return this.verificationText; + } + + const defaultLabel = AINoticeIndicator.i18nBundle.getText(AI_NOTICE_VERIFICATION_TEXT); + + return defaultLabel; + } + + _openRespPopover(e: CustomEvent) { + const resPopover = this.getSlottedNodes("aiNoticePopover")[0]; + if (resPopover) { + resPopover.opener = e.target as Link; + resPopover.openPopup(); + } + } +} + +AINoticeIndicator.define(); + +export default AINoticeIndicator; diff --git a/packages/main/src/AINoticeIndicatorTemplate.tsx b/packages/main/src/AINoticeIndicatorTemplate.tsx new file mode 100644 index 000000000000..2f69ef46c260 --- /dev/null +++ b/packages/main/src/AINoticeIndicatorTemplate.tsx @@ -0,0 +1,40 @@ +import Button from "./Button.js"; +import Link from "./Link.js"; +import Label from "./Label.js"; +import Icon from "./Icon.js"; + +import type AINoticeIndicator from "./AINoticeIndicator.js"; + +export default function AINoticeIndicatorTemplate(this: AINoticeIndicator) { + const { mode, attributionTextValue, verificationTextValue } = this; + + const iconButton = ( + + ); + + const attributionLink = ( + + {attributionTextValue} + + ); + + const verificationTextLabel = ( + + ); + + return ( +
+ {mode !== "Default" && mode !== "Shortened" && iconButton} + {mode !== "IconOnly" && attributionLink} + {mode !== "IconOnly" && mode !== "Shortened" && verificationTextLabel} + +
+ ); +} diff --git a/packages/main/src/bundle.esm.ts b/packages/main/src/bundle.esm.ts index 6ff524ca75e5..ec3497150b09 100644 --- a/packages/main/src/bundle.esm.ts +++ b/packages/main/src/bundle.esm.ts @@ -29,6 +29,7 @@ import { getAllRegisteredTags } from "@ui5/webcomponents-base/dist/CustomElement // or for custom icon collection // setDefaultIconCollection("sap_fiori_3", "my-custom-icons"); +import AINoticeIndicator from "./AINoticeIndicator.js"; import Avatar from "./Avatar.js"; import AvatarGroup from "./AvatarGroup.js"; import Bar from "./Bar.js"; diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties index 0789287415d7..a3394067f3da 100644 --- a/packages/main/src/i18n/messagebundle.properties +++ b/packages/main/src/i18n/messagebundle.properties @@ -1,6 +1,24 @@ #This is the resource bundle for the UI5 Web Components #__ldi.translation.uuid=96bea51a-d5e3-46f0-b1d1-514d97be02ec +#XTIT: Title of the AI Notice +AI_NOTICE_TITLE=Created with AI + +#XTIT: Draft title of the AI Notice +AI_NOTICE_DRAFT_TITLE=Draft created with AI + +#XMSG: Attribution text of the AI Notice +AI_NOTICE_ATTRIBUTION_TEXT=Created with AI. + +#XMSG: Verification text of the AI Notice +AI_NOTICE_VERIFICATION_TEXT=Verify results before use. + +#XMSG: Content of the AI Notice popover +AI_NOTICE_POPOVER_CONTENT=This content was partially or fully generated by artificial intelligence (AI) technologies.\n\n The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use. + +#XBUT: AI Notice popover close button +AI_NOTICE_POPOVER_CLOSE_BUTTON=Close + #XBUT: Card Content aria-label text ARIA_LABEL_CARD_CONTENT=Card Content diff --git a/packages/main/src/themes/AINoticeIndicator.css b/packages/main/src/themes/AINoticeIndicator.css new file mode 100644 index 000000000000..e638e8e3a22e --- /dev/null +++ b/packages/main/src/themes/AINoticeIndicator.css @@ -0,0 +1,7 @@ +:host { + display: flex; + align-items: center; + font-family: var(--sapFontFamily); + font-size: var(--sapFontSize); + color: var(--sapTextColor); +} diff --git a/packages/main/src/types/AINoticeIndicatorMode.ts b/packages/main/src/types/AINoticeIndicatorMode.ts new file mode 100644 index 000000000000..38ab6ad7b1b2 --- /dev/null +++ b/packages/main/src/types/AINoticeIndicatorMode.ts @@ -0,0 +1,27 @@ +/** + * Different types of AINoticeIndicatorMode. + * @public + */ +enum AINoticeIndicatorMode { + /** + * The default mode, displaying the full content. + */ + Default = "Default", + + /** + * A shortened mode, displaying a condensed version of the content. + */ + Shortened = "Shortened", + + /** + * An emphasized mode, highlighting important content. + */ + Emphasized = "Emphasized", + + /** + * An icon-only mode, displaying only an icon without text. + */ + IconOnly = "IconOnly" +} + +export default AINoticeIndicatorMode; diff --git a/packages/main/test/pages/AINoticeIndicator.html b/packages/main/test/pages/AINoticeIndicator.html new file mode 100644 index 000000000000..373bdf24e04f --- /dev/null +++ b/packages/main/test/pages/AINoticeIndicator.html @@ -0,0 +1,109 @@ + + + + + + + AI Notice Indicator + + + + + + + + + +
+

AINoticeIndicator - Default mode

+ + +
+ Created with AI +
+ +
+ + This content was partially or fully generated by artificial intelligence (AI) technologies. + + + The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use. + +
+ + +
+
+
+ +
+

AINoticeIndicator - Shortened mode

+ + +
+ Created with AI +
+ +
+ + This content was partially or fully generated by artificial intelligence (AI) technologies. + + + The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use. + +
+ + +
+
+
+ +
+

AINoticeIndicator - Emphasized mode

+ + +
+ Created with AI +
+ +
+ + This content was partially or fully generated by artificial intelligence (AI) technologies. + + + The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use. + +
+ + +
+
+
+ +
+

AINoticeIndicator - IconOnly mode

+ + +
+ + + + diff --git a/packages/main/test/pages/styles/AINoticeIndicator.css b/packages/main/test/pages/styles/AINoticeIndicator.css new file mode 100644 index 000000000000..b3040a406be4 --- /dev/null +++ b/packages/main/test/pages/styles/AINoticeIndicator.css @@ -0,0 +1,3 @@ +:not(:defined) { + display: none; +} diff --git a/packages/website/docs/_components_pages/main/AINoticeIndicator.mdx b/packages/website/docs/_components_pages/main/AINoticeIndicator.mdx new file mode 100644 index 000000000000..8572925808ef --- /dev/null +++ b/packages/website/docs/_components_pages/main/AINoticeIndicator.mdx @@ -0,0 +1,22 @@ +import AINoticeIndicatorDefault from "../../_samples/main/AINoticeindicator/Default/AINoticeIndicator.md"; +import AINoticeIndicatorShortened from "../../_samples/main/AINoticeindicator/Shortened/AINoticeIndicator.md"; +import AINoticeIndicatorEmphasized from "../../_samples/main/AINoticeindicator/Emphasized/AINoticeIndicator.md"; +import AINoticeIndicatorIconOnly from "../../_samples/main/AINoticeindicator/IconOnly/AINoticeIndicator.md"; + +packages/website/docs/_samples/main/AINoticeindicator/Default/AINoticeIndicator.md + +<%COMPONENT_OVERVIEW%> + +## Default Sample + + +## Shortened Sample + + +## Emphasized Sample + + +## IconOnly Sample + + +<%COMPONENT_METADATA%> \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/Default/AINoticeIndicator.md b/packages/website/docs/_samples/main/AINoticeindicator/Default/AINoticeIndicator.md new file mode 100644 index 000000000000..ffccbf6dd13e --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Default/AINoticeIndicator.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/main/AINoticeindicator/Default/main.js b/packages/website/docs/_samples/main/AINoticeindicator/Default/main.js new file mode 100644 index 000000000000..6ff604e63ae2 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Default/main.js @@ -0,0 +1,6 @@ +import "@ui5/webcomponents/dist/ResponsivePopover.js"; +import "@ui5/webcomponents/dist/AINoticeIndicator.js"; +import "@ui5/webcomponents/dist/Title.js"; +import "@ui5/webcomponents/dist/Link.js"; +import "@ui5/webcomponents/dist/Label.js"; +import "@ui5/webcomponents/dist/Button.js"; \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/Default/sample.html b/packages/website/docs/_samples/main/AINoticeindicator/Default/sample.html new file mode 100644 index 000000000000..0db4819001c6 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Default/sample.html @@ -0,0 +1,44 @@ + + + + + + + + Sample + + + + + + + + Product + Description + Dimensions + Price + + + Notebook Basic 15 + + + + 30 x 18 x 3 cm + 956 EUR + + + Notebook Basic 17 + + + + 29 x 17 x 3.1 cm + 1249 EUR + + + + + + + \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/AINoticeIndicator.md b/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/AINoticeIndicator.md new file mode 100644 index 000000000000..ffccbf6dd13e --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/AINoticeIndicator.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/main/AINoticeindicator/Emphasized/main.js b/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/main.js new file mode 100644 index 000000000000..6ff604e63ae2 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/main.js @@ -0,0 +1,6 @@ +import "@ui5/webcomponents/dist/ResponsivePopover.js"; +import "@ui5/webcomponents/dist/AINoticeIndicator.js"; +import "@ui5/webcomponents/dist/Title.js"; +import "@ui5/webcomponents/dist/Link.js"; +import "@ui5/webcomponents/dist/Label.js"; +import "@ui5/webcomponents/dist/Button.js"; \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/sample.html b/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/sample.html new file mode 100644 index 000000000000..0e2ed25902b0 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Emphasized/sample.html @@ -0,0 +1,41 @@ + + + + + + + + Sample + + + + + +
+

AINoticeIndicator - Emphasized mode

+ + +
+ Created with AI +
+ +
+ + This content was partially or fully generated by artificial intelligence (AI) technologies. + + + The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use. + +
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/AINoticeIndicator.md b/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/AINoticeIndicator.md new file mode 100644 index 000000000000..ffccbf6dd13e --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/AINoticeIndicator.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/main/AINoticeindicator/IconOnly/main.js b/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/main.js new file mode 100644 index 000000000000..6ff604e63ae2 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/main.js @@ -0,0 +1,6 @@ +import "@ui5/webcomponents/dist/ResponsivePopover.js"; +import "@ui5/webcomponents/dist/AINoticeIndicator.js"; +import "@ui5/webcomponents/dist/Title.js"; +import "@ui5/webcomponents/dist/Link.js"; +import "@ui5/webcomponents/dist/Label.js"; +import "@ui5/webcomponents/dist/Button.js"; \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/sample.html b/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/sample.html new file mode 100644 index 000000000000..0bfb1778eae0 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/IconOnly/sample.html @@ -0,0 +1,23 @@ + + + + + + + + Sample + + + + + +
+

AINoticeIndicator - IconOnly mode

+ + +
+ + + + + \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/Shortened/AINoticeIndicator.md b/packages/website/docs/_samples/main/AINoticeindicator/Shortened/AINoticeIndicator.md new file mode 100644 index 000000000000..ffccbf6dd13e --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Shortened/AINoticeIndicator.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/main/AINoticeindicator/Shortened/main.js b/packages/website/docs/_samples/main/AINoticeindicator/Shortened/main.js new file mode 100644 index 000000000000..6ff604e63ae2 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Shortened/main.js @@ -0,0 +1,6 @@ +import "@ui5/webcomponents/dist/ResponsivePopover.js"; +import "@ui5/webcomponents/dist/AINoticeIndicator.js"; +import "@ui5/webcomponents/dist/Title.js"; +import "@ui5/webcomponents/dist/Link.js"; +import "@ui5/webcomponents/dist/Label.js"; +import "@ui5/webcomponents/dist/Button.js"; \ No newline at end of file diff --git a/packages/website/docs/_samples/main/AINoticeindicator/Shortened/sample.html b/packages/website/docs/_samples/main/AINoticeindicator/Shortened/sample.html new file mode 100644 index 000000000000..e9c415258ea1 --- /dev/null +++ b/packages/website/docs/_samples/main/AINoticeindicator/Shortened/sample.html @@ -0,0 +1,41 @@ + + + + + + + + Sample + + + + + +
+

AINoticeIndicator - Shortened mode

+ + +
+ Created with AI +
+ +
+ + This content was partially or fully generated by artificial intelligence (AI) technologies. + + + The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use. + +
+ + +
+
+
+ + + + + \ No newline at end of file