diff --git a/.changeset/calm-fireants-kneel.md b/.changeset/calm-fireants-kneel.md new file mode 100644 index 0000000000..f982e50a94 --- /dev/null +++ b/.changeset/calm-fireants-kneel.md @@ -0,0 +1,11 @@ +--- +"@rhds/elements": minor +--- + +✨ Added + +Health index grades the health or security level of something. + +```html +A +``` diff --git a/docs/_data/relatedItems.yaml b/docs/_data/relatedItems.yaml index 08ae0dc52d..32c0f834e3 100644 --- a/docs/_data/relatedItems.yaml +++ b/docs/_data/relatedItems.yaml @@ -41,6 +41,10 @@ rh-footer: - rh-popover - rh-tooltip - rh-site-status +rh-health-index: + - rh-avatar + - rh-badge + - rh-tag rh-jump-links: - rh-pagination - rh-progress-steps diff --git a/docs/_data/repoStatus.yaml b/docs/_data/repoStatus.yaml index 55c08846a7..126af55267 100644 --- a/docs/_data/repoStatus.yaml +++ b/docs/_data/repoStatus.yaml @@ -148,13 +148,24 @@ libraries: - name: Figma library status: Ready - - name: RH Elements status: Ready - name: WebRH status: Ready - name: Documentation status: Ready +- name: "Health Index" + type: "Element" + overallStatus: "Available" + libraries: + - name: Figma library + status: Ready + - name: RH Elements + status: Ready + - name: WebRH + status: Planned + - name: Documentation + status: Ready - name: "Jumplinks" type: "Element" overallStatus: "Available" diff --git a/docs/_includes/partials/accessibility/1.1.1-A.md b/docs/_includes/partials/accessibility/1.1.1-A.md new file mode 100644 index 0000000000..f5581fcf2a --- /dev/null +++ b/docs/_includes/partials/accessibility/1.1.1-A.md @@ -0,0 +1 @@ +- [SC 1.1.1 Non-text content](https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html) (Level A) \ No newline at end of file diff --git a/elements/rh-health-index/README.md b/elements/rh-health-index/README.md new file mode 100644 index 0000000000..0bcbf525ee --- /dev/null +++ b/elements/rh-health-index/README.md @@ -0,0 +1,37 @@ +# Health Index +Health index grades the health or security level of something. + +## Usage +Use the Health Index element to display a health or security grade (A–F) of something. + +At a minimum an `rh-health-index` element should have a `grade=""` attribute with a value of A–F along with a matching grade letter in the slot. + +### Default +```html +A +``` + +### Sizes + +- Small = `sm` +- Default (i.e., Medium) = `md` +- Large = `lg` +- Extra Large = `xl` + +#### Small + +```html +A +``` + +#### Large + +```html +A +``` + +#### Extra Large + +```html +A +``` diff --git a/elements/rh-health-index/demo/color-context.html b/elements/rh-health-index/demo/color-context.html new file mode 100644 index 0000000000..7ec85e1571 --- /dev/null +++ b/elements/rh-health-index/demo/color-context.html @@ -0,0 +1,73 @@ + + + + + + + \ No newline at end of file diff --git a/elements/rh-health-index/demo/rh-health-index.html b/elements/rh-health-index/demo/rh-health-index.html new file mode 100644 index 0000000000..feab91f110 --- /dev/null +++ b/elements/rh-health-index/demo/rh-health-index.html @@ -0,0 +1,6 @@ +C + + diff --git a/elements/rh-health-index/demo/screen-readers.html b/elements/rh-health-index/demo/screen-readers.html new file mode 100644 index 0000000000..abd1835829 --- /dev/null +++ b/elements/rh-health-index/demo/screen-readers.html @@ -0,0 +1,61 @@ +
+

<rh-health-index> element

+ A + B + C + D + E + F +
+ +
+

ARIA attrs for l10n

+ A +
+ +
+

Native >meter> element

+ + + + + + +
+ +
+

ARIA-attributes >meter> element

+
A
+
B
+
C
+
D
+
E
+
F
+
+ + + + + + diff --git a/elements/rh-health-index/demo/variants.html b/elements/rh-health-index/demo/variants.html new file mode 100644 index 0000000000..1d7dd26bee --- /dev/null +++ b/elements/rh-health-index/demo/variants.html @@ -0,0 +1,69 @@ + + + + + + + diff --git a/elements/rh-health-index/docs/00-overview.md b/elements/rh-health-index/docs/00-overview.md new file mode 100644 index 0000000000..41daf04670 --- /dev/null +++ b/elements/rh-health-index/docs/00-overview.md @@ -0,0 +1,35 @@ +## Overview + +{{ tagName | getElementDescription }} + + + Four health index components. One is green with letter grade A, one is yellow with letter grade C, one is orange with letter grade D, and one is red with letter grade F. + + + +{% repoStatusList %} + +## Sample element + +A + + +## Demos + +View a live version of this component and see how it can be customized. + +{% playground tagName=tagName %}{% endplayground %} + +Full screen demo + + +## When to use + +- When you need to use severity to communicate the health of something +- When you need to communicate how secure or vulnerable something is +- When you need to measure how current or out of date something is + + + +{% repoStatusChecklist %} + diff --git a/elements/rh-health-index/docs/10-style.md b/elements/rh-health-index/docs/10-style.md new file mode 100644 index 0000000000..3449a86478 --- /dev/null +++ b/elements/rh-health-index/docs/10-style.md @@ -0,0 +1,231 @@ + + +## Style + +Health index is a combination of letter grades and severity colors. Severity colors communicate a positive or negative status. For example, green has a positive status and red has a negative status. To meet accessibility standards, letter grades are also used so that color is not the only element communicating the health of something. Health index does not include any interactive elements. + +### Anatomy + +
+ + Anatomy of 3 health index components. Annotation #1 is pointing to the letter grade and annotation #2 is pointing to the severity level. + +
+
    +
  1. Letter grade
  2. +
  3. Severity level
  4. +
+
+
+ + +### Variants + +There are four available variants: `Small`, `Default`, `Large`, and `Extra large`. The only difference between the Large and Extra large variants is the size. + + + 4 columns of health index components. Each column is a different size. Under each column, there is every letter grade and severity color. + + + +## Theme + +Health index is available in both light and dark themes. + +### Light theme + + + Light theme health index component examples. + + + + Light theme health index component examples. + + + + Light theme health index component examples. + + + +### Dark theme + + + Dark theme health index component examples. + + + + Dark theme health index component examples. + + + + Dark theme health index component examples. + + + +## Configuration + +Squares in the Default, Large, and Extra large variants are aligned horizontally. + + + 3 examples of health index component configurations. The small size is just 1 square, so it’s horizontally and vertically centered. The default, large, and extra large sizes are rows of squares, so they’re horizontally centered only. + + + +## Space + +The spacer in the Default variant is the same for all viewport sizes. + + + Health index component with a 16px spacer box in between a C letter grade and a row of severity squares. The active severity square is yellow. + + + +## States + +### Severity level + +Depending on the theme and chosen variant, each severity level can have different colors and font styling. + + + Multiple light theme health index components at different sizes. Annotations 1 through 4 are pointing to various styling details as well as letter grades that have been enlarged. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Annotations of the health index component's light theme examples in the figure above
#VariantFont weightText colorFill colorStroke/Emphasis color
1SmallRegulargreen-70green-20Stroke: green-60
2DefaultRegulargray-95N/AN/A
3LargeMediumgray-95red-10 +
    +
  • Stroke: red-70
  • +
  • Emphasis: red-70
  • +
+
4Extra largeMediumgray-95red-10 +
    +
  • Stroke: red-70
  • +
  • Emphasis: red-70
  • +
+
+
+ + + + Multiple dark theme health index components at different sizes. Annotations 1 through 4 are pointing to various styling details as well as letter grades that have been enlarged.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Annotations of the health index component's dark theme examples in the figure above
#VariantFont weightText colorFill colorStroke/Emphasis color
1MiniRegularwhitegray-95Stroke: green-60
2DefaultRegularwhiteN/AN/A
3LargeMediumwhitegray-95 +
    +
  • Stroke: red-40
  • +
  • Emphasis: red-60
  • +
+
4Extra largeMediumwhitegray-95 +
    +
  • Stroke: red-40
  • +
  • Emphasis: red-60
  • +
+
+
+ + +## Interactive + +Health index includes only text and is not interactive. \ No newline at end of file diff --git a/elements/rh-health-index/docs/20-guidelines.md b/elements/rh-health-index/docs/20-guidelines.md new file mode 100644 index 0000000000..65672c7a1a --- /dev/null +++ b/elements/rh-health-index/docs/20-guidelines.md @@ -0,0 +1,167 @@ + + +## Usage + +Use health index to communicate the health or security of something using letter grades and severity levels. + +## Variants + +There are four available variants: `Small`, `Default`, `Large`, and `Extra large`. Each variant has an associated level of emphasis. + + + + A row of health index components with emphasis descriptions under each size. Small is low, default is medium, large is high, and extra large is highest. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
When to use each variant of health index
VariantEmphasisUse case
SmallLowUse to quickly and simply display a health grade if there is lots of text or other content nearby
DefaultMediumUse to display a health grade without showing other possible colors and grades
LargeHighUse to display a health grade while showing other possible colors and grades
Extra LargeHighestUse to display a health grade while showing other possible colors and grades
+
+ + +### Severity levels + +When health index shows a grade of “A” and the severity level color is green, the experience associated with the index is healthy or in its optimal state. It is important to note that both the letter grade and color are included as indicators, as you should not rely solely on color. + + +

Helpful tip

+

The Ecosystem Catalog team is using a health index to grade the security of their container images. Read their documentation to learn more about how they are calculating grades.

+
+ + + + A 3 by 3 grid of small size health index components. Each row has letter grades A, C, and F. Under each letter grade is text that describes a safe, at risk, or vulnerable state. + + + +## Layout + +Health index should always be placed near helpful text or other content that explains it. This context should be available to all users, including those using assistive technologies. Without context, a user might interpret health index only as colored boxes with letters. + + +

Warning

+

Do not use health index without text or other contextual content.

+
+ + +### Stacked + +Health index can be stacked with other content. In such cases, we recommend using the Small variant, as it does not take focus away from surrounding information. + + + A small size health index component with a letter grade A at the bottom of a stack of text. + + + +### Inline + +Health index can also be inserted inline with text and components. In such cases, we recommend using the Large or Extra large variants to maintain equal hierarchy among elements. + + + An extra large size health index component with a letter grade F in between a heading, body text, and a call to action. + + + +## Best practices + +### Letter grades and color consistency + +Letter grades and severity level colors are designed to work together. Mixing them up will cause users to be confused as to what the health of something actually is. + +
+ + + Small, default, and large size health index components displaying correct letter grades and severity colors. + +

Keep letter grades and severity level colors consistent.

+
+ + + + Small, default, and large size health index components displaying incorrect letter grades and severity colors. + +

Do not mix up letter grades and severity level colors.

+
+
+ + +### Theming + +Light theme components are designed only to work in the light theme, and dark theme components are designed to work only in the dark theme. + +
+ + + Dark theme small, default, and large size health index components. + +

Use the correct components in the correct theme.

+
+ + + + Light theme small, default, and large size health index components used incorrectly in the dark theme. + +

Do not use components from one theme in another theme.

+
+
+ + +### Mixing variants + +Each variant is unique and designed to meet a specific user need. + +
+ + + Small, default, and large size health index components displaying correct letter grades and severity colors. + +

Use the available variants as intended.

+
+ + + + Large size health index component displaying extra text and an incorrect combination of styles. + +

Do not combine variants or add other elements like text.

+
+
\ No newline at end of file diff --git a/elements/rh-health-index/docs/30-code.md b/elements/rh-health-index/docs/30-code.md new file mode 100644 index 0000000000..7993ce3cd6 --- /dev/null +++ b/elements/rh-health-index/docs/30-code.md @@ -0,0 +1,3 @@ +{% renderInstall %}{% endrenderInstall %} + +{% renderCodeDocs hideDescription=true %}{% endrenderCodeDocs %} diff --git a/elements/rh-health-index/docs/40-accessibility.md b/elements/rh-health-index/docs/40-accessibility.md new file mode 100644 index 0000000000..beaf272008 --- /dev/null +++ b/elements/rh-health-index/docs/40-accessibility.md @@ -0,0 +1,33 @@ +## Implementation + +Most accessibility concerns should be covered by implementing health index per +our guidelines. You can double-check your implementation by ensuring the +following: + +- Health index should not be interactive or keyboard-focusable +- Health index should be placed in a context that makes its purpose clear to all +users—with and without assistive technologies +- Health index should communicate its grade both visually and to assistive tech + +Test your experience with a keyboard and [screen reader][sr] to help verify +these accessibility conformance checks. `` should present to +assistive technology using the [meter role][meter] and corresponding ARIA +attributes to communicate to assistive technology. Inspect this element using +the [DevTools Accessibility Tree][chrome-a11y-dev] to see each value. + +## ARIA Authoring Practices Guide (APG) + +Learn to use the accessibility semantics defined by the [Accessible Rich +Internet Application][aria] (ARIA) specification to create accessible web +experiences. + +View APG resources + +{% include 'partials/accessibility/wcag.md' %} +{% include 'partials/accessibility/1.1.1-A.md' %} +{% include 'partials/accessibility/1.4.1-A.md' %} + +[sr]: /accessibility/qa-testing/#screen-readers +[meter]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/meter_role +[chrome-a11y-dev]: https://developer.chrome.com/docs/devtools/accessibility/reference#pane +[aria]: https://www.w3.org/WAI/ diff --git a/elements/rh-health-index/docs/guidelines-best-practice-1-do.png b/elements/rh-health-index/docs/guidelines-best-practice-1-do.png new file mode 100644 index 0000000000..6343b41e41 Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-best-practice-1-do.png differ diff --git a/elements/rh-health-index/docs/guidelines-best-practice-1-dont.png b/elements/rh-health-index/docs/guidelines-best-practice-1-dont.png new file mode 100644 index 0000000000..20f4725ad8 Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-best-practice-1-dont.png differ diff --git a/elements/rh-health-index/docs/guidelines-best-practice-2-do.png b/elements/rh-health-index/docs/guidelines-best-practice-2-do.png new file mode 100644 index 0000000000..3f15d942a2 Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-best-practice-2-do.png differ diff --git a/elements/rh-health-index/docs/guidelines-best-practice-2-dont.png b/elements/rh-health-index/docs/guidelines-best-practice-2-dont.png new file mode 100644 index 0000000000..6343b41e41 Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-best-practice-2-dont.png differ diff --git a/elements/rh-health-index/docs/guidelines-best-practice-3-do.png b/elements/rh-health-index/docs/guidelines-best-practice-3-do.png new file mode 100644 index 0000000000..ebb7954be6 Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-best-practice-3-do.png differ diff --git a/elements/rh-health-index/docs/guidelines-best-practice-3-dont.png b/elements/rh-health-index/docs/guidelines-best-practice-3-dont.png new file mode 100644 index 0000000000..d67a142cae Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-best-practice-3-dont.png differ diff --git a/elements/rh-health-index/docs/guidelines-layout-inline.png b/elements/rh-health-index/docs/guidelines-layout-inline.png new file mode 100644 index 0000000000..a4c519d2ee Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-layout-inline.png differ diff --git a/elements/rh-health-index/docs/guidelines-layout-stacked.png b/elements/rh-health-index/docs/guidelines-layout-stacked.png new file mode 100644 index 0000000000..2c059dcf2f Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-layout-stacked.png differ diff --git a/elements/rh-health-index/docs/guidelines-severity-levels.png b/elements/rh-health-index/docs/guidelines-severity-levels.png new file mode 100644 index 0000000000..1986196c9a Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-severity-levels.png differ diff --git a/elements/rh-health-index/docs/guidelines-variants.png b/elements/rh-health-index/docs/guidelines-variants.png new file mode 100644 index 0000000000..3cb38a7810 Binary files /dev/null and b/elements/rh-health-index/docs/guidelines-variants.png differ diff --git a/elements/rh-health-index/docs/overview.png b/elements/rh-health-index/docs/overview.png new file mode 100644 index 0000000000..9667fe40d4 Binary files /dev/null and b/elements/rh-health-index/docs/overview.png differ diff --git a/elements/rh-health-index/docs/screenshot.png b/elements/rh-health-index/docs/screenshot.png new file mode 100644 index 0000000000..91ffbfd678 Binary files /dev/null and b/elements/rh-health-index/docs/screenshot.png differ diff --git a/elements/rh-health-index/docs/style-anatomy.png b/elements/rh-health-index/docs/style-anatomy.png new file mode 100644 index 0000000000..730ac2e24d Binary files /dev/null and b/elements/rh-health-index/docs/style-anatomy.png differ diff --git a/elements/rh-health-index/docs/style-configuration.png b/elements/rh-health-index/docs/style-configuration.png new file mode 100644 index 0000000000..5afa5bbfe4 Binary files /dev/null and b/elements/rh-health-index/docs/style-configuration.png differ diff --git a/elements/rh-health-index/docs/style-severity-level-dark.png b/elements/rh-health-index/docs/style-severity-level-dark.png new file mode 100644 index 0000000000..c896ac6bf0 Binary files /dev/null and b/elements/rh-health-index/docs/style-severity-level-dark.png differ diff --git a/elements/rh-health-index/docs/style-severity-level-light.png b/elements/rh-health-index/docs/style-severity-level-light.png new file mode 100644 index 0000000000..9ef6c28581 Binary files /dev/null and b/elements/rh-health-index/docs/style-severity-level-light.png differ diff --git a/elements/rh-health-index/docs/style-space.png b/elements/rh-health-index/docs/style-space.png new file mode 100644 index 0000000000..7902257a42 Binary files /dev/null and b/elements/rh-health-index/docs/style-space.png differ diff --git a/elements/rh-health-index/docs/style-theme-dark.png b/elements/rh-health-index/docs/style-theme-dark.png new file mode 100644 index 0000000000..5921451dd3 Binary files /dev/null and b/elements/rh-health-index/docs/style-theme-dark.png differ diff --git a/elements/rh-health-index/docs/style-theme-light.png b/elements/rh-health-index/docs/style-theme-light.png new file mode 100644 index 0000000000..b865e91bb9 Binary files /dev/null and b/elements/rh-health-index/docs/style-theme-light.png differ diff --git a/elements/rh-health-index/docs/style-variants.png b/elements/rh-health-index/docs/style-variants.png new file mode 100644 index 0000000000..76ae551700 Binary files /dev/null and b/elements/rh-health-index/docs/style-variants.png differ diff --git a/elements/rh-health-index/rh-health-index.css b/elements/rh-health-index/rh-health-index.css new file mode 100644 index 0000000000..5b0fa8fb42 --- /dev/null +++ b/elements/rh-health-index/rh-health-index.css @@ -0,0 +1,255 @@ +:host { + display: inline-block; +} + +[hidden] { + display: none !important; +} + +#container { + display: inline-flex; + align-items: center; + width: max-content; + font-size: var(--rh-font-size-body-text-md, 1rem); + font-family: var(--rh-font-family-code, RedHatMono, "Red Hat Mono", "Courier New", Courier, monospace); + line-height: var(--_box-size); + text-transform: uppercase; + + /* sizing */ + --_box-size: var(--rh-space-xl, 24px); + + /* colors */ + --_color-text-active: var(--rh-color-gray-95, #151515); + --_color-text-inactive: var(--rh-color-gray-70, #383838); + --_color-text: var(--_color-text-inactive); + --_color-fill: var(--rh-color-surface-lightest, #ffffff); + --_color-border: var(--rh-color-border-subtle-on-light, #c7c7c7); +} + +#grade { + margin-inline-end: var(--rh-space-lg, 16px); +} + +.box { + background-color: var(--_color-fill, transparent); + color: var(--_color-text); + border: var(--rh-border-width-sm, 1px) solid var(--_color-border); + width: var(--_box-size); + display: flex; + flex-flow: column nowrap; + justify-content: center; + align-items: center; + position: relative; + + --_color-fill-active: var(--_color-10); + --_color-border-active: var(--_color-20); + --_color-text-active: var(--_color-30); +} + +.box:not(:first-child) { + margin-inline-start: -1px; +} + +.box.active { + color: var(--_color-text-active); + z-index: 10; +} + +:is(.sm,.md) .box.active { + border-color: var(--_color-border-active); + background-color: var(--_color-fill-active); +} + +:not(.sm) .box { + --_color-fill-active: var(--_color-10); + --_color-border-active: var(--_color-30); +} + +/***************************************************************************** + * MD Size (default) + *****************************************************************************/ + +.md { + height: var(--_box-size); +} + +.md .box { + height: 100%; +} + +.md .box:is(.b, .c, .d) { + --_color-border-active: var(--_color-25); +} + +/***************************************************************************** + * SM Size + *****************************************************************************/ + +.sm { + display: inline-block; + margin: 0; + border: none; + height: var(--_box-size); +} + +/***************************************************************************** + * LG and XL Sizes + *****************************************************************************/ + +:is(.lg, .xl) { + --_color-fill-active: var(--_color-10); + --_color-border-active: var(--_color-30); + --_color-text-active: var(--_color-30); + + display: flex; + margin: 0; + border: var(--rh-border-width-sm, 1px) solid var(--_color-border); +} + +#container:is(.lg, .xl) .box { + border: none; + margin: 1px; + display: inline-block; + line-height: var(--_box-size); + width: var(--_box-size); + text-align: center; + padding-inline: 2px; +} + +:is(.lg, .xl) .box:after { + display: block; + content: ""; + min-height: var(--rh-length-xs, 4px); + z-index: 10; + background-color: var(--_color-20); +} + +:is(.lg, .xl) .box.active { + position: relative; + margin: -1px; + font-weight: var(--rh-font-weight-code-medium, 500); +} + +:is(.lg, .xl) .box.active:before { + display: block; + content: ""; + inset-inline: -1px; + inset-block: -2px; + z-index: -1; + position: absolute; + background-color: var(--_color-fill-active, var(--_color-10)); + border: var(--rh-border-width-sm, 1px) solid var(--_color-border-active); +} + +/***************************************************************************** + * XL Size + *****************************************************************************/ + +#container.xl { + font-size: var(--rh-font-size-body-text-xl, 1.25rem); + + --_box-size: var(--rh-space-2xl, 32px); +} + +/***************************************************************************** + * Light Color Theme + *****************************************************************************/ + +.a { + --_color-10: var(--rh-color-green-20, #d1f1bb); + --_color-20: var(--rh-color-green-60, #3d7317); + --_color-30: var(--rh-color-green-70, #204d00); +} + +.b { + --_color-10: var(--rh-color-green-10, #e9f7df); + --_color-20: var(--rh-color-green-40, #87bb62); + --_color-25: var(--rh-color-green-60, #3d7317); /* alt */ + --_color-30: var(--rh-color-green-70, #204d00); +} + +.c { + --_color-10: var(--rh-color-yellow-10, #fff4cc); + --_color-20: var(--rh-color-yellow-30, #ffcc17); + --_color-25: var(--rh-color-yellow-50, #b98412); /* alt */ + --_color-30: var(--rh-color-yellow-70, #73480b); +} + +.d { + --_color-10: var(--rh-color-orange-10, #ffe8cc); + --_color-20: var(--rh-color-orange-40, #f5921b); + --_color-25: var(--rh-color-orange-60, #9e4a06); /* alt */ + --_color-30: var(--rh-color-orange-70, #732e00); +} + +.e { + --_color-10: var(--rh-color-red-orange-10, #ffe3d9); + --_color-20: var(--rh-color-red-orange-60, #b1380b); + --_color-30: var(--rh-color-red-orange-70, #731f00); +} + +.f { + --_color-10: var(--rh-color-red-10, #fce3e3); + --_color-20: var(--rh-color-red-70, #5f0000); + --_color-30: var(--rh-color-red-70, #5f0000); +} + +/***************************************************************************** + * Dark Color Theme + *****************************************************************************/ + +#container.dark { + --_color-border: var(--rh-color-border-subtle-on-dark, #707070); +} + +#container.dark .box { + --_color-text-active: var(--rh-color-white, #ffffff); + --_color-fill: var(--rh-color-surface-darkest, #151515); + --_color-text: var(--_color-text-inactive); + --_color-text-inactive: var(--_color-25, var(--_color-20)); +} + +#container.dark:is(.lg, .xl) .box { + --_color-fill: var(--rh-color-surface-dark-alt, #292929); + --_color-fill-active: var(--rh-color-surface-darkest, #151515); +} + +#container.dark.sm .box { + background-color: var(--_color-fill); +} + +.dark .a { + --_color-10: var(--rh-color-green-60, #3d7317); + --_color-25: var(--rh-color-green-10, #e9f7df); + --_color-30: var(--rh-color-green-40, #87bb62); +} + +.dark .b { + --_color-10: var(--rh-color-green-40, #87bb62); + --_color-25: var(--rh-color-green-10, #e9f7df); + --_color-30: var(--rh-color-green-20, #d1f1bb); +} + +.dark .c { + --_color-10: var(--rh-color-yellow-30, #ffcc17); + --_color-25: var(--rh-color-yellow-10, #fff4cc); + --_color-30: var(--rh-color-yellow-10, #fff4cc); +} + +.dark .d { + --_color-10: var(--rh-color-orange-40, #f5921b); + --_color-25: var(--rh-color-orange-10, #ffe8cc); + --_color-30: var(--rh-color-orange-20, #fccb8f); +} + +.dark .e { + --_color-10: var(--rh-color-red-orange-50, #f4784a); + --_color-25: var(--rh-color-red-orange-10, #ffe3d9); + --_color-30: var(--rh-color-red-orange-30, #f89b78); +} + +.dark .f { + --_color-10: var(--rh-color-red-60, #a60000); + --_color-25: var(--rh-color-red-10, #fce3e3); + --_color-30: var(--rh-color-red-40, #f56e6e); +} diff --git a/elements/rh-health-index/rh-health-index.ts b/elements/rh-health-index/rh-health-index.ts new file mode 100644 index 0000000000..9c6fd863fa --- /dev/null +++ b/elements/rh-health-index/rh-health-index.ts @@ -0,0 +1,81 @@ +import { LitElement, html, type PropertyValues } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; +import { classMap } from 'lit/directives/class-map.js'; + +import { InternalsController } from '@patternfly/pfe-core/controllers/internals-controller.js'; + +import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js'; + +import styles from './rh-health-index.css'; + +/** + * Health index displays a health grade (A–F) for a particular item or package. + * @summary Displays a health grade for a particular item or package + */ +@customElement('rh-health-index') +export class RhHealthIndex extends LitElement { + static readonly styles = [styles]; + + private static grades = 'ABCDEF'; + + /** + * Sets the size of the health index + * Defaults to `md` + */ + @property({ reflect: true }) size: 'sm' | 'md' | 'lg' | 'xl' = 'md'; + + /** + * Sets the health grade + * Defaults to `A` + */ + @property({ reflect: true }) grade: 'A' | 'B' | 'C' | 'D' | 'E' | 'F' = 'A'; + + /** + * Sets color theme based on parent context + */ + @colorContextConsumer() private on?: ColorTheme; + + // TODO: use I18nController to support officially supported languages. + #internals = InternalsController.of(this, { + role: 'meter', + ariaValueMin: '1', + ariaValueMax: '6', + ariaValueText: 'Grade A', + ariaLabel: 'Health graded A through F', + ariaRoleDescription: 'Level indicator', + }); + + protected override willUpdate(changed: PropertyValues): void { + this.grade = this.grade.toUpperCase() as this['grade']; + if (changed.has('grade')) { + const { grade } = this; + const gradeNumeral = (RhHealthIndex.grades.indexOf(grade) + 1); + this.#internals.ariaValueNow = gradeNumeral.toString(); + this.#internals.ariaValueText = `Grade ${grade}`; + } + } + + override render() { + const { on, size } = this; + const grades = [...RhHealthIndex.grades].map(x => x.toLowerCase()); + const grade = this.grade.toLowerCase(); + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'rh-health-index': RhHealthIndex; + } +} diff --git a/elements/rh-health-index/test/rh-health-index.e2e.ts b/elements/rh-health-index/test/rh-health-index.e2e.ts new file mode 100644 index 0000000000..7737926f60 --- /dev/null +++ b/elements/rh-health-index/test/rh-health-index.e2e.ts @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; + +const tagName = 'rh-health-index'; + +test.describe(tagName, () => { + test('snapshot', async ({ page }) => { + const componentPage = new PfeDemoPage(page, tagName); + await componentPage.navigate(); + await componentPage.snapshot(); + }); +}); diff --git a/elements/rh-health-index/test/rh-health-index.spec.ts b/elements/rh-health-index/test/rh-health-index.spec.ts new file mode 100644 index 0000000000..372e84472c --- /dev/null +++ b/elements/rh-health-index/test/rh-health-index.spec.ts @@ -0,0 +1,49 @@ +import { expect, html } from '@open-wc/testing'; +import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; +import { allUpdates } from '@patternfly/pfe-tools/test/utils.js'; +import { RhHealthIndex } from '@rhds/elements/rh-health-index/rh-health-index.js'; +// import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js'; + +import 'element-internals-polyfill'; + +describe('', function() { + it('imperatively instantiates', function() { + expect(document.createElement('rh-health-index')).to.be.an.instanceof(RhHealthIndex); + }); + + describe('simply instantiating', function() { + let element: RhHealthIndex; + beforeEach(async function() { + element = await createFixture(html``); + }); + + it('should upgrade', async function() { + const klass = customElements.get('rh-health-index'); + expect(element) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(RhHealthIndex); + }); + + it('should be accessible', async function() { + await expect(element).to.be.accessible(); + }); + }); + + describe('with a grade letter', function() { + let element: RhHealthIndex; + beforeEach(async function() { + element = await createFixture(html` + + `); + await allUpdates(element); + }); + + it('should be accessible', async function() { + // playwright is apparently not reading the aria IDL attrs from internals + // const snapshot = await a11ySnapshot(); + // expect(snapshot.children?.find(x => x.role === 'meter')).to.be.ok; + await expect(element).to.be.accessible(); + }); + }); +});