diff --git a/.changeset/many-frogs-film.md b/.changeset/many-frogs-film.md
new file mode 100644
index 0000000000..90ff92818b
--- /dev/null
+++ b/.changeset/many-frogs-film.md
@@ -0,0 +1,27 @@
+---
+"@patternfly/pfe-card": major
+---
+
+## Patternfly Card 1:1 🎉
+
+This release migrates Patternfly Card `pfe-card` to Patternfly 1:1 and matches the style and functionality to PFv4.
+
+This will have several breaking changes from the existing `pfe-card`. Many of the attributes will be moving to `rh-card`.
+
+### NEW: Features and Updates
+- Styles now match PFv4 Card.
+- Size Attribute: `small` is updated to `compact` and `large` is now an option
+ - Example: ` `.
+- Rounded Attribute: NEW! Optionally applies a border radius for the drop shadow and/or border of the card.
+ - Example: ` `
+- FullHeight Attribute: NEW! Optionally allow the card to take up the full height of the parent element.
+ - Example: ` `
+- Plain Attribute: NEW! Removes the border on the card element.
+ - Example: ` `
+
+
+### Breaking Changes
+- No longer includes an `imgSrc` attribute.
+- No longer has a dark mode theme.
+- No longer includes a `border` attribute.
+
diff --git a/elements/pfe-card/BaseCard.scss b/elements/pfe-card/BaseCard.scss
new file mode 100644
index 0000000000..ea392d3f15
--- /dev/null
+++ b/elements/pfe-card/BaseCard.scss
@@ -0,0 +1,28 @@
+:host {
+ display: flex;
+ flex-direction: column;
+}
+
+[part=header] {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+[part=body] ::slotted(:not([slot]):first-of-type) {
+ margin-block-start: 0;
+}
+
+[part=body] ::slotted(:not([slot]):last-of-type) {
+ margin-block-end: 0;
+}
+
+[part=footer] {
+ display: flex;
+ gap: 0.5em;
+}
+
+.empty {
+ display: none;
+}
+
diff --git a/elements/pfe-card/BaseCard.ts b/elements/pfe-card/BaseCard.ts
new file mode 100644
index 0000000000..63a5c0f2fb
--- /dev/null
+++ b/elements/pfe-card/BaseCard.ts
@@ -0,0 +1,53 @@
+import { LitElement, html } from 'lit';
+import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js';
+
+import { classMap } from 'lit/directives/class-map.js';
+
+import style from './BaseCard.scss';
+
+/**
+ * This element creates a header, body, and footer region in which to place
+ * content or other components.
+ *
+ * @summary Gives a preview of information in a small layout
+ *
+ * @slot header
+ * If this slot is used, we expect a heading level tag (h1, h2, h3, h4, h5, h6).
+ * An icon, svg, or use of the icon component are also valid in this region.
+ * @slot - Any content that is not designated for the header or footer slot, will go to this slot.
+ * @slot footer
+ * Use this slot for anything that you want to be stuck to the base of the card.
+ *
+ * @csspart header - The container for *header* content
+ * @csspart body - The container for *body* content
+ * @csspart footer - The container for *footer* content
+ */
+export abstract class BaseCard extends LitElement {
+ static readonly version = '{{version}}';
+
+ static readonly styles = [style];
+
+ protected slots = new SlotController(this, 'header', null, 'footer');
+
+ render() {
+ return html`
+
+
+
+
+
+
+
+ `;
+ }
+}
diff --git a/elements/pfe-card/custom-elements-manifest.config.js b/elements/pfe-card/custom-elements-manifest.config.js
index 3dcb0a2905..a0cc65240b 100644
--- a/elements/pfe-card/custom-elements-manifest.config.js
+++ b/elements/pfe-card/custom-elements-manifest.config.js
@@ -1,5 +1,5 @@
import { pfeCustomElementsManifestConfig } from '@patternfly/pfe-tools/custom-elements-manifest.js';
export default pfeCustomElementsManifestConfig({
- globs: ['pfe-*.ts'],
+ globs: ['pfe-*.ts', 'BaseCard.ts'],
});
diff --git a/elements/pfe-card/demo/bg-pattern.png b/elements/pfe-card/demo/bg-pattern.png
deleted file mode 100644
index 481e114a56..0000000000
Binary files a/elements/pfe-card/demo/bg-pattern.png and /dev/null differ
diff --git a/elements/pfe-card/demo/demo.css b/elements/pfe-card/demo/demo.css
index 80d9dcaa89..218a3ccc6f 100644
--- a/elements/pfe-card/demo/demo.css
+++ b/elements/pfe-card/demo/demo.css
@@ -1,26 +1,44 @@
[data-demo] {
- padding: 1em;
+ display: contents;
+}
+
+main {
+ padding: 2em;
+ background: #eeeeee;
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(200px, calc(25% - 32px)));
- grid-auto-rows: max-content;
- gap: 32px;
+ grid-template-columns: 1fr;
+ grid-template-rows: max-content 1fr;
}
-.button-series::part(footer) {
- display: flex;
- gap: 15px;
+@media (min-width: 800px) {
+ main {
+ grid-template-columns: 1fr 4fr;
+ grid-template-rows: 1fr;
+ }
}
-#first-image {
- grid-column: 1/2;
+form {
+ display: grid;
+ grid-template-columns: max-content auto;
+ grid-auto-rows: max-content;
+ gap: 1em;
}
-h2[slot="header"] {
- font-size: 1.1em;
- font-weight: bolder;
+pfe-card {
+ max-width: 50%;
+ margin: 0 auto;
}
-.custom-border {
- --pfe-card--BorderColor: #eee;
- --pfe-card--BorderWeight: 1px;
+.resize {
+ padding: 2em;
+ resize: vertical;
+ overflow: auto;
+ min-height: 40vh;
+ background: repeating-linear-gradient(
+ 45deg,
+ #fff,
+ #fff 10px,
+ #eee 10px,
+ #eee 20px
+ );
}
diff --git a/elements/pfe-card/demo/kitten-200x150.jpeg b/elements/pfe-card/demo/kitten-200x150.jpeg
deleted file mode 100644
index 984860bc57..0000000000
Binary files a/elements/pfe-card/demo/kitten-200x150.jpeg and /dev/null differ
diff --git a/elements/pfe-card/demo/kitten-400x250.jpeg b/elements/pfe-card/demo/kitten-400x250.jpeg
deleted file mode 100644
index 13fb6f3f12..0000000000
Binary files a/elements/pfe-card/demo/kitten-400x250.jpeg and /dev/null differ
diff --git a/elements/pfe-card/demo/kitten-500x250.jpeg b/elements/pfe-card/demo/kitten-500x250.jpeg
deleted file mode 100644
index 0aa443dcca..0000000000
Binary files a/elements/pfe-card/demo/kitten-500x250.jpeg and /dev/null differ
diff --git a/elements/pfe-card/demo/kitten-500x500.jpeg b/elements/pfe-card/demo/kitten-500x500.jpeg
deleted file mode 100644
index fdbde2e068..0000000000
Binary files a/elements/pfe-card/demo/kitten-500x500.jpeg and /dev/null differ
diff --git a/elements/pfe-card/demo/kitten-650x250.jpeg b/elements/pfe-card/demo/kitten-650x250.jpeg
deleted file mode 100644
index 7d6efda883..0000000000
Binary files a/elements/pfe-card/demo/kitten-650x250.jpeg and /dev/null differ
diff --git a/elements/pfe-card/demo/kitten-900x300.jpeg b/elements/pfe-card/demo/kitten-900x300.jpeg
deleted file mode 100644
index 96dc7c11b9..0000000000
Binary files a/elements/pfe-card/demo/kitten-900x300.jpeg and /dev/null differ
diff --git a/elements/pfe-card/demo/pfe-card.html b/elements/pfe-card/demo/pfe-card.html
index 38c9b1db1a..8f7f506815 100644
--- a/elements/pfe-card/demo/pfe-card.html
+++ b/elements/pfe-card/demo/pfe-card.html
@@ -4,98 +4,33 @@
-
+
+
-
- Lightest card
- This is the lightest pfe-card and a link , and a visited link with border.
- Try
- Buy
-
-
-
- Default card
- Unwrapped item. This is the default pfe-card and a link , and a visited link .
- Try
- Buy
-
-
-
- Darker Card
- This is the darker pfe-card and a link , and a visited link .
- Try
- Buy
-
-
-
- Darkest Card
- This is the darkest pfe-card and a link , and a visited link .
- Learn more
-
-
-
- Complement Card
- This is the complement pfe-card and a link , and a visited link .
- Learn more
-
-
-
- Accent Card
- This is the accent pfe-card and a link , and a visited link .
- Learn more
-
-
-
-
- Imaged card with top & side overflow
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat porro similique saepe tempora vel! Facilis,
- porro?
- Learn more
-
+
-
- Deprecated color attribute
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta rerum tempore natus alias! Cumque illum
- provident corrupti voluptates.
-
-
- Select a palette
- Lightest
- Lighter
- Default
- Darker
- Darkest
- Accent
- Complement
-
-
-
+
+
+ Lightest card
+ This is the lightest pfe-card and a link , and a visited link with border.
+ Try
+ Buy
+
+
diff --git a/elements/pfe-card/demo/pfe-card.js b/elements/pfe-card/demo/pfe-card.js
index b226a181c9..f8973dd13c 100644
--- a/elements/pfe-card/demo/pfe-card.js
+++ b/elements/pfe-card/demo/pfe-card.js
@@ -1,9 +1,22 @@
-import '@patternfly/pfe-band';
import '@patternfly/pfe-card';
-import '@patternfly/pfe-select';
+import '@patternfly/pfe-button';
+import '@patternfly/pfe-switch';
+const form = document.getElementById('card-settings');
+const card = document.querySelector('pfe-card');
-const root = document.querySelector('[data-demo="pfe-card"]')?.shadowRoot ?? document;
-
-root.querySelector('#context-selector').addEventListener('select', event => {
- event.target.closest('pfe-card').setAttribute('color', event.value);
+form.addEventListener('change', function(event) {
+ const { checked } = event.target;
+ const { attribute } = event.target.dataset;
+ switch (attribute) {
+ case 'flat':
+ case 'rounded':
+ case 'plain':
+ case 'full-height':
+ card.toggleAttribute(attribute, checked);
+ break;
+ case 'size':
+ card.setAttribute('size', form.querySelector('label[for="size"]:not([hidden])').textContent.toLowerCase());
+ break;
+ }
});
+
diff --git a/elements/pfe-card/docs/pfe-card.md b/elements/pfe-card/docs/pfe-card.md
index 744588d189..00217b31e7 100644
--- a/elements/pfe-card/docs/pfe-card.md
+++ b/elements/pfe-card/docs/pfe-card.md
@@ -2,43 +2,43 @@
Cards are flexible surfaces used to group information in a small layout. They give small previews of information or provide secondary content in relation to the content it's near. Several cards can be used together to group related information.
-
+
Default card
This is the default card
Link in the footer
-
- Lightest card
- This is the lightest card with a border
+
+ Compact card
+ This is the compact card
Link in the footer
-
- Darker card
- This is the darker card
+
+ Rounded card
+ This is the rounded card
Link in the footer
-
- Darkest card
- This is the darkest card
+
+ Large card
+ This is the large card
Link in the footer
-
- Complement card
- This is the complement card
+
+ Full Height card
+ This is the full height card
Link in the footer
-
- Accent card
- This is the accent card
+
+ Plain card
+ This is the plain card
Link in the footer
-
+
{% endrenderOverview %}
{% band header="Usage" %}
diff --git a/elements/pfe-card/package.json b/elements/pfe-card/package.json
index 93db34af1a..dbc43bd297 100644
--- a/elements/pfe-card/package.json
+++ b/elements/pfe-card/package.json
@@ -10,6 +10,7 @@
"types": "./pfe-card.d.ts",
"exports": {
".": "./pfe-card.js",
+ "./BaseCard.js": "./BaseCard.js",
"./*": "./*.js"
},
"publishConfig": {
@@ -27,7 +28,6 @@
"build:clean": "npm run clean",
"build:esbuild": "node ../../scripts/build.js --include pfe-card",
"build:analyze": "npm run analyze",
- "build:lightdom": "sass pfe-card--lightdom.scss pfe-card--lightdom.min.css --style=compressed --load-path=../../node_modules",
"build:types": "tsc -b .",
"🧑🔬-----TEST-------🧑🔬": "❓ Test packages",
"test": "wtr --files './test/*.spec.ts' --config ../../web-test-runner.config.js",
diff --git a/elements/pfe-card/pfe-card--lightdom.scss b/elements/pfe-card/pfe-card--lightdom.scss
deleted file mode 100644
index 357107be13..0000000000
--- a/elements/pfe-card/pfe-card--lightdom.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@use "@patternfly/pfe-sass" as *;
-
-@include configure($name: 'card');
diff --git a/elements/pfe-card/pfe-card.scss b/elements/pfe-card/pfe-card.scss
index cdb3e02a3f..4300a73f05 100644
--- a/elements/pfe-card/pfe-card.scss
+++ b/elements/pfe-card/pfe-card.scss
@@ -1,278 +1,72 @@
-@use "@patternfly/pfe-sass" as *;
-
-@include configure(
- $name: 'card',
- $variables: (
- // Individual padding overrides available
- PaddingTop: calc(#{pfe-var(container-spacer)} * 2),
- PaddingRight: calc(#{pfe-var(container-spacer)} * 2),
- PaddingBottom: calc(#{pfe-var(container-spacer)} * 2),
- PaddingLeft: calc(#{pfe-var(container-spacer)} * 2),
-
- //-- Border settings
- BorderRadius: pfe-var(surface--border-radius),
-
- //-- Color properties
- BackgroundColor: pfe-var(surface--base),
- context: pfe-var(surface--base--context),
- BackgroundPosition: center center,
-
- spacing: pfe-var(container-spacer),
-
- header: (
- BackgroundColor: rgba(0, 0, 0, pfe-var(opacity)),
- BackgroundColor--dark: rgba(255, 255, 255, pfe-var(opacity)),
- Color: pfe-broadcasted(text)
- ),
-
- footer: (
- spacing--horizontal: calc(#{pfe-var(container-spacer)} / 2)
- ),
- ),
-);
-
-// Nested internal variables (pfe-local calls), maps cannot "self-reference"
-@include merge-local-variables((
- // Internal spacing; elements inside the card (header, body, footer regions)
- spacing--vertical: pfe-local(spacing),
-
- // Combine above variables or allow single override point via variable
- Padding: pfe-local(PaddingTop) pfe-local(PaddingRight) pfe-local(PaddingBottom) pfe-local(PaddingLeft),
-
- //-- Border variable encompasses width, style, and color
- Border: pfe-local(BorderWidth, 0) pfe-local(BorderStyle, solid) pfe-local(BorderColor, pfe-var(surface--border))
-));
-
-$size-small: (
- PaddingTop: pfe-var(container-spacer),
- PaddingRight: pfe-var(container-spacer),
- PaddingBottom: pfe-var(container-spacer),
- PaddingLeft: pfe-var(container-spacer)
-);
-
:host {
- --context: #{pfe-local(context)};
-
- --pfe-theme--color--surface--lightest: #ffffff;
- --pfe-theme--color--surface--lighter: #ececec;
- --pfe-theme--color--surface--base: #f0f0f0;
- --pfe-theme--color--surface--darker: #3c3f42;
- --pfe-theme--color--surface--darkest: #151515;
- --pfe-theme--color--surface--accent: #004080;
- --pfe-theme--color--surface--complement: #002952;
-
- // Start of style declarations for host element
- display: flex;
- flex-direction: column;
- justify-items: flex-start;
- // This allows the card to fill it's container if necessary
- align-self: stretch;
-
- padding: pfe-local(Padding);
- border: pfe-local(Border); // @TODO add automatic border when lightest card is on lightest background?
- border-radius: pfe-local(BorderRadius);
-
- // This property ensures that children in the slots do no overflow
- // the border-radius being set on the container
- overflow: hidden;
-
- // Base colors
- background-color: var(--pfe-card--BackgroundColor, var(--pfe-context-background-color));
- background-position: pfe-local(BackgroundPosition);
- color: pfe-broadcasted(text);
-
- // Remove background color for print
- @include pfe-no-print-background;
-
- // Add the border to the card for print
- @include pfe-print-media {
- border-radius: pfe-fetch(surface--border-radius);
- border: pfe-fetch(surface--border-width) pfe-fetch(surface--border-style) pfe-fetch(surface--border);
- }
+ background-color: var(--pf-c-card--BackgroundColor, var(--pf-global--BackgroundColor--100, #ffffff));
+ box-shadow: var(--pf-c-card--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)));
}
-/* @deprecated */
-:host([color]),
-:host([color-palette]),
-:host([on]) {
- background-color: var(--pfe-card--BackgroundColor, var(--pfe-context-background-color));
+:host([size="compact"]) {
+ --_pf-c-card__body--FontSize: var(--pf-c-card--size-compact__body--FontSize, var(--pf-global--FontSize--sm, .875rem));
+ --_pf-c-card__footer--FontSize: var(--pf-c-card--size-compact__footer--FontSize, var(--pf-global--spacer--md, 1rem));
+ --_pf-c-card--first-child--PaddingTop: var(--pf-c-card--size-compact--first-child--PaddingTop, var(--pf-global--spacer--lg, 1.5rem));
+ --_pf-c-card--child--PaddingRight: var(--pf-c-card--size-compact--child--PaddingRight, var(--pf-global--spacer--md, 1rem));
+ --_pf-c-card--child--PaddingBottom: var(--pf-c-card--size-compact--child--PaddingBottom, var(--pf-global--spacer--md, 1rem));
+ --_pf-c-card--child--PaddingLeft: var(--pf-c-card--size-compact--child--PaddingLeft, var(--pf-global--spacer--md, 1rem));
+ --_pf-c-card__title--not--last-child--PaddingBottom: var(--pf-c-card--size-compact__title--not--last-child--PaddingBottom, var(--pf-global--spacer--sm, .5rem));
}
-:host([size="small"]) {
- @include pfe-print-local($size-small);
+:host([size="large"]) {
+ --pf-c-card__title--FontSize: var(--pf-c-card--size-large__title--FontSize, var(--pf-global--FontSize--xl, 1.25rem));
+ --_pf-c-card--first-child--PaddingTop: var(--pf-c-card--size-large--first-child--PaddingTop, var(--pf-global--spacer--xl, 2rem));
+ --_pf-c-card--child--PaddingRight: var(--pf-c-card--size-large--child--PaddingRight, var(--pf-global--spacer--xl, 2rem));
+ --_pf-c-card--child--PaddingBottom: var(--pf-c-card--size-large--child--PaddingBottom, var(--pf-global--spacer--xl, 2rem));
+ --_pf-c-card--child--PaddingLeft: var(--pf-c-card--size-large--child--PaddingLeft, var(--pf-global--spacer--xl, 2rem));
+ --_pf-c-card__title--not--last-child--PaddingBottom: var(--pf-c-card--size-large__title--not--last-child--PaddingBottom, var(--pf-global--spacer--lg, 1.5rem));
}
-:host([border]:not([border="false"])) {
- --pfe-card--BorderWidth: #{pfe-fetch(surface--border-width)};
+:host([flat]) {
+ --pf-c-card--BoxShadow: none;
+ border: var(--pf-c-card--m-flat--BorderWidth, var(--pf-global--BorderWidth--sm, 1px)) solid var(--pf-c-card--m-flat--BorderColor, var(--pf-global--BorderColor--100, #d2d2d2));
}
-// Targets the wrappers in the shadow DOM
-.pfe-card {
- &__header,
- &__body,
- &__footer {
- ::slotted([overflow~="top"]) {
- z-index: 1;
- margin-top: -2rem; //IE11 fallback
- margin-top: calc(-1 * #{pfe-local(PaddingTop)}) !important;
- :host(.has-header) & {
- padding-top: pfe-local(spacing);
- }
- }
-
- ::slotted([overflow~="right"]) {
- margin-right: -2rem; //IE11 fallback
- margin-right: calc(-1 * #{pfe-local(PaddingRight)});
- }
-
- ::slotted([overflow~="bottom"]) {
- margin-bottom: -2rem; //IE11 fallback
- margin-bottom: calc(-1 * calc(#{pfe-local(PaddingBottom)} + #{pfe-local(BorderRadius)}));
- align-self: flex-end;
- }
-
- ::slotted([overflow~="left"]) {
- margin-left: -2rem; //IE11 fallback
- margin-left: calc(-1 * #{pfe-local(PaddingLeft)});
- }
-
- ::slotted(img) {
- max-width: 100% !important;
- align-self: flex-start; //Don't stretch image 100% with other Flexbox items in card.
- object-fit: cover; // Fix distortion
- }
-
- ::slotted(img:not[overflow]) {
- align-self: flex-start; //Don't stretch image 100% with other Flexbox items in card.
- }
-
- ::slotted(img[overflow]) {
- max-width: unset !important;
- }
-
- ::slotted(img[overflow~="right"]) {
- width: calc(100% + 2rem) !important; //IE11 fallback
- width: calc(100% + #{pfe-local(PaddingRight)}) !important;
- }
-
- ::slotted(img[overflow~="left"]) {
- width: calc(100% + 2rem) !important; //IE11 fallback
- width: calc(100% + #{pfe-local(PaddingLeft)}) !important;
- }
-
- ::slotted(img[overflow~="right"][overflow~="left"]) {
- width: calc(100% + 4rem) !important; //IE11 fallback
- width: calc(100% + #{pfe-local(PaddingRight)} + #{pfe-local(PaddingLeft)}) !important;
- }
- }
- &__header {
- z-index: 2;
- // Declare the header background color
- background-color: pfe-local(BackgroundColor, $region: header);
- color: pfe-local($cssvar: Color, $region: header);
- :host([color-palette^="dark"]) & {
- // Declare the header background color
- background-color: pfe-local(BackgroundColor--dark, $region: header);
- }
-
- // Margin styles on header region
- margin-top: calc(#{pfe-local(PaddingTop)} * -1) !important;
- margin-right: calc(#{pfe-local(PaddingRight)} * -1);
- margin-bottom: pfe-local(spacing--vertical);
- margin-left: calc(#{pfe-local(PaddingLeft)} * -1);
-
- // Padding for the header region
- padding-top: pfe-local(spacing--vertical);
- padding-right: pfe-local(PaddingRight);
- padding-left: pfe-local(PaddingLeft);
- padding-bottom: pfe-local(spacing--vertical);
-
- &:not(.has-body,.has-footer) {
- margin-bottom: pfe-local(PaddingBottom);
- }
-
- ::slotted([overflow~="top"]) {
- --pfe-card__overflow--MarginTop: calc(#{pfe-local(PaddingTop)} * -1);
- }
-
- &:not(.has-header) {
- display: none;
- }
-
- &.has-body,&.has-footer ::slotted([overflow~="bottom"]) {
- --pfe-card__overflow--MarginBottom: calc(#{pfe-local(spacing--vertical)} * -1);
- }
-
- ::slotted([overflow~="bottom"]) {
- --pfe-card__overflow--MarginBottom: calc(#{pfe-local(PaddingBottom)} * -1);
- }
-
- @each $tag in (h1, h2, h3, h4, h5, h6) {
- ::slotted(#{$tag}) {
- margin-bottom: 0;
- }
- }
- }
- &__body {
- &:not(.has-header) ::slotted([overflow~="top"]) {
- --pfe-card__overflow--MarginTop: calc(#{pfe-local(PaddingTop)} * -1);
- }
-
- ::slotted([overflow~="top"]) {
- z-index: 1;
- --pfe-card__overflow--MarginTop: calc(#{pfe-local(spacing--vertical)} * -1);
- }
-
- ::slotted([overflow~="bottom"]) {
- --pfe-card__overflow--MarginBottom: calc(#{pfe-local(PaddingBottom)} * -1);
- }
-
- &.has-footer ::slotted([overflow~="bottom"]) {
- --pfe-card__overflow--MarginBottom: calc(#{pfe-local(spacing--vertical)} * -1);
- }
+:host([plain]) {
+ --pf-c-card--BoxShadow: var(--pf-c-card--m-plain--BoxShadow, none);
+ --pf-c-card--BackgroundColor: var(--pf-c-card--m-plain--BackgroundColor, transparent);
+}
- &:is(.has-header){
- ::slotted(img[overflow~="top"]){
- padding-top: pfe-local(spacing);
- }
- }
+:host([rounded]) {
+ border-radius: var(--pf-c-card--m-rounded--BorderRadius, var(--pf-global--BorderRadius--sm, 3px));
+}
- &:not(.has-footer) {
- margin-bottom: 0;
- }
- }
- &__footer {
- margin-top: auto; // This allows the footer to move to the very bottom
+:host([full-height]) {
+ height: var(--pf-c-card--m-full-height--Height, 100%);
+ --_pf-c-card__body--FullHeight--Flex: 1 1 auto;
+}
- display: flex;
- flex-direction: pfe-local(Row, row, $region: footer);
- flex-wrap: pfe-local(Wrap, wrap, $region: footer);
- // Aligns buttons and CTAs
- align-items: pfe-local(AlignItems, baseline, $region: footer);
+[part="header"],
+[part="body"],
+[part="footer"] {
+ padding-inline-start: var(--_pf-c-card--child--PaddingLeft, var(--pf-global--spacer--lg, 1.5rem));
+ padding-inline-end: var(--_pf-c-card--child--PaddingRight, var(--pf-global--spacer--lg, 1.5rem));
+ padding-block-end: var(--_pf-c-card--child--PaddingBottom, var(--pf-global--spacer--lg, 1.5rem));
+}
- ::slotted([overflow~="bottom"]) {
- --pfe-card__overflow--MarginBottom: calc(#{pfe-local(PaddingBottom)} * -1);
- }
+[part="body"] {
+ font-size: var(--_pf-c-card__body--FontSize, var(--pf-global--FontSize--md, 1rem));
+ flex: var(--_pf-c-card__body--FullHeight--Flex, initial);
+}
- &:not(.has-footer) {
- display: none;
- }
- }
- &__header,
- &__body {
- margin-bottom: pfe-local(spacing--vertical);
+header {
+ padding-block-start: var(--_pf-c-card--first-child--PaddingTop, var(--pf-global--spacer--lg, 1.5rem));
+ padding-block-end: var(--_pf-c-card__title--not--last-child--PaddingBottom, var(--pf-global--spacer--md, 1rem));
+}
- //-- Slotted styles for typography
- // Remove margins from typography inside the slots
- @each $tag in (p, h1, h2, h3, h4, h5, h6) {
- ::slotted(#{$tag}:first-child) {
- // Remove top margins
- margin-top: 0;
- }
- }
- }
+header ::slotted(*) {
+ font-family: var(--pf-c-card__title--FontFamily, var(--pf-global--FontFamily--heading--sans-serif, "RedHatDisplayUpdated", helvetica, arial, sans-serif));
+ font-size: var(--pf-c-card__title--FontSize, var(--pf-global--FontSize--md, 1rem));
+ font-weight: var(--pf-c-card__title--FontWeight, var(--pf-global--FontWeight--bold, 700));
+ margin-block: 0;
}
-#header ::slotted(:is(h1,h2,h3,h4,h5,h6)) {
- font-weight: var(--pfe-card-header-font-weight, 500);
+[part="footer"] {
+ font-size: var(--_pf-c-card__footer--FontSize, var(--pf-global--FontSize--md, 1rem));
}
+
diff --git a/elements/pfe-card/pfe-card.ts b/elements/pfe-card/pfe-card.ts
index 2aff589d9e..6505cdf4ac 100644
--- a/elements/pfe-card/pfe-card.ts
+++ b/elements/pfe-card/pfe-card.ts
@@ -1,19 +1,7 @@
-import type { ColorPalette, ColorTheme } from '@patternfly/pfe-core/controllers/color-context.js';
-
-import { LitElement, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
-import { classMap } from 'lit/directives/class-map.js';
-
-import {
- colorContextConsumer,
- colorContextProvider,
- deprecation,
- observed,
- pfelement
-} from '@patternfly/pfe-core/decorators.js';
-import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js';
import style from './pfe-card.scss';
+import { BaseCard } from './BaseCard';
/**
* This element creates a header, body, and footer region in which to place
@@ -27,123 +15,64 @@ import style from './pfe-card.scss';
* @slot - Any content that is not designated for the header or footer slot, will go to this slot.
* @slot footer
* Use this slot for anything that you want to be stuck to the base of the card.
- * This region is bottom-aligned.
- *
- * @slot pfe-card--header - {@deprecated use header}
- * @slot pfe-card--footer - {@deprecated use footer}
*
* @csspart header - The container for *header* content
* @csspart body - The container for *body* content
* @csspart footer - The container for *footer* content
*
*
- * @cssproperty {} --pfe-theme--color--surface--lightest {@default `#ffffff`}
- * @cssproperty {} --pfe-theme--color--surface--lighter {@default `#ececec`}
- * @cssproperty {} --pfe-theme--color--surface--base {@default `#f0f0f0`}
- * @cssproperty {} --pfe-theme--color--surface--darker {@default `#3c3f42`}
- * @cssproperty {} --pfe-theme--color--surface--darkest {@default `#151515`}
- * @cssproperty {} --pfe-theme--color--surface--accent {@default `#004080`}
- * @cssproperty {} --pfe-theme--color--surface--complement {@default `#002952`}
- *
- * @cssproperty --pfe-band--BackgroundColor
- * Though using the `color` attribute is strongly recommended when setting the background color for the band, you can also use completely custom colors by updating the `--pfe-band--BackgroundColor` variable. If you update this value manually, you should also update the `--theme` context variable to invoke the right theme on it and it's child elements. Supported themes include: `light`, `dark`, and `saturated`.
- * @cssproperty --pfe-card--BackgroundPosition
- * This is designed for use with the `img-src` attribute to allow you to align your background image. Default value is `center center`.
- * {@default `center center`}
- * @cssproperty --pfe-card--Border
- * This allows the customization of a border around the entire container.
- * {@default `center center`}
- * @cssproperty --pfe-card--Border
- * This allows the customization of a border around the entire container.
- * @cssproperty --pfe-card--BorderRadius
- * This allows the customization of a border radius around the entire container.
- * @cssproperty --pfe-card--BorderWeight
- * This allows the customization of a border weight around the entire container.
- * @cssproperty --pfe-card--BorderStyle
- * This allows the customization of a border style around the entire container.
- * @cssproperty --pfe-card--BorderColor
- * This allows the customization of a border color around the entire container.
+ * @cssproperty {} --pf-c-card--BackgroundColor {@default `#ffffff`}
+ * @cssproperty {} --pf-c-card--BoxShadow {@default `0 0.0625rem 0.125rem 0 rgba(3, 3, 3, 0.12), 0 0 0.125rem 0 rgba(3, 3, 3, 0.06)`}
+ * @cssproperty {} --pf-c-card--size-compact__body--FontSize {@default `.875rem`}
+ * @cssproperty {} --pf-c-card--size-compact__footer--FontSize {@default `1rem`}
+ * @cssproperty {} --pf-c-card--size-compact--first-child--PaddingTop {@default `1.5rem`}
+ * @cssproperty {} --pf-c-card--size-compact--child--PaddingRight {@default `1rem`}
+ * @cssproperty {} --pf-c-card--size-compact--child--PaddingBottom {@default `1rem`}
+ * @cssproperty {} --pf-c-card--size-compact--child--PaddingLeft {@default `1rem`}
+ * @cssproperty {} --pf-c-card--size-compact__title--not--last-child--PaddingBottom {@default `.5rem`}
+ * @cssproperty {} --pf-c-card--size-large__title--FontSize {@default `1.25rem`}
+ * @cssproperty {} --pf-c-card--size-large--first-child--PaddingTop {@default `2rem`}
+ * @cssproperty {} --pf-c-card--size-large--child--PaddingRight {@default `2rem`}
+ * @cssproperty {} --pf-c-card--size-large--child--PaddingBottom {@default `2rem`}
+ * @cssproperty {} --pf-c-card--size-large--child--PaddingLeft {@default `2rem`}
+ * @cssproperty {} --pf-c-card--size-large__title--not--last-child--PaddingBottom {@default `1.5rem`}
+ * @cssproperty {} --pf-c-card--m-flat--BorderWidth {@default `1px solid #d2d2d2`}
+ * @cssproperty {} --pf-c-card--m-plain--BoxShadow {@default `none`}
+ * @cssproperty {} --pf-c-card--m-plain--BackgroundColor {@default `transparent`}
+ * @cssproperty {} --pf-c-card--m-rounded--BorderRadius {@default `3px`}
+ * @cssproperty {} --pf-c-card--m-full-height--Height {@default `100%`}
+ * @cssproperty {} --pf-c-card__title--FontFamily {@default `"RedHatDisplayUpdated", helvetica, arial, sans-serif`}
+ * @cssproperty {} --pf-c-card__title--FontSize {@default `1rem`}
+ * @cssproperty {} --pf-c-card__title--FontWeight {@default `700`}
*/
-@customElement('pfe-card') @pfelement()
-export class PfeCard extends LitElement {
+@customElement('pfe-card')
+export class PfeCard extends BaseCard {
static readonly version = '{{version}}';
- static readonly styles = [style];
+ static readonly styles = [...BaseCard.styles, style];
/**
- * Optional background image applied to the entire card container.
- * Alignment of this image can be managed using the `--pfe-card--BackgroundPosition`
- * variable which is set to `center center` by default.
+ * Optionally provide a size for the card and the card contents.
+ * The default is set to `undefined` and provides default styles.
+ * Compact provides styles which decreases the padding between the sections.
+ * Large provides styles which increases the padding between the sections and the font size for the title, header, and footer.
*/
- @observed
- @property({ attribute: 'img-src', reflect: true }) imgSrc?: string;
+ @property({ reflect: true }) size?: 'compact' | 'large';
/**
- * Sets color palette, which affects the element's styles as well as descendants' color theme.
- * Overrides parent color context.
- * Your theme will influence these colors so check there first if you are seeing inconsistencies.
- * See [CSS Custom Properties](#css-custom-properties) for default values
- *
- * Card always resets its context to `base`, unless explicitly provided with a `color-palette`.
- */
- @colorContextProvider()
- @property({ reflect: true, attribute: 'color-palette' }) colorPalette?: ColorPalette = 'base';
-
- /** @deprecated use `color-palette` */
- @deprecation({ alias: 'colorPalette', attribute: 'color' }) color?: ColorPalette;
+ * Optionally apply a border radius for the drop shadow and/or border.
+ */
+ @property({ type: Boolean, reflect: true }) rounded = false;
/**
- * Sets color theme based on parent context
- */
- @colorContextConsumer()
- @property({ reflect: true }) on?: ColorTheme;
-
- /** Optionally adjusts the padding on the container. Accepts: `small`. */
- @property({ reflect: true }) size?: 'small';
+ * Optionally allow the card to take up the full height of the parent element.
+ */
+ @property({ type: Boolean, reflect: true, attribute: 'full-height' }) fullHeight = false;
/**
- * Optionally apply a border color and weight to the entire card container.
- * The default color and weight is `#d2d2d2` and `1px`, respectively.
+ * Optionally remove the border on the card container.
*/
- @property({ type: Boolean, reflect: true }) border = false;
-
- protected slots = new SlotController(this, {
- slots: ['header', null, 'footer'],
- deprecations: {
- header: 'pfe-card--header',
- footer: 'pfe-card--footer',
- }
- });
-
- render() {
- const classes = {
- 'has-header': this.slots.hasSlotted('header', 'pfe-card--header'),
- 'has-footer': this.slots.hasSlotted('footer', 'pfe-card--footer'),
- 'has-body': this.slots.hasSlotted(),
- };
-
- return html`
-
-
-
-
-
- `;
- }
-
- /** Update the background image */
- protected _imgSrcChanged(_oldValue: unknown, newValue: unknown) {
- if (typeof this.imgSrc === 'string') {
- // Set the image as the background image
- this.style.backgroundImage = newValue ? `url('${newValue}')` : ``;
- }
- }
+ @property({ type: Boolean, reflect: true }) plain = false;
}
declare global {
diff --git a/elements/pfe-card/test/pfe-card.spec.ts b/elements/pfe-card/test/pfe-card.spec.ts
index 1b7c0757b2..06b9c89b02 100644
--- a/elements/pfe-card/test/pfe-card.spec.ts
+++ b/elements/pfe-card/test/pfe-card.spec.ts
@@ -1,382 +1,181 @@
import { expect, html, aTimeout } from '@open-wc/testing';
import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js';
-import { hexToRgb, getColor } from '@patternfly/pfe-tools/test/hex-to-rgb.js';
-
+import { a11ySnapshot, findAccessibilityNode } from '@web/test-runner-commands';
import '@patternfly/pfe-tools/test/stub-logger.js';
import { PfeCard } from '@patternfly/pfe-card';
-/** Returns the luminance value from RGB */
-const luminance = (r: number, g: number, b: number): number => {
- return (0.2126 * r / 255 + 0.7152 * g / 255 + 0.0722 * b / 255);
-};
-
-const TEMPLATES = {
-
- card1: html`
-
- Card 1
- This is pfe-card.
- Text in footer
- `,
-
- card2: html`
-
-
- `,
-
- card3: html`
-
- Card 3
-
- `,
-
- card4: html`
-
- Card 4
- This is pfe-card.
- Text in footer
- `,
-
-};
-
-const dynamicHeaderFooter = html`
- `;
-
describe('', function() {
it('should upgrade', async function() {
- expect(await createFixture(TEMPLATES.card1))
+ expect(await createFixture(html` `))
.to.be.an.instanceof(customElements.get('pfe-card'))
.and
.to.be.an.instanceof(PfeCard);
});
- it(`should render a header and footer when content for those slots are added dynamically`, async function() {
- const element = await createFixture(dynamicHeaderFooter);
-
- const header = document.createElement('h2');
- header.setAttribute('slot', 'header');
- header.textContent = 'Card Header';
-
- const footer = document.createElement('div');
- footer.setAttribute('slot', 'footer');
- footer.textContent = 'This is the footer';
-
- element.prepend(header);
- element.appendChild(footer);
-
- await aTimeout(50);
-
- const cardHeaderSlot =
- element.shadowRoot!.querySelector('slot[name="header"]')!;
- const cardFooterSlot =
- element.shadowRoot!.querySelector('slot[name="footer"]')!;
-
- await aTimeout(50);
-
- expect(cardHeaderSlot.assignedNodes().length).to.equal(1);
- expect(cardFooterSlot.assignedNodes().length).to.equal(1);
- });
-
- it(`should render a header and footer when content for those deprecated slots are added dynamically`, async function() {
- const element = await createFixture(dynamicHeaderFooter);
-
- const header = document.createElement('h2');
- header.setAttribute('slot', 'pfe-card--header');
- header.textContent = 'Card Header';
-
- const footer = document.createElement('div');
- footer.setAttribute('slot', 'pfe-card--footer');
- footer.textContent = 'This is the footer';
-
- element.prepend(header);
- element.appendChild(footer);
-
- await element.updateComplete;
-
- const cardHeaderSlot =
- element.shadowRoot!.querySelector('slot[name="pfe-card--header"]')!;
- const cardFooterSlot =
- element.shadowRoot!.querySelector('slot[name="pfe-card--footer"]')!;
-
- await element.updateComplete;
+ describe('with header and footer content', function() {
+ let element: PfeCard;
+ let origHeight: number;
- expect(cardHeaderSlot.assignedNodes().length).to.equal(1);
- expect(cardFooterSlot.assignedNodes().length).to.equal(1);
- });
+ beforeEach(async function() {
+ element = await createFixture(html`
+
+ Card 1
+ This is pfe-card.
+ Text in footer
+ `);
+ });
- // Iterate over the colors object to test expected background color results
- describe('color palettes', function() {
- // Themes and their expected hex values
- Object.entries({
- default: '#f0f0f0',
- darker: '#3c3f42',
- darkest: '#151515',
- accent: '#004080',
- complement: '#002952',
- lightest: '#ffffff',
- }).forEach(([colorName, colorValue]) => {
- it(`it should have a background color of ${colorValue} when color-palette is ${colorName}`, async function() {
- const element = await createFixture(TEMPLATES.card1);
- // If this is not the default color, update the color attribute
- if (colorName !== 'default') {
- element.setAttribute('color-palette', colorName);
- }
+ beforeEach(function() {
+ const { height } = element.getBoundingClientRect();
+ origHeight = height;
+ });
- // Get the background color value
- const [r, g, b] = getColor(element, 'background-color');
- // Test that the color is rendering as expected
- expect([r, g, b]).to.deep.equal(hexToRgb(colorValue));
- // Test that the color is working
- if (['dark', 'darker', 'darkest', 'complement', 'accent'].includes(colorName)) {
- expect(luminance(r, g, b)).to.be.lessThan(0.5);
- } else {
- expect(luminance(r, g, b)).to.be.greaterThan(0.5);
- }
+ describe('size', function() {
+ describe('unset', function() {
+ it('should have default sizing for card parts', function() {
+ expect(getComputedStyle(element.querySelector('p')!, 'body font-size').getPropertyValue('font-size')).to.equal('16px');
+ expect(getComputedStyle(element.querySelector('[slot=footer]')!, 'footer font-size').getPropertyValue('font-size')).to.equal('16px');
+ });
});
- it(`it should have a background color of ${colorValue} when deprecated color attribute is ${colorName}`, async function() {
- const element = await createFixture(TEMPLATES.card1);
- // If this is not the default color, update the color attribute
- if (colorName !== 'default') {
- element.setAttribute('color', colorName);
- }
-
- await element.updateComplete;
+ describe('compact', function() {
+ beforeEach(async function() {
+ element.setAttribute('size', 'compact');
+ });
+ it('should be smaller', function() {
+ const { height } = element.getBoundingClientRect();
+ expect(origHeight, 'height').to.be.greaterThan(height);
+ });
+ });
- // Get the background color value
- const [r, g, b] = getColor(element, 'background-color');
- // Test that the color is rendering as expected
- expect([r, g, b]).to.deep.equal(hexToRgb(colorValue));
- // Test that the color is working
- if (['dark', 'darker', 'darkest', 'complement', 'accent'].includes(colorName)) {
- expect(luminance(r, g, b)).to.be.lessThan(0.5);
- } else {
- expect(luminance(r, g, b)).to.be.greaterThan(0.5);
- }
+ describe('large', function() {
+ beforeEach(async function() {
+ element.setAttribute('size', 'large');
+ });
+ it('should have larger font sizes for body, footer, more padding for header', function() {
+ const { height } = element.getBoundingClientRect();
+ expect(origHeight, 'height').to.be.lessThan(height);
+ expect(getComputedStyle(element.querySelector('p')!, 'body font-size').getPropertyValue('font-size'))
+ .to.equal('16px');
+ expect(getComputedStyle(element.querySelector('[slot=footer]')!, 'footer font-size').getPropertyValue('font-size'))
+ .to.equal('16px');
+ });
});
});
- });
-
- it('should have standard padding when size is not set', async function() {
- const element = await createFixture(TEMPLATES.card1);
- expect(getComputedStyle(element).getPropertyValue('padding')).to.equal('32px');
- });
-
- it('should have reduced padding when size is small', async function() {
- const element = await createFixture(TEMPLATES.card1);
- element.setAttribute('size', 'small');
- expect(getComputedStyle(element).getPropertyValue('padding')).to.equal('16px');
- element.removeAttribute('size');
- });
- it('should have a standard border when border is set', async function() {
- const element = await createFixture(TEMPLATES.card1);
- element.setAttribute('border', '');
-
- expect(getColor(element, 'border-left-color')).to.deep.equal(hexToRgb('#d2d2d2'));
- expect(Math.round(parseFloat(getComputedStyle(element).getPropertyValue('border-left-width'))))
- .to.equal(1);
- });
-
- // Iterate over the slots object to test expected results
- describe('slots', function() {
- Object.entries({
- header: {
- name: 'header',
- class: 'pfe-card__header',
- content: 'Card 1',
- },
- body: {
- name: undefined,
- class: 'pfe-card__body',
- content: 'This is pfe-card.',
- },
- footer: {
- name: 'footer',
- class: 'pfe-card__footer',
- content: 'Text in footer',
- },
- }).forEach(([title, config]) => {
- it(`${title} content is placed into correct slot`, async function() {
- const element = await createFixture(TEMPLATES.card1);
- const selector = title !== 'body' ? `[slot=${config.name}]` : 'p';
- expect(element.querySelector(selector)!.assignedSlot)
- .to.equal(element.shadowRoot!.querySelector(`.${config.class} > *`) );
+ describe('rounded', function() {
+ describe('unset', function() {
+ it('should have default border radius', function() {
+ expect(getComputedStyle(element).getPropertyValue('border-radius')).to.equal('0px');
+ });
+ });
- const content = element.shadowRoot!.querySelector(`.${config.class} > *`)!
- .assignedNodes()
- .map(n => n.textContent)
- .join('')
- .trim();
- expect(content).to.equal(config.content);
+ describe('is set', function() {
+ it('should have 3px border radius', async function() {
+ element.setAttribute('rounded', '');
+ await element.updateComplete;
+ expect(getComputedStyle(element).getPropertyValue('border-radius')).to.equal('3px');
+ });
});
});
- });
- describe('deprecated slots', function() {
- let element: PfeCard|undefined;
+ describe('flat', function() {
+ describe('unset', function() {
+ it('should have default box shadow and border (none)', function() {
+ expect(getComputedStyle(element).getPropertyValue('box-shadow')).not.to.equal('none');
+ expect(getComputedStyle(element).getPropertyValue('border')).to.equal('0px none rgb(0, 0, 0)');
+ });
+ });
- beforeEach(async function() {
- element = await createFixture(html`
-
- Deprecated header
- This is pfe-card.
- Deprecated footer
- `);
- });
+ describe('is set', function() {
+ it('should not have a box shadow and should have an added border', async function() {
+ element.setAttribute('flat', '');
+ await element.updateComplete;
- afterEach(function() {
- element = undefined;
+ expect(getComputedStyle(element).getPropertyValue('box-shadow')).to.equal('none');
+ expect(getComputedStyle(element).getPropertyValue('border')).not.to.equal('none');
+ });
+ });
});
- Object.entries({
- header: {
- name: 'pfe-card--header',
- class: 'pfe-card__header',
- content: 'Deprecated header',
- },
- body: {
- name: undefined,
- class: 'pfe-card__body',
- content: 'This is pfe-card.',
- },
- footer: {
- name: 'pfe-card--footer',
- class: 'pfe-card__footer',
- content: 'Deprecated footer',
- },
- }).forEach(([title, config]) => {
- it(`${title} content is placed into correct slot`, async function() {
- const selector = title !== 'body' ? `[slot="${config.name}"]` : 'p';
-
- const slotSelector = config.name ? `.${config.class} > [name="${config.name}"]` : `.${config.class} > slot`;
-
- expect(element!.querySelector(selector)!.assignedSlot)
- .to.equal(element!.shadowRoot!.querySelector(slotSelector));
+ describe('full-height', function() {
+ describe('is not set', function() {
+ it('should not be taller', async function() {
+ const { height } = element.getBoundingClientRect();
+ expect(height).to.equal(origHeight);
+ });
+ });
- const content = element!.shadowRoot!
- .querySelector(slotSelector)!
- .assignedNodes()
- .map(n => n.textContent)
- .join('')
- .trim();
+ describe('is set', function() {
+ beforeEach(async function() {
+ element.toggleAttribute('full-height', true);
+ await element.updateComplete;
+ });
- expect(content).to.equal(config.content);
+ it('should be taller', async function() {
+ const { height } = element.getBoundingClientRect();
+ expect(height).to.be.greaterThan(origHeight);
+ });
});
});
- });
- // Iterate over possibilities for images
- describe('overflow', function() {
- ['top', 'right', 'bottom', 'left'].forEach(direction => {
- it(`image should overflow to the ${direction}`, async function() {
- const element = await createFixture(TEMPLATES.card2);
- const image = element.querySelector('img')!;
- image.setAttribute('overflow', direction);
- await aTimeout(50);
- const margin = direction !== 'bottom' ? '-32px' : '-35px';
- expect(getComputedStyle(image).getPropertyValue(`margin-${direction}`)).to.equal(margin);
+ describe('plain', function() {
+ describe('unset', function() {
+ it('should have the default box shadow and background-color', function() {
+ expect(getComputedStyle(element).getPropertyValue('box-shadow')).not.to.be.undefined;
+ expect(getComputedStyle(element).getPropertyValue('background-color')).to.equal('rgb(255, 255, 255)');
+ });
+ });
+ describe('is set', function() {
+ it('should have transparent background color and no box shadow', async function() {
+ element.setAttribute('plain', '');
+ expect(getComputedStyle(element).getPropertyValue('box-shadow')).to.equal('none');
+ expect(getComputedStyle(element).getPropertyValue('background-color')).to.equal('rgba(0, 0, 0, 0)');
+ });
});
});
- it('image should overflow all padding', async function() {
- const element = await createFixture(TEMPLATES.card3);
- const image = element.querySelector('img')!;
- expect(image.getAttribute('overflow')).to.equal('top right bottom left');
- const style = getComputedStyle(image);
- await aTimeout(50);
- expect(style.getPropertyValue('margin-top'), 'margin-top').to.equal('-32px');
- expect(style.getPropertyValue('margin-right'), 'margin-right').to.equal('-32px');
- expect(style.getPropertyValue('margin-bottom'), 'margin-bottom').to.equal('-35px');
- expect(style.getPropertyValue('margin-left'), 'margin-left').to.equal('-32px');
+ describe('accessible', function() {
+ it('should be accessible in both the light and shadow dom', async function() {
+ expect(element).to.be.accessible();
+ });
+
+ it.skip('should have an article element wrapper in the shadow dom', async function() {
+ const snapshot = await a11ySnapshot();
+ const foundNode = findAccessibilityNode(snapshot, node => node.role === 'WebArea');
+ expect(foundNode, 'A node with the supplied name has been found.').to.not.be.null;
+ });
});
});
- // Iterate over the custom properties to test overrides work
- describe('custom properties', function() {
- Object.values({
- paddingTop: {
- variable: '--pfe-card--PaddingTop',
- css: 'padding-top',
- default: '32px',
- custom: '28px',
- },
- paddingRight: {
- variable: '--pfe-card--PaddingRight',
- css: 'padding-right',
- default: '32px',
- custom: '28px',
- },
- paddingBottom: {
- variable: '--pfe-card--PaddingBottom',
- css: 'padding-bottom',
- default: '32px',
- custom: '28px',
- },
- paddingLeft: {
- variable: '--pfe-card--PaddingLeft',
- css: 'padding-left',
- default: '32px',
- custom: '28px',
- },
- padding: {
- variable: '--pfe-card--Padding',
- css: 'padding',
- default: '32px',
- custom: '20px 10px',
- },
- borderRadius: {
- variable: '--pfe-card--BorderRadius',
- css: 'border-radius',
- default: '3px',
- custom: '50%',
- },
- border: {
- variable: '--pfe-card--Border',
- css: 'border',
- default: '0px solid rgb(210, 210, 210)',
- custom: '1px solid #eee',
- customExpected: '1px solid rgb(238, 238, 238)',
- },
- backgroundColor: {
- variable: '--pfe-card--BackgroundColor',
- css: 'background-color',
- default: '#dfdfdf',
- defaultExpected: 'rgb(240, 240, 240)',
- custom: 'hotpink',
- customExpected: 'rgb(255, 105, 180)',
- },
- }).forEach(async property => {
- describe(`${property.variable}`, function() {
- let element: PfeCard;
- let style: CSSStyleDeclaration;
- beforeEach(async function() {
- element = await createFixture(TEMPLATES.card1);
- style = getComputedStyle(element);
- await aTimeout(50);
- });
+ it(`should render a header and footer when content for those slots are added dynamically`, async function() {
+ const element = await createFixture(html`
+ `);
- it('Uses expected default', async function() {
- const actual = style.getPropertyValue(property.css);
- const expected = property.defaultExpected ?? property.default;
- expect(actual).to.equal(expected);
- });
+ const header = document.createElement('h2');
+ header.setAttribute('slot', 'header');
+ header.textContent = 'Card Header';
+
+ const footer = document.createElement('div');
+ footer.setAttribute('slot', 'footer');
+ footer.textContent = 'This is the footer';
- it('Allows user customization', async function() {
- // Update the variable
- element.style.setProperty(property.variable, property.custom);
+ element.prepend(header);
+ element.appendChild(footer);
- // Test the update worked
- await aTimeout(50);
+ await aTimeout(50);
- const actual = style.getPropertyValue(property.css);
- const expected = property.customExpected ?? property.custom;
- expect(actual).to.equal(expected);
- });
- });
- });
+ const cardHeaderSlot =
+ element.shadowRoot!.querySelector('slot[name="header"]')!;
+ const cardFooterSlot =
+ element.shadowRoot!.querySelector('slot[name="footer"]')!;
+
+ await aTimeout(50);
+
+ expect(cardHeaderSlot.assignedNodes().length).to.equal(1);
+ expect(cardFooterSlot.assignedNodes().length).to.equal(1);
});
});
diff --git a/web-test-runner.config.js b/web-test-runner.config.js
index 8afea64ce2..3d2b182a3f 100644
--- a/web-test-runner.config.js
+++ b/web-test-runner.config.js
@@ -1,5 +1,6 @@
import { pfeTestRunnerConfig } from '@patternfly/pfe-tools/test-runner.js';
import { fakePrismModule } from './web-dev-server.config.js';
+import { a11ySnapshotPlugin } from '@web/test-runner-commands/plugins';
export default pfeTestRunnerConfig({
tsconfig: 'tsconfig.settings.json',
@@ -8,5 +9,6 @@ export default pfeTestRunnerConfig({
// reporter: 'default',
plugins: [
fakePrismModule(),
+ a11ySnapshotPlugin()
],
});