diff --git a/.changeset/hot-spiders-prove.md b/.changeset/hot-spiders-prove.md new file mode 100644 index 0000000000..5ff1366f92 --- /dev/null +++ b/.changeset/hot-spiders-prove.md @@ -0,0 +1,11 @@ +--- +"@rhds/elements": minor +--- + +✨ Added `` + +Website status communicates the operational status of a website or domain using a status icon and link. It is usually located in the Footer component. + +```html + +``` \ No newline at end of file diff --git a/docs/_data/relatedItems.yaml b/docs/_data/relatedItems.yaml index 3ebc7409f4..08ae0dc52d 100644 --- a/docs/_data/relatedItems.yaml +++ b/docs/_data/relatedItems.yaml @@ -40,6 +40,7 @@ rh-footer: - rh-accordion - rh-popover - rh-tooltip + - rh-site-status rh-jump-links: - rh-pagination - rh-progress-steps @@ -68,6 +69,9 @@ rh-skip-link: - rh-navigation - rh-navigation-secondary - rh-subnav +rh-site-status: + - rh-card + - rh-footer rh-spinner: - form - search-bar diff --git a/docs/_data/repoStatus.yaml b/docs/_data/repoStatus.yaml index 2e5cae955e..baa46d1e5c 100644 --- a/docs/_data/repoStatus.yaml +++ b/docs/_data/repoStatus.yaml @@ -249,7 +249,21 @@ - name: webRH status: Planned - name: Documentation - status: Ready + status: Ready +- name: "Site Status" + type: "Element" + overallStatus: "New" + libraries: + - name: Figma library + status: Ready + - name: Responsive + status: Ready + - name: RH Elements + status: Ready + - name: webRH + status: Planned + - name: Documentation + status: Ready - name: "Spinner" type: "Element" overallStatus: "Available" diff --git a/elements/rh-site-status/README.md b/elements/rh-site-status/README.md new file mode 100644 index 0000000000..c8d2bb4e3d --- /dev/null +++ b/elements/rh-site-status/README.md @@ -0,0 +1,24 @@ +# Site Status +Website status communicates the operational status of a website or domain using a status icon and link. It is usually located in the Footer component + +## Installation + +If using npm/bundlers: + +```bash +npm install @rhds/elements +``` + +Then once installed, import it to your application: + +```js +import '@rhds/elements/rh-site-status/rh-site-status.js'; +``` + +## Usage +- When you need to communicate the operational status of a website or domain +- When you need to provide users with a link to a status page where they can learn more + +```html + +``` diff --git a/elements/rh-site-status/demo/color-context.html b/elements/rh-site-status/demo/color-context.html new file mode 100644 index 0000000000..90afeefdaa --- /dev/null +++ b/elements/rh-site-status/demo/color-context.html @@ -0,0 +1,8 @@ + + + + + diff --git a/elements/rh-site-status/demo/fetch-override.js b/elements/rh-site-status/demo/fetch-override.js new file mode 100644 index 0000000000..645c49717b --- /dev/null +++ b/elements/rh-site-status/demo/fetch-override.js @@ -0,0 +1,15 @@ +export function overrideFetch(ok, status, statusText, json) { + window.fetch = new Proxy(window.fetch, { + apply: (target, thisArg, args) => { + if (args[0] === 'https://status.redhat.com/index.json') { + return Promise.resolve({ + ok, + status, + statusText, + json + }); + } + return target.apply(thisArg, args); + } + }); +} diff --git a/elements/rh-site-status/demo/loading.html b/elements/rh-site-status/demo/loading.html new file mode 100644 index 0000000000..981cf2711d --- /dev/null +++ b/elements/rh-site-status/demo/loading.html @@ -0,0 +1,32 @@ + + + + +

Example of translated loading text

+ + + Chargement + + + + + + diff --git a/elements/rh-site-status/demo/rh-site-status.html b/elements/rh-site-status/demo/rh-site-status.html new file mode 100644 index 0000000000..7ec85cfbe1 --- /dev/null +++ b/elements/rh-site-status/demo/rh-site-status.html @@ -0,0 +1,5 @@ + + + diff --git a/elements/rh-site-status/demo/status-404.html b/elements/rh-site-status/demo/status-404.html new file mode 100644 index 0000000000..ca2673c474 --- /dev/null +++ b/elements/rh-site-status/demo/status-404.html @@ -0,0 +1,10 @@ + + + + + diff --git a/elements/rh-site-status/demo/status-critical.html b/elements/rh-site-status/demo/status-critical.html new file mode 100644 index 0000000000..a9c0cf6777 --- /dev/null +++ b/elements/rh-site-status/demo/status-critical.html @@ -0,0 +1,15 @@ + + + + + diff --git a/elements/rh-site-status/demo/status-maintenance.html b/elements/rh-site-status/demo/status-maintenance.html new file mode 100644 index 0000000000..1cbca2458f --- /dev/null +++ b/elements/rh-site-status/demo/status-maintenance.html @@ -0,0 +1,15 @@ + + + + + diff --git a/elements/rh-site-status/demo/status-major.html b/elements/rh-site-status/demo/status-major.html new file mode 100644 index 0000000000..bd57275a8d --- /dev/null +++ b/elements/rh-site-status/demo/status-major.html @@ -0,0 +1,15 @@ + + + + + diff --git a/elements/rh-site-status/demo/status-minor.html b/elements/rh-site-status/demo/status-minor.html new file mode 100644 index 0000000000..fa1c9d0b7b --- /dev/null +++ b/elements/rh-site-status/demo/status-minor.html @@ -0,0 +1,15 @@ + + + + + diff --git a/elements/rh-site-status/docs/00-overview.md b/elements/rh-site-status/docs/00-overview.md new file mode 100644 index 0000000000..48d2bd404d --- /dev/null +++ b/elements/rh-site-status/docs/00-overview.md @@ -0,0 +1,25 @@ +## Overview + +{{ tagName | getElementDescription }} + +{% example palette="darkest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational'", + src="./site-status-sample.svg" +%} + +{% repoStatusList %} + +## Sample element + + + +## Demo + +{% playground tagName=tagName %}{% endplayground %} +{% cta href="./demo/", target="_blank" %}View the demo{% endcta %} + +## When to use +- When you need to communicate the operational status of a website or domain +- When you need to provide users with a link to a status page where they can learn more + +{% repoStatusChecklist %} diff --git a/elements/rh-site-status/docs/10-style.md b/elements/rh-site-status/docs/10-style.md new file mode 100644 index 0000000000..a65ed34987 --- /dev/null +++ b/elements/rh-site-status/docs/10-style.md @@ -0,0 +1,123 @@ + + +## Styles + +Website status is a combination of an icon and link within a very small card. It is designed to be understood immediately. Otherwise, a user can click on the link to learn more. + +### Anatomy + +
+ {% example palette="darkest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational'", + src="../site-status-anatomy.svg" + %} +
+
    +
  1. Icon
  2. +
  3. Link
  4. +
  5. Container
  6. +
+
+
+ +## Theme + +Currently, Website status is only available in the dark theme. You may use Website status in the light theme if necessary. If you need a separate light version designed instead, [contact us](https://github.com/RedHat-UX/red-hat-design-system/discussions). + + +
+{% example palette="darkest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational on a dark background'", + src="../site-status-theme.svg" + %} + +{% example palette="lightest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational on a light background'", + src="../site-status-theme.svg" + %} +
+ +## Configuration + +Website status has a fixed height and its width changes depending on the amount of link text. The icon and link are horizontally aligned to the card as well. + +{% example palette="darkest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational' with fixed height, width and horizontal alignment indicators.", + src="../site-status-configuration.svg" +%} + +## Icons + +Website status includes three icons that also indicate the severity of the status. + +{% example palette="darkest", + alt="Three images of site status elements, one with a green checkmark and the text 'All systems operational', the second with a yellow exclamation point and the text 'Partial system outage', and the third with a red exclamation point and the text 'Major system outage'.", + src="../site-status-icons.svg" +%} + +## Space + +Space values remain the same at all viewport sizes. + +{% example palette="darkest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational' with space indicators.", + src="../site-status-space.svg" +%} + +{% example palette="none", + alt="Image of a site status element with a green checkmark and the text 'All systems operational' in a footer element with space indicators.", + src="../site-status-footer.svg" +%} + +{% example palette="none", + alt="Image of a site status element with a green checkmark and the text 'All systems operational' in a footer element with space indicators at a small viewport size.", + src="../site-status-footer-mobile.svg"%} + + +## Interaction states + +Only the link is selectable and it should be underlined in all interaction states. + +{% example palette="darkest", + alt="Three images of a site status element with a green checkmark and the text 'All systems operational' first one displaying the mouse hover state, the second the keyboard state, the third a combination of hover and focus.", + src="../site-status-interaction-states.svg" +%} + diff --git a/elements/rh-site-status/docs/20-guidelines.md b/elements/rh-site-status/docs/20-guidelines.md new file mode 100644 index 0000000000..0490d882e2 --- /dev/null +++ b/elements/rh-site-status/docs/20-guidelines.md @@ -0,0 +1,154 @@ + + + +## Usage + +Use Website status to inform users about the operational status of a website or domain and provide them with a link to a status page where they can learn more. + +## Writing Content + +The severity of a status should be communicated in as few words as possible. Remove extra words if necessary so the component does not become too wide. + + +## Character count + +The recommended maximum character count is listed below and includes spaces. + + + + + + + + + + + + + + + +
ElementCharacter count
Link Text30
+
+ +## Layout + +### Heirarchy + +Website status can be placed higher or lower on a page, it just depends on the type of website. + +### Placement + +Website status is usually placed in the [Footer](../../footer/) component, but it may be placed somewhere else if it makes sense to do so. + +{% example palette="darkest", + alt="Image of a site status element with a green checkmark and the text 'All systems operational' in a footer element.", + src="../site-status-footer-placement.svg" +%} + +## Best Practices + +### Pairing icons with text + +To avoid confusion, write link text with the same severity as the status icon. + +
+
+ {% example palette="darkest", + alt="Image of a site status element with a correct usage of a green checkmark icon and the text 'All systems operational'.", + src="../site-status-icons-do.svg" + %} +

Do

+

Write link text with the same severity as the status icon and vice versa.

+
+ +
+ {% example palette="darkest", + alt="Image of a site status element with an incorrect usage of an orange red exclamation point icon and the text 'All systems operational'.", + src="../site-status-icons-dont.svg" + %} +

Don't

+

Do not make it confusing to understand what the status or severity is.

+
+
+ +## Removing icons + +Always include a status icon, it helps communicate severity visually. + +
+
+ {% example palette="darkest", + alt="Image of a site status element with a correct usage of a green checkmark icon and the text 'All systems operational'.", + src="../site-status-icons-do.svg" + %} +

Do

+

Include an icon to make it easier for users to understand.

+
+ +
+ {% example palette="darkest", + alt="Image of a site status element with an incorrect usage of an orange red exclamation point icon and the text 'All systems operational'.", + src="../site-status-icons-dont-no-icon.svg" + %} +

Don't

+

Do not remove the status icon.

+
+
+ +## Writing link text too long + +Do not write link text that is too long, it will take users longer to read and make the component wider. + +
+
+ {% example palette="darkest", + alt="Image of a site status element with a correct usage of a green checkmark icon and the text 'All systems operational'.", + src="../site-status-icons-do.svg" + %} +

Do

+

Write link text using as few words as possible.

+
+ +
+ {% example palette="darkest", + alt="Image of a site status element with an incorrect usage of an orange red exclamation point icon and the text 'All systems operational'.", + src="../site-status-icons-dont-long-text.svg" + %} +

Don't

+

Do not include unnecessary words or punctuation.

+
+
diff --git a/elements/rh-site-status/docs/30-code.md b/elements/rh-site-status/docs/30-code.md new file mode 100644 index 0000000000..0957e13f8e --- /dev/null +++ b/elements/rh-site-status/docs/30-code.md @@ -0,0 +1,17 @@ +{% renderOverview %} + +{% endrenderOverview %} + +{% band header="Usage" %}{% endband %} + +{% renderSlots %}{% endrenderSlots %} + +{% renderAttributes %}{% endrenderAttributes %} + +{% renderMethods %}{% endrenderMethods %} + +{% renderEvents %}{% endrenderEvents %} + +{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} + +{% renderCssParts %}{% endrenderCssParts %} \ No newline at end of file diff --git a/elements/rh-site-status/docs/40-accessibility.md b/elements/rh-site-status/docs/40-accessibility.md new file mode 100644 index 0000000000..dc04db0b3b --- /dev/null +++ b/elements/rh-site-status/docs/40-accessibility.md @@ -0,0 +1,27 @@ +## Keyboard navigation + +Users should have the ability to move focus to the Website status link if it is used in the Footer component or anywhere else. + +{% example palette="none", + alt="Image of a site status element with a green checkmark and the text 'All systems operational' in a footer element with focus indicators.", + src="../site-status-accessibility-footer.svg" +%} + +## Screen reader guidelines + +Website status should communicate the following to users. + +- What is the severity of the status icon +- What the text says +- That the text is also a link +- Where the link will take them if selected +- If the status is still loading + + +{% include 'accessibility/ariaguide.md' %} + +{% include 'accessibility/wcag.md' %} +{% include 'accessibility/2.1.1-A.md' %} +{% include 'accessibility/2.1.3-AAA.md' %} +{% include 'accessibility/2.4.3-A.md' %} +{% include 'accessibility/2.5.5-AAA.md' %} \ No newline at end of file diff --git a/elements/rh-site-status/docs/do.svg b/elements/rh-site-status/docs/do.svg new file mode 100644 index 0000000000..63843d76f1 --- /dev/null +++ b/elements/rh-site-status/docs/do.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/dont.svg b/elements/rh-site-status/docs/dont.svg new file mode 100644 index 0000000000..036c622c5c --- /dev/null +++ b/elements/rh-site-status/docs/dont.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/screenshot.png b/elements/rh-site-status/docs/screenshot.png new file mode 100644 index 0000000000..1b9b317d10 Binary files /dev/null and b/elements/rh-site-status/docs/screenshot.png differ diff --git a/elements/rh-site-status/docs/site-status-accessibility-footer.svg b/elements/rh-site-status/docs/site-status-accessibility-footer.svg new file mode 100644 index 0000000000..7aaa26b5f3 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-accessibility-footer.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-anatomy.svg b/elements/rh-site-status/docs/site-status-anatomy.svg new file mode 100644 index 0000000000..1b48cb9740 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-anatomy.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-configuration.svg b/elements/rh-site-status/docs/site-status-configuration.svg new file mode 100644 index 0000000000..d566b8ea01 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-configuration.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-footer-mobile.svg b/elements/rh-site-status/docs/site-status-footer-mobile.svg new file mode 100644 index 0000000000..c26a9571ab --- /dev/null +++ b/elements/rh-site-status/docs/site-status-footer-mobile.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-footer-placement.svg b/elements/rh-site-status/docs/site-status-footer-placement.svg new file mode 100644 index 0000000000..789f412ffa --- /dev/null +++ b/elements/rh-site-status/docs/site-status-footer-placement.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-footer.svg b/elements/rh-site-status/docs/site-status-footer.svg new file mode 100644 index 0000000000..9d13579bb9 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-footer.svg @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-icons-do.svg b/elements/rh-site-status/docs/site-status-icons-do.svg new file mode 100644 index 0000000000..0234422a24 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-icons-do.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-icons-dont-long-text.svg b/elements/rh-site-status/docs/site-status-icons-dont-long-text.svg new file mode 100644 index 0000000000..0cc81ce90b --- /dev/null +++ b/elements/rh-site-status/docs/site-status-icons-dont-long-text.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-icons-dont-no-icon.svg b/elements/rh-site-status/docs/site-status-icons-dont-no-icon.svg new file mode 100644 index 0000000000..e62839b968 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-icons-dont-no-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-icons-dont.svg b/elements/rh-site-status/docs/site-status-icons-dont.svg new file mode 100644 index 0000000000..20435c0dd9 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-icons-dont.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-icons.svg b/elements/rh-site-status/docs/site-status-icons.svg new file mode 100644 index 0000000000..2dcb231b9e --- /dev/null +++ b/elements/rh-site-status/docs/site-status-icons.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-interaction-states.svg b/elements/rh-site-status/docs/site-status-interaction-states.svg new file mode 100644 index 0000000000..86e2aa1966 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-interaction-states.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-sample.svg b/elements/rh-site-status/docs/site-status-sample.svg new file mode 100644 index 0000000000..1691cf6f31 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-sample.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-space.svg b/elements/rh-site-status/docs/site-status-space.svg new file mode 100644 index 0000000000..b701b119b9 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-space.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/docs/site-status-theme.svg b/elements/rh-site-status/docs/site-status-theme.svg new file mode 100644 index 0000000000..1691cf6f31 --- /dev/null +++ b/elements/rh-site-status/docs/site-status-theme.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/elements/rh-site-status/rh-site-status.css b/elements/rh-site-status/rh-site-status.css new file mode 100644 index 0000000000..39e491c41c --- /dev/null +++ b/elements/rh-site-status/rh-site-status.css @@ -0,0 +1,49 @@ +:host { + display: inline-flex; +} + +#container { + display: inline-flex; + padding: var(--rh-space-lg, 16px); + background: var(--rh-color-surface-light, #e0e0e0); + border-radius: var(--rh-border-radius-default, 3px); + min-width: 150px; +} + +#container.dark { + background: var(--rh-color-surface-dark, #383838); +} + +a { + display: inline-flex; + font-size: var(--rh-font-size-body-text-sm, 0.875rem); + text-decoration: none; + align-items: center; + gap: var(--rh-space-md, 8px); + color: var(--rh-color-text-primary-on-light, #151515); + text-transform: lowercase; +} + +.dark a { + color: var(--rh-color-text-primary-on-dark, #ffffff); +} + +span:first-letter { + text-transform: uppercase; +} + +a:focus { + outline: + var(--rh-border-width-md, 2px) + solid + var(--rh-color-border-interactive-on-light, #0066cc); + border-radius: var(--rh-border-radius-default, 3px); +} + +a:is(:hover,:focus) { + text-decoration: underline; +} + +.dark a:focus { + outline-color: var(--rh-color-border-interactive-on-dark, #92c5f9); +} diff --git a/elements/rh-site-status/rh-site-status.ts b/elements/rh-site-status/rh-site-status.ts new file mode 100644 index 0000000000..adbfb0fb41 --- /dev/null +++ b/elements/rh-site-status/rh-site-status.ts @@ -0,0 +1,145 @@ +import { type TemplateResult, LitElement, html, svg } 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 { Logger } from '@patternfly/pfe-core/controllers/logger.js'; + +import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js'; + +import '../rh-spinner/rh-spinner.js'; + +import styles from './rh-site-status.css'; + +interface ApiStatus { + status: { + description: string; + indicator: string; + }; +} + +const statusIconsMap: Record = { + 'ok': svg` + + + `, + 'warn': svg` + + + `, + 'danger': svg` + + + ` +}; + +// map statuspage.io's text to our text; at least one of their status +// strings is too long for the space we have +const textMap: Record = { + 'Partially Degraded Service': 'Partial service', +}; + +// map statuspage.io's statuses to icon map keys +const statusMap: Record = { + 'maintenance': 'warn', + 'critical': 'danger', + 'major': 'warn', + 'minor': 'warn', + 'none': 'ok', +}; + +/** + * Website status communicates the operational status of a website or domain using a status icon and link. It is usually located in the Footer component. + * + * @summary Communicates operational status of a website or domain + * + * @slot loading-text - Text to display while loading the status defaults to "Loading" + */ +@customElement('rh-site-status') +export class RhSiteStatus extends LitElement { + static readonly styles = [styles]; + + private static isApiStatus = (data: ApiStatus): data is ApiStatus => { + return ( + typeof data === 'object' && + data !== null && + 'status' in data && + data.status !== null && + 'description' in data.status && + 'indicator' in data.status + ); + }; + + #logger = new Logger(this); + + #text = ''; + + #icon: TemplateResult = html``; + + #isLoading = true; + + /** + * Sets color theme based on parent context + */ + @colorContextConsumer() private on?: ColorTheme; + + async connectedCallback() { + super.connectedCallback(); + await this.#getStatus(); + } + + render() { + const { on = '' } = this; + return html` + + `; + } + + async #getStatus() { + try { + const data: ApiStatus = await fetch('https://status.redhat.com/index.json', { + mode: 'cors', + cache: 'no-cache', + headers: { + Accept: 'application/json', + } + }) + .then(response => { + if (response.ok) { + return response.json(); + } else { + throw new Error(`${response.statusText}`); + } + }); + if (!RhSiteStatus.isApiStatus(data)) { + throw new Error('Invalid status data'); + } + const statusText = data.status.description; + this.#text = textMap[statusText] || statusText; + this.#icon = statusIconsMap[statusMap[data.status.indicator]]; + this.#isLoading = false; + this.requestUpdate(); + } catch (error) { + this.#logger.warn('Error loading site status:', error); + this.#text = 'Error loading status'; + this.#icon = statusIconsMap['danger']; + this.#isLoading = false; + this.requestUpdate(); + } + } +} + +declare global { + interface HTMLElementTagNameMap { + 'rh-site-status': RhSiteStatus; + } +} diff --git a/elements/rh-site-status/test/rh-site-status.e2e.ts b/elements/rh-site-status/test/rh-site-status.e2e.ts new file mode 100644 index 0000000000..f7aa9b49f0 --- /dev/null +++ b/elements/rh-site-status/test/rh-site-status.e2e.ts @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; + +const tagName = 'rh-site-status'; + +test.describe(tagName, () => { + test('snapshot', async ({ page }) => { + const componentPage = new PfeDemoPage(page, tagName); + await componentPage.navigate(); + await componentPage.snapshot(); + }); +}); diff --git a/elements/rh-site-status/test/rh-site-status.spec.ts b/elements/rh-site-status/test/rh-site-status.spec.ts new file mode 100644 index 0000000000..a4ef37aaab --- /dev/null +++ b/elements/rh-site-status/test/rh-site-status.spec.ts @@ -0,0 +1,110 @@ +import { expect, html } from '@open-wc/testing'; +import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; +import { RhSiteStatus } from '@rhds/elements/rh-site-status/rh-site-status.js'; + +import { stub, type SinonStub } from 'sinon'; + +describe('', function() { + let element: RhSiteStatus; + let fetchStub: SinonStub; + + afterEach(function() { + fetchStub?.restore?.(); + }); + + describe('simply instantiating', function() { + beforeEach(async function() { + fetchStub = stub(window, 'fetch'); + fetchStub.resolves({ + ok: true, + status: 200, + statusText: 'OK', + json: () => Promise.resolve({ + status: { + indicator: 'none', + description: 'All systems operational' + } + }) + }); + element = await createFixture(html``); + await element.updateComplete; + }); + + it('should upgrade', async function() { + element = await createFixture(html``); + const klass = customElements.get('rh-site-status'); + expect(element) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(RhSiteStatus); + }); + }); + + describe('when endpoint returns an All systems operational', function() { + beforeEach(async function() { + fetchStub = stub(window, 'fetch'); + fetchStub.resolves({ + ok: true, + status: 200, + statusText: 'OK', + json: () => Promise.resolve({ + status: { + indicator: 'none', + description: 'All Systems Operational' + } + }) + }); + element = await createFixture(html``); + await element.updateComplete; + }); + + it('should show all systems operational message', async function() { + // we moved the text capitalization styling to a CSS concern, so can't test for the text capitalization here. + expect(element.shadowRoot!.textContent).to.include('All Systems Operational'); + }); + }); + + describe('when endpoint returns a 404', function() { + beforeEach(async function() { + fetchStub = stub(window, 'fetch'); + fetchStub.resolves({ + ok: false, + status: 404, + statusText: '404 Not Found', + }); + element = await createFixture(html``); + await element.updateComplete; + }); + + afterEach(function() { + fetchStub.restore(); + }); + + it('should show an error message', async function() { + expect(element.shadowRoot!.textContent).to.include('Error loading status'); + }); + }); + + describe('when endpoint returns a partial minor outage ', function() { + beforeEach(async function() { + fetchStub = stub(window, 'fetch'); + fetchStub.resolves({ + ok: true, + status: 200, + statusText: 'OK', + json: () => Promise.resolve({ + status: { + indicator: 'minor', + description: 'Partially Degraded Service' + } + }) + }); + element = await createFixture(html``); + await element.updateComplete; + }); + + it('should show partial outage message', async function() { + expect(element.shadowRoot!.textContent).to.include('Partial service'); + }); + }); +});