Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(disclosure): add <rh-disclosure> #2043

Open
wants to merge 10 commits into
base: staging/cubone
Choose a base branch
from
21 changes: 21 additions & 0 deletions .changeset/nasty-ravens-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"@rhds/elements": minor
---

✨ Added `<rh-disclosure>`

A disclosure is a widget that enables content to be either collapsed (hidden) or expanded (visible).

```html
<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
Collapsed panel title
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
Lorem ipsum dolor sit amet consectetur adipisicing, elit.
</div>
</details>
</rh-disclosure>
```
12 changes: 12 additions & 0 deletions docs/_data/repoStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,18 @@ export default [
docs: 'ready',
},
},
{
tagName: 'rh-disclosure',
name: 'Disclosure',
type: 'element',
overallStatus: 'ready',
libraries: {
figma: 'ready',
rhds: 'ready',
shared: 'ready',
docs: 'ready',
},
},
{
tagName: 'rh-footer',
name: 'Footer',
Expand Down
22 changes: 22 additions & 0 deletions elements/rh-disclosure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Disclosure

A disclosure is a widget that enables content to be either
collapsed (hidden) or expanded (visible).

## Usage

Place the following markup on your page:

```html
<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
Collapsed panel title
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
Lorem ipsum dolor sit amet consectetur adipisicing, elit. Velit distinctio, nesciunt nobis sit, a dolor, non numquam rerum recusandae, deserunt enim assumenda quidem. Id impedit necessitatibus obcaecati ratione reprehenderit laborum?
</div>
</details>
</rh-disclosure>
```
21 changes: 21 additions & 0 deletions elements/rh-disclosure/demo/color-context.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<rh-context-demo>
<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
Collapsed panel title
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
Lorem ipsum dolor sit amet consectetur adipisicing, elit. Velit distinctio, nesciunt nobis sit, a dolor, non numquam rerum recusandae, deserunt enim assumenda quidem. Id impedit necessitatibus obcaecati ratione reprehenderit laborum?
</div>
</details>
</rh-disclosure>
</rh-context-demo>

<link rel="stylesheet" href="../rh-disclosure-lightdom.css">

<script type="module">
import '@rhds/elements/lib/elements/rh-context-demo/rh-context-demo.js';
import '@rhds/elements/rh-disclosure/rh-disclosure.js';
import '@rhds/elements/rh-icon/rh-icon.js';
</script>
44 changes: 44 additions & 0 deletions elements/rh-disclosure/demo/nested-disclosures.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
This is the top level disclosure
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
<p>Be sure to test the ESC key + focus when nesting disclosures together. Lorem ipsum dolor <a href="#">fake link</a> adipisicing, elit.</p>

<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
Be sure to open this disclosure
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
<p>You can hit escape to test focus and see which details element closes!</p>

<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
Third nested disclosure
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
<p>This is nesting! <a href="#">fake link 2</a> and more text.</p>
</div>
</details>
</rh-disclosure>

</div>
</details>
</rh-disclosure>

</div>
</details>
</rh-disclosure>

<link rel="stylesheet" href="../rh-disclosure-lightdom.css">

<script type="module">
import '@rhds/elements/rh-disclosure/rh-disclosure.js';
import '@rhds/elements/rh-icon/rh-icon.js';
</script>
18 changes: 18 additions & 0 deletions elements/rh-disclosure/demo/rh-disclosure.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<rh-disclosure>
<details>
<summary>
<rh-icon set="ui" icon="caret-down"></rh-icon>
Collapsed panel title
</summary>
<div class="details-content"> <!-- `.details-content` class required -->
Lorem ipsum dolor <a href="#">sit amet consectetur</a> adipisicing, elit. Velit distinctio, nesciunt nobis sit, a dolor, non numquam rerum recusandae, deserunt enim assumenda quidem. Id impedit necessitatibus obcaecati ratione reprehenderit laborum?
</div>
</details>
</rh-disclosure>

<link rel="stylesheet" href="../rh-disclosure-lightdom.css">

<script type="module">
import '@rhds/elements/rh-disclosure/rh-disclosure.js';
import '@rhds/elements/rh-icon/rh-icon.js';
</script>
9 changes: 9 additions & 0 deletions elements/rh-disclosure/docs/00-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## When to use

- When you want to have some content you want to expand and collapse
- When putting all of the content on the page might not be relevant to all users
or distract them from the main content on the page

<div id="overview-image-description" class="visually-hidden">
An expanded disclosure element with a panel trigger and lorem ipsum for details content
</div>
1 change: 1 addition & 0 deletions elements/rh-disclosure/docs/10-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Style
1 change: 1 addition & 0 deletions elements/rh-disclosure/docs/20-guidelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Guidelines
1 change: 1 addition & 0 deletions elements/rh-disclosure/docs/40-accessibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Accessibility
Binary file added elements/rh-disclosure/docs/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions elements/rh-disclosure/rh-disclosure-lightdom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
rh-disclosure {
& details {
/* stylelint-disable-next-line rhds/token-values */
border: var(--rh-border-width-sm, 1px) solid var(--rh-color-border-subtle, #c7c7c7);
font-family: var(--rh-font-family-body-text);

& .details-content {
font-size: var(--rh-font-size-body-text-md, 1rem);
line-height: var(--rh-line-height-body-text, 1.5);
padding: var(--rh-space-xl, 24px);
padding-block-start: var(--rh-space-lg, 16px);

& :is(p, h2, h3, h4, h5, h6):first-of-type {
margin-block-start: 0;
}
}
}

& summary {
align-items: center;
cursor: pointer;
display: flex;
font-size: var(--rh-font-size-body-text-md, 1rem);
font-weight: var(--rh-font-weight-body-text-medium, 500);
gap: var(--rh-length-sm, 6px);
list-style: none;
padding: var(--rh-space-lg, 16px) var(--rh-space-xl, 24px);

& rh-icon[icon='caret-down'] {
inline-size: var(--rh-space-lg, 16px);
block-size: var(--rh-space-lg, 16px);
transition: 0.2s;
will-change: rotate;
}

&::-webkit-details-marker,
&::marker {
display: none;
}
}

details[open] {
box-shadow: var(--rh-box-shadow-sm, 0 2px 4px 0 rgba(21, 21, 21, 0.2));
position: relative;

&:before {
content: '';
border-inline-start: 3px solid var(--rh-color-brand-red-on-light, #ee0000);
position: absolute;
z-index: 1;
inset-inline-start: -1px;
inset-block: -1px;
}

& > summary rh-icon[icon='caret-down'] {
transform: rotate(-180deg);
}
}
}
3 changes: 3 additions & 0 deletions elements/rh-disclosure/rh-disclosure.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
display: block;
}
58 changes: 58 additions & 0 deletions elements/rh-disclosure/rh-disclosure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { LitElement, html, isServer } from 'lit';
import { customElement } from 'lit/decorators/custom-element.js';

import styles from './rh-disclosure.css';

/**
* @summary A disclosure is a widget that enables content to be either collapsed (hidden) or expanded (visible).
* @slot - Place the `details`, `summary`, and `.details-content` elements in the default slot.
*/

@customElement('rh-disclosure')
export class RhDisclosure extends LitElement {
static readonly styles = [styles];

#details?: HTMLDetailsElement;
#summary?: HTMLElement;

firstUpdated() {
this.#details = this.querySelector<HTMLDetailsElement>('details')!;
this.#summary = this.querySelector<HTMLElement>('details summary')!;
if (!isServer) {
this.#details?.addEventListener('keydown', this.#handleKeyDown.bind(this));
}
}

disconnectedCallback() {
if (!isServer && this.#details) {
this.#details.removeEventListener('keydown', this.#handleKeyDown.bind(this));
}
super.disconnectedCallback();
}

render() {
return html`
<slot></slot>
`;
}

#handleKeyDown(event: KeyboardEvent): void {
if (event.code === 'Escape') {
event.stopPropagation();
this.#closeDetails();
}
}

#closeDetails(): void {
if (this.#details?.open) {
this.#details.open = false;
this.#summary?.focus();
}
}
}

declare global {
interface HTMLElementTagNameMap {
'rh-disclosure': RhDisclosure;
}
}
25 changes: 25 additions & 0 deletions elements/rh-disclosure/test/rh-disclosure.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { test } from '@playwright/test';
import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js';
import { SSRPage } from '@patternfly/pfe-tools/test/playwright/SSRPage.js';

const tagName = 'rh-disclosure';

test.describe(tagName, () => {
test('snapshot', async ({ page }) => {
const componentPage = new PfeDemoPage(page, tagName);
await componentPage.navigate();
await componentPage.snapshot();
});

test('ssr', async ({ browser }) => {
const fixture = new SSRPage({
tagName,
browser,
demoDir: new URL('../demo/', import.meta.url),
importSpecifiers: [
`@patternfly/elements/${tagName}/${tagName}.js`,
],
});
await fixture.snapshots();
});
});
44 changes: 44 additions & 0 deletions elements/rh-disclosure/test/rh-disclosure.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { expect, html } from '@open-wc/testing';
import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js';
import { RhDisclosure } from '@rhds/elements/rh-disclosure/rh-disclosure.js';

describe('<rh-disclosure>', function() {
describe('simply instantiating', function() {
let element: RhDisclosure;
it('imperatively instantiates', function() {
expect(document.createElement('rh-disclosure')).to.be.an.instanceof(RhDisclosure);
});

it('should upgrade', async function() {
element = await createFixture<RhDisclosure>(html`<rh-disclosure></rh-disclosure>`);
const klass = customElements.get('rh-disclosure');
expect(element)
.to.be.an.instanceOf(klass)
.and
.to.be.an.instanceOf(RhDisclosure);
});
});

describe('when the element loads', function() {
let element: RhDisclosure;
beforeEach(async function() {
element = await createFixture<RhDisclosure>(html`
<rh-disclosure>
<details>
<summary>
Summary title
</summary>
<div class="details-content">
Details content goes here.
</div>
</details>
</rh-disclosure>
`);
await element.updateComplete;
});

it('should be accessible', async function() {
await expect(element).to.be.accessible();
});
});
});
Loading