diff --git a/.changeset/feat-rh-button.md b/.changeset/feat-rh-button.md new file mode 100644 index 00000000000..234a97c8806 --- /dev/null +++ b/.changeset/feat-rh-button.md @@ -0,0 +1,13 @@ +--- +"@rhds/elements": minor +--- + +✨ Added ``. + +Button is a form-associated custom element. Buttons allow users to +perform an action when triggered. They feature a text label, a background or a +border, and icons. + +```html +Submit +``` diff --git a/.changeset/rh-context-slotchange.md b/.changeset/rh-context-slotchange.md new file mode 100644 index 00000000000..04da7e858e9 --- /dev/null +++ b/.changeset/rh-context-slotchange.md @@ -0,0 +1,4 @@ +--- +"@rhds/elements": patch +--- +``: notify children of context when adding them using javascript. diff --git a/.changeset/rh-context-theme.md b/.changeset/rh-context-theme.md new file mode 100644 index 00000000000..576557caacd --- /dev/null +++ b/.changeset/rh-context-theme.md @@ -0,0 +1,4 @@ +--- +"@rhds/elements": patch +--- +``: set color context, rather than palette, on consumers diff --git a/.stylelintrc.yml b/.stylelintrc.yml index 25432b7f931..45aedad3c80 100644 --- a/.stylelintrc.yml +++ b/.stylelintrc.yml @@ -5,7 +5,7 @@ ignoreFiles: rules: alpha-value-notation: number # TODO: fix for `percentage` in tokens color-hex-length: long - color-function-notation: legacy # TODO: fix for `modern` in tokens + color-function-notation: modern custom-property-pattern: _?[a-z]+[a-z0-9-]* declaration-block-no-redundant-longhand-properties: - true diff --git a/docs/_plugins/importMap.cjs b/docs/_plugins/importMap.cjs index 72c6ddc6cfd..f1673832802 100644 --- a/docs/_plugins/importMap.cjs +++ b/docs/_plugins/importMap.cjs @@ -74,19 +74,16 @@ module.exports = function(eleventyConfig, { Object.assign(json.imports ?? {}, Object.values(json.scopes ?? {}).find(x => 'lit-html' in x)); // ENDHACK - // HACK: no clue why we need to do this + // TODO: automate this Object.assign(json.imports ?? {}, { // TODO // '@rhds/elements/lib/': '/assets/packages/@rhds/elements/lib/', // '@rhds/elements/lib/context/': '/assets/packages/@rhds/elements/lib/context/', // '@rhds/elements/lib/context/color/': '/assets/packages/@rhds/elements/lib/context/color/', - // '@rhds/elements/lib/context/color/consumer.js': '/assets/packages/@rhds/elements/lib/context/color/consumer.js', '@rhds/elements/lib/': '/assets/lib/', '@rhds/elements/lib/context/': '/assets/lib/context/', '@rhds/elements/lib/context/color/': '/assets/lib/context/color/', - '@rhds/elements/lib/context/color/consumer.js': '/assets/lib/context/color/consumer.js', }); - // ENDHACk performance.mark('importMap-end'); @@ -122,4 +119,3 @@ function logPerf() { } /* eslint-enable no-console */ } - diff --git a/elements/rh-button/README.md b/elements/rh-button/README.md new file mode 100644 index 00000000000..3bff50f3220 --- /dev/null +++ b/elements/rh-button/README.md @@ -0,0 +1,26 @@ +# Button + +Buttons allow users to perform an action when triggered. They feature a text +label, a background or a border, and icons. + +## Installation + +If using npm/bundlers: + +```bash +npm install @rhds/elements +``` + +Then once installed, import it to your application: + +```js +import '@rhds/elements/rh-button/rh-button.js'; +``` + +## Usage + +### Basic Button + +```html +Ok +``` diff --git a/elements/rh-button/demo/color-context.html b/elements/rh-button/demo/color-context.html new file mode 100644 index 00000000000..bac0b640913 --- /dev/null +++ b/elements/rh-button/demo/color-context.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/elements/rh-button/demo/color-context.js b/elements/rh-button/demo/color-context.js new file mode 100644 index 00000000000..e082d563689 --- /dev/null +++ b/elements/rh-button/demo/color-context.js @@ -0,0 +1,3 @@ +for (const provider of document.querySelectorAll('rh-context-provider')) { + provider.append(document.getElementById('template').content.cloneNode(true)); +} diff --git a/elements/rh-button/demo/demo.css b/elements/rh-button/demo/demo.css new file mode 100644 index 00000000000..a789da83fdf --- /dev/null +++ b/elements/rh-button/demo/demo.css @@ -0,0 +1,52 @@ +section { + display: flex; + align-items: center; + flex-flow: row wrap; + gap: var(--rh-space-sm, 6px); + padding: var(--rh-space-xl, 24px) var(--rh-space-4xl, 64px); +} + +rh-context-provider { + padding-block: var(--rh-space-lg, 16px); + padding-inline: var(--rh-space-6xl, 96px); + display: flex; + align-items: center; + gap: var(--rh-space-sm, 6px); +} + +h2 { + width: 100%; +} + +#checkboxes { + display: grid; + grid-template-columns: min-content 1fr; +} + +label { + display: block; +} + +form h2 { + width: 100%; +} + +form output { + text-transform: capitalize; +} + +datalist { + display: flex; + flex-direction: column; + justify-content: space-between; + writing-mode: vertical-lr; +} + +datalist, +input { + width: 200px; +} + +option { + padding: 0; +} diff --git a/elements/rh-button/demo/form-control.html b/elements/rh-button/demo/form-control.html new file mode 100644 index 00000000000..e87f9587223 --- /dev/null +++ b/elements/rh-button/demo/form-control.html @@ -0,0 +1,31 @@ + + + + +
+

Form Control

+
+
+ + rh-button in a <fieldset> element; + clicking this button must submit the form + + Submit +
+ +
+ Use these checkboxes to toggle disabled state + + + + +
+ +
+ Observe and reset form state + Reset + + Pending +
+
+
diff --git a/elements/rh-button/demo/form-control.js b/elements/rh-button/demo/form-control.js new file mode 100644 index 00000000000..8c983ce9ae1 --- /dev/null +++ b/elements/rh-button/demo/form-control.js @@ -0,0 +1,28 @@ +const form = document.getElementById('form'); +const fieldset = document.getElementById('fieldset'); + +/** @this {HTMLFormElement} */ +function onSubmit(event) { + event.preventDefault(); + this.elements.output.textContent = 'Submitted'; +} + +/** @this {HTMLFormElement} */ +function onReset() { + fieldset.disabled = false; + this.elements.output.textContent = 'Pending'; +} + +/** @this{HTMLInputElement} */ +function onChange({ target: { checked, dataset: { controls } } }) { + // eslint-disable-next-line no-console + console.log(`${controls}.disabled =`, checked); + const el = document.getElementById(controls); + if (el) { + el.disabled = checked; + } +} + +form.addEventListener('submit', onSubmit); +form.addEventListener('reset', onReset); +form.addEventListener('change', onChange); diff --git a/elements/rh-button/demo/rh-button.html b/elements/rh-button/demo/rh-button.html new file mode 100644 index 00000000000..822b82d8242 --- /dev/null +++ b/elements/rh-button/demo/rh-button.html @@ -0,0 +1,15 @@ + + + +
+

Variants

+ Danger + Primary + Link + Secondary + Secondary Danger + Tertiary + Close + Play + Disabled +
diff --git a/elements/rh-button/demo/rh-button.js b/elements/rh-button/demo/rh-button.js new file mode 100644 index 00000000000..d581a9cf99c --- /dev/null +++ b/elements/rh-button/demo/rh-button.js @@ -0,0 +1,5 @@ +import '@rhds/elements/rh-context-provider/rh-context-provider.js'; +import '@rhds/elements/rh-button/rh-button.js'; + +import { Logger } from '@patternfly/pfe-core/controllers/logger.js'; +Logger.debugLog(true); diff --git a/elements/rh-button/docs/rh-button.md b/elements/rh-button/docs/rh-button.md new file mode 100644 index 00000000000..8f1a8d46f69 --- /dev/null +++ b/elements/rh-button/docs/rh-button.md @@ -0,0 +1,17 @@ +{% renderOverview %} + Ok +{% endrenderOverview %} + +{% band header="Usage" %}{% endband %} + +{% renderSlots %}{% endrenderSlots %} + +{% renderAttributes %}{% endrenderAttributes %} + +{% renderMethods %}{% endrenderMethods %} + +{% renderEvents %}{% endrenderEvents %} + +{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} + +{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/rh-button/rh-button.css b/elements/rh-button/rh-button.css new file mode 100644 index 00000000000..64d2ef7c653 --- /dev/null +++ b/elements/rh-button/rh-button.css @@ -0,0 +1,501 @@ +.light { + /* PRIMARY */ + --_primary-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-background-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_primary-border-color: transparent; + --_primary-border-width: var(--rh-border-width-sm, 1px); + --_primary-active-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-active-background-color: var(--rh-color-interactive-blue-darkest, #004080); + --_primary-active-border-width: var(--rh-border-width-sm, 1px); + --_primary-focus-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-focus-background-color: var(--rh-color-interactive-blue-darkest, #004080); + --_primary-focus-border-width: var(--rh-border-width-md, 2px); + --_primary-hover-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-hover-background-color: var(--rh-color-interactive-blue-darkest, #004080); + --_primary-hover-border-width: var(--rh-border-width-sm, 1px); + + /* DANGER */ + --_danger-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-background-color: var(--rh-color-red-600, #be0000); + --_danger-border-color: transparent; + --_danger-border-width: var(--rh-border-width-sm, 1px); + --_danger-active-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-active-background-color: #a30000; /* WARNING: non-token value */ + --_danger-active-border-color: transparent; + --_danger-focus-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-focus-background-color: #a30000; /* WARNING: non-token value */ + --_danger-focus-border-color: transparent; + --_danger-focus-border-width: var(--rh-border-width-md, 2px); + --_danger-hover-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-hover-background-color: #a30000; /* WARNING: non-token value */ + --_danger-hover-border-color: transparent; + + /* SECONDARY */ + --_secondary-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-danger-color: var(--rh-color-red-600, #be0000); + --_secondary-background-color: transparent; + --_secondary-border-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-border-width: var(--rh-border-width-sm, 1px); + --_secondary-active-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-active-background-color: transparent; + --_secondary-active-border-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-active-border-width: var(--rh-border-width-md, 2px); + --_secondary-focus-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-focus-background-color: transparent; + --_secondary-focus-border-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-focus-border-width: var(--rh-border-width-md, 2px); + --_secondary-hover-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-hover-background-color: transparent; + --_secondary-hover-border-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_secondary-hover-border-width: var(--rh-border-width-md, 2px); + + /* TERTIARY */ + --_tertiary-color: var(--rh-color-text-primary-on-light, #151515); + --_tertiary-background-color: transparent; + --_tertiary-border-color: var(--rh-color-border-strong-on-light, #151515); + --_tertiary-border-width: var(--rh-border-width-sm, 1px); + --_tertiary-active-color: var(--rh-color-text-primary-on-light, #151515); + --_tertiary-active-background-color: transparent; + --_tertiary-active-border-color: var(--rh-color-border-strong-on-light, #151515); + --_tertiary-active-border-width: var(--rh-border-width-md, 2px); + --_tertiary-focus-color: var(--rh-color-text-primary-on-light, #151515); + --_tertiary-focus-background-color: transparent; + --_tertiary-focus-border-color: var(--rh-color-border-strong-on-light, #151515); + --_tertiary-focus-border-width: var(--rh-border-width-md, 2px); + --_tertiary-hover-color: var(--rh-color-text-primary-on-light, #151515); + --_tertiary-hover-background-color: transparent; + --_tertiary-hover-border-color: var(--rh-color-border-strong-on-light, #151515); + --_tertiary-hover-border-width: var(--rh-border-width-md, 2px); + + /* LINK */ + --_link-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_link-background-color: transparent; + --_link-active-color: var(--rh-color-blue-600, #002952); + --_link-active-background-color: transparent; + --_link-focus-color: var(--rh-color-blue-600, #002952); + --_link-focus-background-color: transparent; + --_link-focus-outline-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_link-hover-color: var(--rh-color-blue-600, #002952); + --_link-hover-background-color: transparent; + + /* CLOSE */ + --_close-color: var(--rh-context-light-color-text-muted, #6a6e73); + --_close-background-color: transparent; + --_close-active-color: var(--rh-color-black-900, #151515); + --_close-active-background-color: transparent; + --_close-focus-color: var(--rh-color-black-900, #151515); + --_close-focus-background-color: transparent; + --_close-focus-outline-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_close-hover-color: var(--rh-color-black-900, #151515); + --_close-hover-background-color: transparent; + + /* PLAY */ + --_b9: var(--rh-color-black-900-rgb, 21 21 21); + --_play-color: var(--rh-color-icon-secondary-on-dark, #ffffff); + --_play-background-color: rgb(var(--_b9) / var(--rh-opacity-50, 50%)); + --_play-active-background-color: rgb(var(--_b9) / var(--rh-opacity-80, 80%)); + --_play-focus-background-color: rgb(var(--_b9) / var(--rh-opacity-80, 80%)); + --_play-focus-outline-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_play-hover-background-color: rgb(var(--_b9) / var(--rh-opacity-80, 80%)); +} + +.dark { + --_focus-outline-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + + /* PRIMARY */ + --_primary-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-background-color: var(--rh-color-interactive-blue-darker, #0066cc); + --_primary-border-color: transparent; + --_primary-border-width: var(--rh-border-width-sm, 1px); + --_primary-active-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-active-background-color: var(--rh-color-interactive-blue-darkest, #004080); + --_primary-focus-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-focus-background-color: var(--rh-color-interactive-blue-darkest, #004080); + --_primary-hover-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_primary-hover-background-color: var(--rh-color-interactive-blue-darkest, #004080); + + /* DANGER */ + --_danger-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-background-color: #fe5142; /* WARNING: non-token value */ + --_danger-border-color: transparent; + --_danger-border-width: var(--rh-border-width-sm, 1px); + --_danger-active-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-active-background-color: #ff7468; /* WARNING: non-token value */ + --_danger-active-border-color: transparent; + --_danger-focus-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-focus-background-color: #ff7468; /* WARNING: non-token value */ + --_danger-focus-border-color: transparent; + --_danger-hover-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_danger-hover-background-color: #ff7468; /* WARNING: non-token value */ + --_danger-hover-border-color: transparent; + + /* SECONDARY */ + --_secondary-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-danger-color: var(--rh-color-red-200, #fab6b6); + --_secondary-background-color: transparent; + --_secondary-border-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-border-width: var(--rh-border-width-sm, 1px); + --_secondary-active-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-active-background-color: transparent; + --_secondary-active-border-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-active-border-width: var(--rh-border-width-md, 2px); + --_secondary-focus-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-focus-background-color: transparent; + --_secondary-focus-border-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-focus-border-width: var(--rh-border-width-md, 2px); + --_secondary-hover-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-hover-background-color: transparent; + --_secondary-hover-border-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_secondary-hover-border-width: var(--rh-border-width-md, 2px); + + /* TERTIARY */ + --_tertiary-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_tertiary-background-color: transparent; + --_tertiary-border-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_tertiary-border-width: var(--rh-border-width-sm, 1px); + --_tertiary-active-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_tertiary-active-background-color: transparent; + --_tertiary-active-border-color: var(--rh-color-border-strong-on-dark, #ffffff); + --_tertiary-active-border-width: var(--rh-border-width-md, 2px); + --_tertiary-focus-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_tertiary-focus-background-color: transparent; + --_tertiary-focus-border-color: var(--rh-color-border-strong-on-dark, #ffffff); + --_tertiary-focus-border-width: var(--rh-border-width-md, 2px); + --_tertiary-hover-color: var(--rh-color-text-primary-on-dark, #ffffff); + --_tertiary-hover-background-color: transparent; + --_tertiary-hover-border-color: var(--rh-color-border-strong-on-dark, #ffffff); + --_tertiary-hover-border-width: var(--rh-border-width-md, 2px); + + /* LINK */ + --_link-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_link-background-color: transparent; + --_link-active-color: var(--rh-color-interactive-blue-lightest, #bee1f4); + --_link-active-background-color: transparent; + --_link-focus-color: var(--rh-color-interactive-blue-lightest, #bee1f4); + --_link-focus-background-color: transparent; + --_link-focus-outline-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_link-hover-color: var(--rh-color-interactive-blue-lightest, #bee1f4); + --_link-hover-background-color: transparent; + + /* CLOSE */ + --_close-color: var(--rh-color-text-secondary-on-dark, #d2d2d2); + --_close-background-color: transparent; + --_close-focus-background-color: transparent; + --_close-focus-outline-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + + /* PLAY */ + --_w: var(--rh-color-white-rgb, 255 255 255); + --_play-color: var(--rh-color-icon-secondary-on-light, #151515); + --_play-background-color: rgb(var(--_w) / var(--rh-opacity-50, 50%)); + --_play-active-background-color: rgb(var(--_w) / var(--rh-opacity-80, 80%)); + --_play-focus-background-color: rgb(var(--_w) / var(--rh-opacity-80, 80%)); + --_play-focus-outline-color: var(--rh-color-interactive-blue-lighter, #73bcf7); + --_play-hover-background-color: rgb(var(--_w) / var(--rh-opacity-80, 80%)); +} + +:host, +#rhds-container { + display: contents; +} + +:host([hidden]) { + display: none !important; +} + +[hidden] { + display: none !important; +} + +rh-icon { + color: currentcolor; +} + +button { + cursor: pointer; + position: relative; + color: var(--_color); + background-color: var(--_background-color); + font-family: inherit; + font-size: var(--rh-font-size-body-text-md, 1rem); + font-weight: var(--rh-font-weight-body-text-regular, 400); + line-height: var(--rh-line-height-body-text, 1.5); + padding-block: var(--rh-space-sm, 6px); + padding-inline: var(--rh-space-lg, 16px); + border-width: 0; + border-style: solid; + border-radius: var(--rh-border-radius-default, 3px); + outline-offset: var(--rh-length-4xs, 1px); + + --_color: var(--_default-color, var(--_primary-color)); + --_background-color: var(--_default-background-color, var(--_primary-background-color)); + --_border-color: var(--_default-border-color, var(--_primary-border-color)); + --_border-width: var(--_default-border-width, var(--_primary-border-width)); +} + +button:after { + position: absolute; + inset: 0 0 0 0; + content: ""; + border-style: solid; + border-color: var(--_border-color); + border-width: var(--_border-width); + border-radius: var(--rh-border-radius-default, 3px); +} + +[part="icon"] { + display: none; + pointer-events: none; +} + +.hasIcon { + position: relative; + display: flex; + align-items: center; +} + +/****************************** + * * + * PRIMARY * + * * + ******************************/ + +button:active { + --_color: var(--_active-color, var(--_primary-active-color)); + --_background-color: var(--_active-background-color, var(--_primary-active-background-color)); + --_border-width: var(var(--_active-border-width), var(--_primary-active-border-width)); +} + +button:focus { + --_color: var(--_focus-color, var(--_primary-focus-color)); + --_background-color: var(--_focus-background-color, var(--_primary-focus-background-color)); + --_border-width: var(--_focus-border-width, var(--_primary-focus-border-width)); +} + +button:focus:after { + border-width: var(--_border-width, var(--rh-border-width-md, 2px)); +} + +button:active, +button:focus { + outline: + var(--rh-border-width-md, 2px) + solid + var(--_focus-outline-color, var(--rh-color-interactive-blue-darker, #0066cc)); +} + +button:hover { + --_color: var(--_hover-color, var(--_primary-hover-color)); + --_background-color: var(--_hover-background-color, var(--_primary-hover-background-color)); + --_border-width: var(--_hover-border-width, var(--_primary-hover-border-width)); +} + +/****************************** + * * + * SECONDARY * + * * + ******************************/ + +:host([variant="secondary" i]) button { + --_default-color: var(--_secondary-color); + --_default-background-color: var(--_secondary-background-color); + --_default-border-color: var(--_secondary-border-color); + --_default-border-width: var(--_secondary-border-width); + --_active-color: var(--_secondary-active-color); + --_active-background-color: var(--_secondary-active-background-color); + --_active-border-color: var(--_secondary-active-border-color); + --_active-border-width: var(--_secondary-active-border-width); + --_focus-color: var(--_secondary-focus-color); + --_focus-background-color: var(--_secondary-focus-background-color); + --_focus-border-color: var(--_secondary-focus-border-color); + --_hover-color: var(--_secondary-hover-color); + --_hover-background-color: var(--_secondary-hover-background-color); + --_hover-border-color: var(--_secondary-hover-border-color); + --_hover-border-width: var(--_secondary-hover-border-width); +} + +:host([variant="secondary" i][danger]) button { + --_default-color: var(--_secondary-danger-color); + --_default-background-color: transparent; + --_default-border-color: var(--_danger-background-color); + --_active-color: var(--_danger-background-color); + --_active-background-color: transparent; + --_active-border-color: var(--_danger-active-border-color); + --_focus-color: var(--_secondary-danger-color); + --_focus-background-color: transparent; + --_focus-border-color: var(--_danger-focus-border-color); + --_hover-color: var(--_danger-background-color); + --_hover-background-color: transparent; + --_hover-border-color: var(--_danger-hover-border-color); +} + +/****************************** + * * + * TERTIARY * + * * + ******************************/ + +:host([variant="tertiary" i]) button { + --_default-color: var(--_tertiary-color); + --_default-background-color: var(--_tertiary-background-color); + --_default-border-color: var(--_tertiary-border-color); + --_active-color: var(--_tertiary-active-color); + --_active-background-color: var(--_tertiary-active-background-color); + --_active-border-color: var(--_tertiary-active-border-color); + --_active-border-width: var(--_tertiary-active-border-width); + --_focus-color: var(--_tertiary-focus-color); + --_focus-background-color: var(--_tertiary-focus-background-color); + --_focus-border-color: var(--_tertiary-focus-border-color); + --_hover-color: var(--_tertiary-hover-color); + --_hover-background-color: var(--_tertiary-hover-background-color); + --_hover-border-color: var(--_tertiary-hover-border-color); + --_hover-border-width: var(--_tertiary-hover-border-width); +} + +/****************************** + * * + * LINK * + * * + ******************************/ + +:host([variant="link" i]) button { + display: inline; + + --_default-color: var(--_link-color); + --_default-background-color: var(--_link-background-color); + --_default-border-color: transparent; + --_active-color: var(--_link-active-color); + --_active-background-color: var(--_link-active-background-color); + --_active-border-color: transparent; + --_focus-color: var(--_link-focus-color); + --_focus-background-color: var(--_link-focus-background-color); + --_focus-border-color: transparent; + --_focus-outline-color: var(--_link-focus-outline-color); + --_hover-color: var(--_link-hover-color); + --_hover-background-color: var(--_link-hover-background-color); + --_hover-border-color: transparent; +} + +/****************************** + * * + * CLOSE * + * * + ******************************/ + +:host([variant="close" i]) button { + --_default-color: var(--_close-color); + --_default-background-color: var(--_close-background-color); + --_active-color: var(--_close-active-color); + --_active-background-color: var(--_close-active-background-color); + --_active-border-color: transparent; + --_focus-color: var(--_close-focus-color); + --_focus-background-color: var(--_close-focus-background-color); + --_focus-border-color: transparent; + --_focus-outline-color: var(--_close-focus-outline-color); + --_hover-color: var(--_close-hover-color); + --_hover-background-color: transparent; + --_hover-border-color: transparent; + + width: var(--rh-length-lg, 16px); + aspect-ratio: 1; +} + +/****************************** + * * + * PLAY * + * * + ******************************/ + +:host([variant="play" i]) button { + border-radius: 100%; + width: var(--rh-length-4xl, 64px); + + --_default-color: var(--_play-color); + --_default-background-color: var(--_play-background-color); + --_default-background-opacity: var(--_play-background-opacity); + --_active-color: var(--_play-color); + --_active-background-color: var(--_play-active-background-color); + --_active-background-opacity: var(--_play-active-background-opacity); + --_focus-color: var(--_play-color); + --_focus-background-color: var(--_play-focus-background-color); + --_focus-background-opacity: var(--_play-focus-background-opacity); + --_focus-outline-color: var(--_play-focus-outline-color); + --_hover-color: var(--_play-color); + --_hover-background-color: var(--_play-hover-background-color); + --_hover-background-opacity: var(--_play-hover-background-opacity); + --_icon-size: var(--rh-size-icon-04, 40px); +} + +:host(:is([variant="play" i], [variant="close" i])) button { + aspect-ratio: 1; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; +} + +:host(:is([variant="play" i], [variant="close" i])) [part="icon"] { + display: inline-block; + width: var(--_icon-size, var(--rh-size-icon-01, 16px)); + height: var(--_icon-size, var(--rh-size-icon-01, 16px)); +} + +:host(:is([variant="play" i], [variant="close" i])) svg { + fill: currentcolor; + stroke: currentcolor; +} + +/* visually hidden */ +:host(:is([variant="play" i], [variant="close" i])) #text { + display: inline; + position: absolute !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; +} + +/****************************** + * * + * DANGER * + * * + ******************************/ + +:host([danger]) button { + --_default-color: var(--_danger-color); + --_default-background-color: var(--_danger-background-color); + --_default-border-color: var(--_danger-border-color); + --_active-color: var(--_danger-active-color); + --_active-background-color: var(--_danger-active-background-color); + --_active-border-color: var(--_danger-active-border-color); + --_focus-color: var(--_danger-focus-color); + --_focus-background-color: var(--_danger-focus-background-color); + --_focus-border-color: var(--_danger-focus-border-color); + --_hover-color: var(--_danger-hover-color); + --_hover-background-color: var(--_danger-hover-background-color); + --_hover-border-color: var(--_danger-hover-border-color); +} + +/****************************** + * * + * DISABLED * + * * + ******************************/ + +:host(:disabled) button, +:host(:disabled[danger]) button, +:host(:disabled[variant="link"]) button, +button[disabled] { + pointer-events: none; + cursor: default; + + --_color: var(--rh-context-light-color-text-muted, #6a6e73); + --_background-color: var(--rh-color-black-300, #d2d2d2); +} + +:host(:disabled) button:after { + --_border-color: transparent; +} diff --git a/elements/rh-button/rh-button.ts b/elements/rh-button/rh-button.ts new file mode 100644 index 00000000000..bc4710c90d9 --- /dev/null +++ b/elements/rh-button/rh-button.ts @@ -0,0 +1,76 @@ +import { html } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import { classMap } from 'lit/directives/class-map.js'; + +import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js'; + +import { BaseButton } from '@patternfly/elements/pf-button/BaseButton.js'; + +import styles from './rh-button.css'; + +@customElement('rh-button') +export class RhButton extends BaseButton { + static readonly styles = [styles]; + + /** + * Changes the style of the button. + * - Primary: Used for the most important call to action on a page. Try to + * limit primary buttons to one per page. + * - Secondary: Use secondary buttons for general actions on a page, that + * don’t require as much emphasis as primary button actions. For example, + * you can use secondary buttons where there are multiple actions, like in + * toolbars or data lists. + * - Tertiary: Tertiary buttons are flexible and can be used as needed. + */ + @property({ reflect: true }) variant: 'primary'|'secondary'|'tertiary'|'close'|'play' = 'primary'; + + /** @deprecated The size property is not currently used */ + declare size: string; + + @property({ type: Boolean, reflect: true }) danger = false; + + @colorContextConsumer() private on?: ColorTheme; + + get #variant() { return this.variant?.toLowerCase(); } + + override willUpdate() { + switch (this.#variant) { + case 'close': + case 'play': + this.icon = this.#variant; + break; + } + } + + override render() { + const { on = 'light' } = this; + return html`
${super.render()}
`; + } + + protected renderDefaultIcon() { + switch (this.#variant) { + // TODO: revisit when rh-icon is ready + // return html``; + case 'close': + return html` + + + + `; + case 'play': + return html` + + + + `; + default: + return '' as ReturnType; + } + } +} + +declare global { + interface HTMLElementTagNameMap { + 'rh-button': RhButton; + } +} diff --git a/elements/rh-button/test/rh-button.e2e.ts b/elements/rh-button/test/rh-button.e2e.ts new file mode 100644 index 00000000000..bab66ae43cf --- /dev/null +++ b/elements/rh-button/test/rh-button.e2e.ts @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; + +const tagName = 'rh-button'; + +test.describe(tagName, () => { + test('snapshot', async ({ page }) => { + const componentPage = new PfeDemoPage(page, tagName); + await componentPage.navigate(); + await componentPage.snapshot(); + }); +}); diff --git a/elements/rh-button/test/rh-button.spec.ts b/elements/rh-button/test/rh-button.spec.ts new file mode 100644 index 00000000000..606f4c93acb --- /dev/null +++ b/elements/rh-button/test/rh-button.spec.ts @@ -0,0 +1,18 @@ +import { expect, html } from '@open-wc/testing'; +import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; +import { RhButton } from '../rh-button.js'; + +const template = html` + +`; + +describe('', function() { + it('should upgrade', async function() { + const element = await createFixture(template); + const klass = customElements.get('rh-button'); + expect(element) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(RhButton); + }); +}); diff --git a/elements/rh-context-provider/rh-context-provider.ts b/elements/rh-context-provider/rh-context-provider.ts index 2e3da54c791..35d01acd169 100644 --- a/elements/rh-context-provider/rh-context-provider.ts +++ b/elements/rh-context-provider/rh-context-provider.ts @@ -20,6 +20,10 @@ export class RhContextProvider extends LitElement { @property({ reflect: true, attribute: 'color-palette' }) colorPalette?: ColorPalette; render() { - return html``; + return html``; + } + + #onSlotchange() { + this.requestUpdate('colorPalette'); } } diff --git a/elements/rh-context-provider/test/rh-context-provider.spec.ts b/elements/rh-context-provider/test/rh-context-provider.spec.ts index 7debaf40c59..0de30bbee45 100644 --- a/elements/rh-context-provider/test/rh-context-provider.spec.ts +++ b/elements/rh-context-provider/test/rh-context-provider.spec.ts @@ -28,14 +28,41 @@ export class ContextProviderConsumer extends LitElement { @colorContextConsumer() on?: ColorTheme; } +declare global { + interface HTMLElementTagNameMap { + 'test-context-consumer': ContextConsumer; + 'test-context-consumer-provider': ContextConsumerProvider; + 'test-context-provider-consumer': ContextProviderConsumer; + } +} + describe('', function() { - it('should upgrade', async function() { - const el = await createFixture (html``); - const klass = customElements.get('rh-context-provider'); - expect(el) - .to.be.an.instanceOf(klass) - .and - .to.be.an.instanceOf(RhContextProvider); + describe('simply instantiating', function() { + let element: RhContextProvider; + beforeEach(async function() { + element = await createFixture(html``); + }); + it('should upgrade', async function() { + const klass = customElements.get('rh-context-provider'); + expect(element) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(RhContextProvider); + }); + describe('setting darkest color palette', function() { + beforeEach(async function() { + element.colorPalette = 'darkest'; + await element.updateComplete; + }); + describe('then imperatively adding children', function() { + beforeEach(async function() { + element.append(document.createElement('test-context-consumer')); + }); + it('should notify the children', function() { + expect(element.querySelector('test-context-consumer')?.on).to.equal('dark'); + }); + }); + }); }); describe('with child', function() { diff --git a/elements/rh-dialog/demo/rh-dialog.html b/elements/rh-dialog/demo/rh-dialog.html index 2775aba4007..44f14c110b7 100644 --- a/elements/rh-dialog/demo/rh-dialog.html +++ b/elements/rh-dialog/demo/rh-dialog.html @@ -4,32 +4,32 @@
Standard modal dialog
- Open + Open
Video modal dialog
- Open + Open
YouTube embed modal dialog
- Open + Open
No header content
- Open + Open
No headings
- Open + Open
A lot of content
- Open + Open
@@ -144,7 +144,7 @@

Modal with a header with a truly excessive super duper long ti

Color Context

- +

Light

Light Context

@@ -166,8 +166,8 @@

Light Context

Learn more
- Open Dialog -
+ Open Dialog +

Dark

@@ -191,7 +191,7 @@

Dark Context

Learn more - Open Dialog + Open Dialog
diff --git a/elements/rh-dialog/demo/rh-dialog.js b/elements/rh-dialog/demo/rh-dialog.js index 6e284bd2663..b0df67e4ec1 100644 --- a/elements/rh-dialog/demo/rh-dialog.js +++ b/elements/rh-dialog/demo/rh-dialog.js @@ -1,13 +1,10 @@ -import '@rhds/elements/rh-dialog/rh-dialog.js'; +import '@rhds/elements/rh-button/rh-button.js'; import '@rhds/elements/rh-cta/rh-cta.js'; +import '@rhds/elements/rh-dialog/rh-dialog.js'; import '@patternfly/elements/pf-panel/pf-panel.js'; import '@patternfly/elements/pf-button/pf-button.js'; import '@patternfly/elements/pf-card/pf-card.js'; -const root = document.querySelector('[data-demo="rh-dialog"]')?.shadowRoot ?? document; - -const customTriggerModal = root.querySelector('#custom-modal'); - -customTriggerModal?.setTrigger(root.querySelector('#custom-trigger')); - +const customTriggerModal = document.querySelector('#custom-modal'); +customTriggerModal?.setTrigger(document.querySelector('#custom-trigger')); diff --git a/elements/rh-pagination/demo/rh-pagination.html b/elements/rh-pagination/demo/rh-pagination.html index 75efaef8a12..b4128c8657b 100644 --- a/elements/rh-pagination/demo/rh-pagination.html +++ b/elements/rh-pagination/demo/rh-pagination.html @@ -24,8 +24,8 @@

Valid HTML

Overflow start
- Add Page - Remove Page + Add Page + Remove Page diff --git a/elements/rh-pagination/demo/rh-pagination.js b/elements/rh-pagination/demo/rh-pagination.js index 425cfdfd837..e6edfc43352 100644 --- a/elements/rh-pagination/demo/rh-pagination.js +++ b/elements/rh-pagination/demo/rh-pagination.js @@ -1,6 +1,5 @@ +import '@rhds/elements/rh-button/rh-button.js'; import '@rhds/elements/rh-pagination/rh-pagination.js'; -// TODO: rh-button -import '@patternfly/elements/pf-button/pf-button.js'; const $ = s => document.querySelector(s); const $$ = s => document.querySelectorAll(s); diff --git a/elements/rh-tooltip/demo/rh-tooltip.html b/elements/rh-tooltip/demo/rh-tooltip.html index 559c50622b9..453752baecf 100644 --- a/elements/rh-tooltip/demo/rh-tooltip.html +++ b/elements/rh-tooltip/demo/rh-tooltip.html @@ -6,7 +6,7 @@

Light Theme!

- Left Tooltip + Left Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -14,7 +14,7 @@

Light Theme!

- Right Tooltip + Right Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -22,7 +22,7 @@

Light Theme!

- Top Tooltip + Top Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -30,7 +30,7 @@

Light Theme!

- Bottom Tooltip + Bottom Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -42,7 +42,7 @@

Dark Theme!

- Left Tooltip + Left Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -50,7 +50,7 @@

Dark Theme!

- Right Tooltip + Right Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -58,7 +58,7 @@

Dark Theme!

- Top Tooltip + Top Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. @@ -66,7 +66,7 @@

Dark Theme!

- Bottom Tooltip + Bottom Tooltip Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. diff --git a/elements/rh-tooltip/demo/rh-tooltip.js b/elements/rh-tooltip/demo/rh-tooltip.js index ec09b2b033e..97cd1260337 100644 --- a/elements/rh-tooltip/demo/rh-tooltip.js +++ b/elements/rh-tooltip/demo/rh-tooltip.js @@ -1,3 +1,3 @@ -import '@patternfly/elements/pf-button/pf-button.js'; +import '@rhds/elements/rh-button/rh-button.js'; import '@rhds/elements/rh-context-provider/rh-context-provider.js'; import '@rhds/elements/rh-tooltip/rh-tooltip.js'; diff --git a/lib/context/color/provider.ts b/lib/context/color/provider.ts index 52a7e27866c..008a5ee3416 100644 --- a/lib/context/color/provider.ts +++ b/lib/context/color/provider.ts @@ -75,7 +75,8 @@ export class ColorContextProvider< } get #local() { - return ColorContextProvider.contexts.get(this.host.getAttribute(this.#attribute) ?? ''); + return ColorContextProvider + .contexts.get(this.host.getAttribute(this.#attribute) ?? ''); } get value(): ColorTheme { @@ -147,7 +148,7 @@ export class ColorContextProvider< event.stopPropagation(); // Run the callback to initialize the child's colour-context - event.callback(this.host.getAttribute(this.#attribute) as ColorTheme ?? this.#consumer.value); + event.callback(this.value); // Cache the callback for future updates, if requested if (event.multiple) { @@ -180,4 +181,3 @@ export function colorContextProvider(options?: ColorC }); }; } - diff --git a/web-test-runner.config.js b/web-test-runner.config.js index 1d10cb4c3d6..21af9a81081 100644 --- a/web-test-runner.config.js +++ b/web-test-runner.config.js @@ -4,8 +4,7 @@ import { litcssOptions } from './web-dev-server.config.js'; export default pfeTestRunnerConfig({ litcssOptions, tsconfig: 'tsconfig.json', - files: ['**/elements/**/*.spec.ts'], + files: ['elements/**/*.spec.ts'], // uncomment to get default wtr reporter // reporter: 'default', }); -