diff --git a/.changeset/real-comics-leave.md b/.changeset/real-comics-leave.md new file mode 100644 index 0000000000..00162f4fb1 --- /dev/null +++ b/.changeset/real-comics-leave.md @@ -0,0 +1,8 @@ +--- +"@patternfly/elements": minor +--- + +✨ Added `` component + +A **hint** is in-app messaging that provides a one-step reminder, explanation, or call to action for a page or modal. Hints provide information about an interaction or prerequisite step that might not be immediately obvious to the user. + \ No newline at end of file diff --git a/elements/package.json b/elements/package.json index 94dadf7fc8..3f4e952bc7 100644 --- a/elements/package.json +++ b/elements/package.json @@ -31,6 +31,7 @@ "./pf-dropdown/pf-dropdown-item.js": "./pf-dropdown/pf-dropdown-item.js", "./pf-dropdown/pf-dropdown-menu.js": "./pf-dropdown/pf-dropdown-menu.js", "./pf-dropdown/pf-dropdown.js": "./pf-dropdown/pf-dropdown.js", + "./pf-hint/pf-hint.js": "./pf-hint/pf-hint.js", "./pf-icon/pf-icon.js": "./pf-icon/pf-icon.js", "./pf-jazz-hands/pf-jazz-hands.js": "./pf-jazz-hands/pf-jazz-hands.js", "./pf-jump-links/pf-jump-links-item.js": "./pf-jump-links/pf-jump-links-item.js", @@ -128,7 +129,8 @@ "castastrophe (https://github.com/castastrophe)", "Wes Ruvalcaba", "Rohit Bharmal (https://github.com/Rohit2601)", - "Ajinyka Shinde " + "Ajinyka Shinde ", + "Gili Greenberger (https://github.com/Gili-Greenberger)" ], "dependencies": { "@lit/context": "^1.1.5", diff --git a/elements/pf-dropdown/pf-dropdown.css b/elements/pf-dropdown/pf-dropdown.css index 3b1d30eeee..7eafb6a620 100644 --- a/elements/pf-dropdown/pf-dropdown.css +++ b/elements/pf-dropdown/pf-dropdown.css @@ -214,7 +214,14 @@ pf-dropdown-menu, --pf-c-dropdown__menu--Top, calc(100% + var(--pf-global--spacer--xs, 0.25rem)) ) !important; - left: 0 !important; + left: var( + --pf-c-dropdown__menu--Left, + 0 + ) !important; + right: var( + --pf-c-dropdown__menu--Right, + auto + ) !important; z-index: var( --pf-c-dropdown__menu--ZIndex, var(--pf-global--ZIndex--sm, 200) diff --git a/elements/pf-hint/README.md b/elements/pf-hint/README.md new file mode 100644 index 0000000000..e9213125aa --- /dev/null +++ b/elements/pf-hint/README.md @@ -0,0 +1,112 @@ +# PatternFly Elements Hint + +A **hint** is in-app messaging that provides a one-step reminder, explanation, or call to action for a page or modal. Hints provide information about an interaction or prerequisite step that might not be immediately obvious to the user. + +Read more about Hint in the [PatternFly Elements Hint documentation](https://patternflyelements.org/components/hint) + +## Installation + +Load `` via CDN: + +```html + +``` + +Or, if you are using [NPM](https://npm.im/), install it + +```bash +npm install @patternfly/elements +``` + +Then once installed, import it to your application: + +```js +import '@patternfly/elements/pf-hint/pf-hint.js'; +``` + +## Usage + +### Default with no title + +Basic hint without a title, used for simple informational messages. + +```html + + Welcome to the new documentation experience. + Learn more about the improved features. + +``` + +### Hint with title + +Add a title to your hint to provide more context to the user. + +```html + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + +``` + +### Hint with footer + +```html + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + + Try it for 90 days + + +``` + +### Hint with actions + +```html + + + + + + + + Link + + + + + + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + +``` + +## Slots + +- `(default)` – Body content of the hint. +- `title` – Optional title that appears above the body copy. +- `footer` – Optional footer content, typically links or buttons. +- `actions` – Optional actions menu (for example, a kebab dropdown in the top-right corner). + +## CSS Custom Properties + +- `--pf-c-hint--BackgroundColor` – Background color (`var(--pf-global--palette--blue-50, #e7f1fa)`). +- `--pf-c-hint--BorderColor` – Border color (`var(--pf-global--palette--blue-100, #bee1f4)`). +- `--pf-c-hint--BorderWidth` – Border width (`1px`). +- `--pf-c-hint--BorderRadius` – Border radius (`var(--pf-global--BorderRadius--sm, 3px)`). +- `--pf-c-hint--PaddingTop/Right/Bottom/Left` – Padding around the container (`var(--pf-global--spacer--lg, 1.5rem)`). +- `--pf-c-hint__title--FontSize` – Title font size (`1.125rem`). +- `--pf-c-hint__title--FontWeight` – Title font weight (`var(--pf-global--FontWeight--semi-bold, 700)`). +- `--pf-c-hint__body--FontSize` – Body font size (`1rem`). +- `--pf-c-hint__body--Color` – Body text color (`var(--pf-global--Color--100, #151515)`). +- `--pf-c-hint__footer--child--MarginRight` – Spacing between footer children (`1rem`). +- `--pf-c-hint__actions--c-dropdown--MarginTop` – Offset for dropdown actions (`calc(0.375rem * -1)`). + +## CSS Parts + +- `container` +- `title` +- `body` +- `footer` +- `actions` diff --git a/elements/pf-hint/demo/index.html b/elements/pf-hint/demo/index.html new file mode 100644 index 0000000000..43fb03bd61 --- /dev/null +++ b/elements/pf-hint/demo/index.html @@ -0,0 +1,47 @@ +

Default with no title

+ + Welcome to the new documentation experience. + Learn more about the improved features. + + + + + + + Link + Action + Disabled Link + Disabled Action +
+ Separated Link + Separated Action +
+
+ Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + Try it for 90 days +
+ + + + diff --git a/elements/pf-hint/demo/with-title.html b/elements/pf-hint/demo/with-title.html new file mode 100644 index 0000000000..a511307ad5 --- /dev/null +++ b/elements/pf-hint/demo/with-title.html @@ -0,0 +1,59 @@ +

Hint with title

+ + + + + + Link + Action + Disabled Link + Disabled Action +
+ Separated Link + Separated Action +
+
+ Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. +
+ + + + + + Link + Action + Disabled Link + Disabled Action +
+ Separated Link + Separated Action +
+
+ Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + Try it for 90 days +
+ + + + diff --git a/elements/pf-hint/docs/pf-hint.md b/elements/pf-hint/docs/pf-hint.md new file mode 100644 index 0000000000..d9f8030343 --- /dev/null +++ b/elements/pf-hint/docs/pf-hint.md @@ -0,0 +1,104 @@ +{% renderOverview %} + + Welcome to the new documentation experience. + Learn more about the improved features. + +{% endrenderOverview %} + +{% band header="Usage" %} + ### Default with no title + Basic hint without a title, used for simple informational messages. + + {% htmlexample %} + + Welcome to the new documentation experience. + Learn more about the improved features. + + {% endhtmlexample %} + + ### Hint with title + Add a title to your hint by using the `title` slot to provide more context. + + {% htmlexample %} + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + + {% endhtmlexample %} + + ### With a footer + Add action links or buttons to the footer using the `footer` slot. + + {% htmlexample %} + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + + Try it for 90 days + + + {% endhtmlexample %} + + ### With actions + Add an actions menu (like a kebab dropdown) using the `actions` slot. + + {% htmlexample %} + + + + + + Link + + + + + + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + + {% endhtmlexample %} + + ### Complete example + A hint with all available slots. + + {% htmlexample %} + + + + + + Link + + + + + + Disabled link + + + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management to remediate all your systems across regions and geographies. + + Try it for 90 days + + + {% endhtmlexample %} + +{% endband %} + +{% renderSlots %}{% endrenderSlots %} + +{% renderAttributes %}{% endrenderAttributes %} + +{% renderProperties %}{% endrenderProperties %} + +{% renderMethods %}{% endrenderMethods %} + +{% renderEvents %}{% endrenderEvents %} + +{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} + +{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/pf-hint/pf-hint.css b/elements/pf-hint/pf-hint.css new file mode 100644 index 0000000000..19d60d76b6 --- /dev/null +++ b/elements/pf-hint/pf-hint.css @@ -0,0 +1,120 @@ +:host { + display: block; + + /* Container */ + --pf-c-hint--GridRowGap: var(--pf-global--spacer--md, 1rem); + --pf-c-hint--PaddingTop: var(--pf-global--spacer--lg, 1.5rem); + --pf-c-hint--PaddingRight: var(--pf-global--spacer--lg, 1.5rem); + --pf-c-hint--PaddingBottom: var(--pf-global--spacer--lg, 1.5rem); + --pf-c-hint--PaddingLeft: var(--pf-global--spacer--lg, 1.5rem); + --pf-c-hint--BackgroundColor: var(--pf-global--palette--blue-50, #e7f1fa); + --pf-c-hint--BorderColor: var(--pf-global--palette--blue-100, #bee1f4); + --pf-c-hint--BorderWidth: 1px; + --pf-c-hint--BorderRadius: var(--pf-global--BorderRadius--sm, 3px); + --pf-c-hint--BoxShadow: var(--pf-global--BoxShadow--sm, 0 0.0625rem 0.125rem 0 rgba(3, 3, 3, 0.12), 0 0 0.125rem 0 rgba(3, 3, 3, 0.06)); + + /* Title */ + --pf-c-hint__title--FontSize: 1.125rem; + --pf-c-hint__title--FontWeight: var(--pf-global--FontWeight--semi-bold, 700); + --pf-c-hint__title--Color: var(--pf-global--Color--100, #151515); + --pf-c-hint__title--LineHeight: var(--pf-global--LineHeight--md, 1.5); + + /* Body */ + --pf-c-hint__body--FontSize: 1rem; + --pf-c-hint__body--Color: var(--pf-global--Color--100, #151515); + --pf-c-hint__body--LineHeight: var(--pf-global--LineHeight--md, 1.5); + + /* Footer */ + --pf-c-hint__footer--MarginTop: 0; + --pf-c-hint__footer--child--MarginRight: 1rem; + + /* Actions */ + --pf-c-hint__actions--Top: var(--pf-global--spacer--lg, 1.5rem); + --pf-c-hint__actions--Right: var(--pf-global--spacer--lg, 1.5rem); + --pf-c-hint__actions--MarginLeft: 3rem; + --pf-c-hint__actions--c-dropdown--MarginTop: calc(0.375rem * -1); +} + +#container { + position: relative; + display: grid; + grid-template-columns: 1fr auto; + gap: var(--pf-c-hint--GridRowGap); + padding: + var(--pf-c-hint--PaddingTop) + var(--pf-c-hint--PaddingRight) + var(--pf-c-hint--PaddingBottom) + var(--pf-c-hint--PaddingLeft); + background-color: var(--pf-c-hint--BackgroundColor); + border: var(--pf-c-hint--BorderWidth) solid var(--pf-c-hint--BorderColor); + border-radius: var(--pf-c-hint--BorderRadius); + box-shadow: var(--pf-c-hint--BoxShadow); + overflow: visible; +} + +#container > * { + grid-column: 1; +} + +#actions { + grid-column: 2; + grid-row: 1 / -1; + align-self: start; + position: relative; + z-index: 1000; +} + +#actions ::slotted(pf-dropdown) { + margin-top: var(--pf-c-hint__actions--c-dropdown--MarginTop); + --pf-c-dropdown__menu--MinWidth: auto; + --pf-c-dropdown__menu--Left: auto; + --pf-c-dropdown__menu--Right: 0; +} + +#title { + font-size: var(--pf-c-hint__title--FontSize); + font-weight: var(--pf-c-hint__title--FontWeight); + color: var(--pf-c-hint__title--Color); + line-height: var(--pf-c-hint__title--LineHeight); +} + +#body { + font-size: var(--pf-c-hint__body--FontSize); + color: var(--pf-c-hint__body--Color); + line-height: var(--pf-c-hint__body--LineHeight); +} + +#footer { + margin-top: var(--pf-c-hint__footer--MarginTop); + font-size: var(--pf-c-hint__body--FontSize); + line-height: var(--pf-c-hint__body--LineHeight); +} + +#footer ::slotted(*) { + margin-right: var(--pf-c-hint__footer--child--MarginRight); +} + +#footer ::slotted(*:last-child) { + margin-right: 0; +} + +/* Hidden elements */ +[hidden] { + display: none !important; +} + +/* Link styling within hint */ +::slotted(a) { + color: var(--pf-global--link--Color, #06c); + text-decoration: none; +} + +::slotted(a:hover) { + color: var(--pf-global--link--Color--hover, #004080); + text-decoration: underline; +} + +/* Button styling in footer */ +::slotted(pf-button) { + --pf-c-button--m-link--Color: var(--pf-global--link--Color, #06c); +} diff --git a/elements/pf-hint/pf-hint.ts b/elements/pf-hint/pf-hint.ts new file mode 100644 index 0000000000..d3fe94a80a --- /dev/null +++ b/elements/pf-hint/pf-hint.ts @@ -0,0 +1,68 @@ +import { LitElement, html, type TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { classMap } from 'lit/directives/class-map.js'; + +import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js'; + +import styles from './pf-hint.css'; + +/** + * A **hint** is in-app messaging that provides a one-step reminder, explanation, + * or call to action for a page or modal. Hints provide information about an interaction + * or prerequisite step that might not be immediately obvious to the user. + * + * @summary Provides inline contextual help or information to users + * @alias Hint + */ +@customElement('pf-hint') +export class PfHint extends LitElement { + static readonly styles: CSSStyleSheet[] = [styles]; + + #slots = new SlotController(this, 'title', null, 'footer', 'actions'); + + render(): TemplateResult<1> { + const hasActions = !this.#slots.isEmpty('actions'); + + return html` +
+
+ + +
+
+ + +
+
+ + +
+ +
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-hint': PfHint; + } +} diff --git a/elements/pf-hint/test/pf-hint.e2e.ts b/elements/pf-hint/test/pf-hint.e2e.ts new file mode 100644 index 0000000000..f6c748476f --- /dev/null +++ b/elements/pf-hint/test/pf-hint.e2e.ts @@ -0,0 +1,25 @@ +import { test } from '@playwright/test'; +import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; +import { SSRPage } from '@patternfly/pfe-tools/test/playwright/SSRPage.js'; + +const tagName = 'pf-hint'; + +test.describe(tagName, () => { + test('snapshot', async ({ page }) => { + const componentPage = new PfeDemoPage(page, tagName); + await componentPage.navigate(); + await componentPage.snapshot(); + }); + + test('ssr', async ({ browser }) => { + const fixture = new SSRPage({ + tagName, + browser, + demoDir: new URL('../demo/', import.meta.url), + importSpecifiers: [ + `@patternfly/elements/${tagName}/${tagName}.js`, + ], + }); + await fixture.snapshots(); + }); +}); diff --git a/elements/pf-hint/test/pf-hint.spec.ts b/elements/pf-hint/test/pf-hint.spec.ts new file mode 100644 index 0000000000..b7b7fc2302 --- /dev/null +++ b/elements/pf-hint/test/pf-hint.spec.ts @@ -0,0 +1,96 @@ +import { expect, html } from '@open-wc/testing'; +import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; +import { PfHint } from '@patternfly/elements/pf-hint/pf-hint.js'; +import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js'; + +describe('', function() { + describe('simply instantiating', function() { + it('imperatively instantiates', function() { + expect(document.createElement('pf-hint')).to.be.an.instanceof(PfHint); + }); + + it('should upgrade', async function() { + const el = await createFixture(html` + Basic hint + `); + const klass = customElements.get('pf-hint'); + expect(el) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(PfHint); + }); + }); + + describe('basic hint', function() { + let element: PfHint; + beforeEach(async function() { + element = await createFixture(html` + Welcome to the new documentation experience. + `); + }); + + it('should render body content, and not title footer, or actions', async function() { + const snap = await a11ySnapshot(); + expect(snap.children?.length).to.equal(1); + }); + }); + + describe('hint with title', function() { + let element: PfHint; + beforeEach(async function() { + element = await createFixture(html` + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management. + + `); + await element.updateComplete; + }); + + it('should render title and body content', async function() { + const snap = await a11ySnapshot(); + expect(snap.children?.length).to.equal(2); + }); + }); + + describe('hint with footer', function() { + let element: PfHint; + beforeEach(async function() { + element = await createFixture(html` + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management. + Try it for 90 days + + `); + await element.updateComplete; + }); + + it('should render footer', function() { + const footer = element.shadowRoot!.querySelector('#footer'); + expect(footer).to.exist; + }); + }); + + describe('hint with actions', function() { + let element: PfHint; + beforeEach(async function() { + element = await createFixture(html` + + + Do more with Find it Fix it capabilities + Upgrade to Red Hat Smart Management. + + `); + await element.updateComplete; + }); + + it('should render title, body, and actions', async function() { + const { children: [actions, title, body, ...rest] = [] } = await a11ySnapshot(); + expect(actions.role).to.equal('button'); + expect(title.role).to.equal('text'); + expect(body.role).to.equal('text'); + expect(rest.length).to.equal(0); + }); + }); +});