Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .storybook/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@
.toc-list-item {
font-size: 1rem !important;
}


.sbdocs-content ul li{
font-size: 1rem !important;
}
14 changes: 13 additions & 1 deletion .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** @type { import('@storybook/web-components-vite').StorybookConfig } */
import remarkGfm from 'remark-gfm';

const config = {
stories: [
"../storybook/**/*.mdx",
Expand All @@ -8,7 +10,17 @@ const config = {
"@storybook/addon-links",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-a11y"
"@storybook/addon-a11y",
{
name: '@storybook/addon-docs',
options: {
mdxPluginOptions: {
mdxCompileOptions: {
remarkPlugins: [remarkGfm],
},
},
},
},
],
framework: {
name: "@storybook/web-components-vite",
Expand Down
15,219 changes: 9,107 additions & 6,112 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
"devDependencies": {
"@chromatic-com/storybook": "^1.5.0",
"@storybook/addon-a11y": "^8.1.11",
"@storybook/addon-docs": "^8.1.11",
"@storybook/addon-essentials": "^8.1.11",
"@storybook/addon-links": "^8.1.11",
"@storybook/addon-mdx-gfm": "^8.1.11",
"@storybook/blocks": "^8.1.11",
"@storybook/manager-api": "^8.1.11",
"@storybook/test": "^8.1.11",
Expand Down
46 changes: 46 additions & 0 deletions src/components/usa-details/_readme.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Meta, Story, Canvas, Stories, Controls, Primary } from '@storybook/blocks';
import * as DetailsStories from './usa-details.stories';


# Details

A details component hides or reveals hidden content panels when selected.

- [Learn more about using this component on designsystem.digital.gov](https://designsystem.digital.gov/components/accordion/)
- [Accessibility tests](https://designsystem.digital.gov/components/accordion/accessibility-tests/)
- [Release notes]

## Element name
`<usa-details>`

## Implementation example

<Canvas of={DetailsStories.GroupSingleSelect} />

## Expected elements
| Name | Description | Example code | Count |
|:--------|:--------|:--------|:--------|
| `<details>` | Wrapper | `<details></details>` | 1+ |
| `<summary>` | Panel title |`<summary>[Title text]</summary>` | 1 per `<details>` element |
| `<div slot="details-body">` | Panel content |`<div slot="details-body">[Collapsible panel content]</div>` | 1 per `<details>` element |

## Customization
### Props and attributes

| Attribute/prop | Element | Description |
|:--------|:--------|:--------|
| `open="true"` | `<details>` | If the `open` attribute is present on the `<details>` element, the content inside its `details-content` slot should be visible on load.
| `name="[group name]"` | `<details>` | If a defined `name` attribute is present on the `<details>` element, the `<details>` elements with the same `name` value will be treated as a single-select group and toggle each other.

### CSS variables
| Description | USWDS 4 CSS variable | USWDS 3 setting |
|:--------|:--------|:--------|
| Summary/title background color | `--usa-theme-details-summary-background-color` | `$theme-accordion-button-background-color` |
| Panel background color |`--usa-theme-details-panel-background-color` | `$theme-accordion-background-color` |
| Component border color | `--usa-theme-details-border-color` | `$theme-accordion-border-color` |
| Component border width | `--usa-theme-details-border-width` | `$theme-accordion-border-width` |
| Component font family | `--usa-theme-details-font-family` | `$theme-accordion-font-family` |

## All variants

<Stories />
59 changes: 59 additions & 0 deletions src/components/usa-details/index.js
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Are there any events we can trigger via JS on this component?

For example, <usa-banner> can be toggled via toggle() in console.

details-toggle-2024-07-05.at.13.17.58.webm

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { LitElement, html, css, unsafeCSS, nothing } from "lit";
import { classMap } from "lit/directives/class-map.js";
import styles from "./usa-details.css.js";

/**
* @summary The usa-details component.
*
* @slot details-body - Content for the details panel
*
* @attribute {Boolean} open - Set the panel to be open on load
* @attribute {String} name - Add a group name if the element is part of a details group
*
* @cssprop --usa-theme-details-font-family - Sets the font family for the details element
* @cssprop --usa-theme-details-border-color - Sets the border width for the details element
* @cssprop --usa-theme-details-border-width - Sets the border color for the details element
* @cssprop --usa-theme-details-panel-background-color - Sets the background color of the content panels
* @cssprop --usa-theme-details-summary-background-color - Sets the background color of the summary element
*
* @tagname usa-details
*/
export class UsaDetails extends LitElement {
static styles = [styles];

static properties = {
bordered: { type: Boolean },
}

connectedCallback() {
super.connectedCallback();
this.details = [...this.querySelectorAll('details')];
}

render() {
const classes = {
"usa-details__bordered": this.bordered
}
return html`
${this.details.map((detail) => {
this.summary = detail.querySelector('summary');
this.content = detail.querySelector('[slot="details-body"]');
this.open = detail.getAttribute('open');
this.name = detail.getAttribute('name');
this.summary.setAttribute("part", "summary");
this.summary.classList.add('usa-details__summary');
this.content.setAttribute("part", "content");
this.content.classList.add('usa-details__content');

return html`
<details part="wrapper" class="usa-details ${classMap(classes)}" open="${this.open || nothing}" name="${this.name || nothing}">
${this.summary}
${this.content}
</details>
`;
})}
`;
}
}

window.customElements.define("usa-details", UsaDetails);
12 changes: 12 additions & 0 deletions src/components/usa-details/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "usa-details",
"version": "0.0.1",
"description": "USWDS details component",
"main": "index.js",
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "SEE LICENSE IN LICENSE.md"
}
84 changes: 84 additions & 0 deletions src/components/usa-details/usa-details.css.js
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Curious why didn't we use usa-accordion SCSS here?

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { css } from "lit";
import "../../uswds-core/system-vars.css";
import "../../uswds-core/theme-vars.css";

export default css`
:host {
/* general component vars */
--usa-theme-details-border-color: var(--usa-theme-details-summary-background-color);
--usa-theme-details-border-width: var(--usa-system-unit-05);
--usa-theme-details-font-family: var(--usa-theme-font-body);
--usa-theme-details-font-size: var();
--usa-theme-details-icon-size: var(--usa-system-unit-3);
--usa-theme-details-icon-open: url("https://designsystem.digital.gov/assets/img/usa-icons/add.svg");
--usa-theme-details-icon-closed: url("https://designsystem.digital.gov/assets/img/usa-icons/remove.svg");
--usa-theme-details-icon-position: right 1.25rem center;
--usa-theme-details-margin-top: var(--usa-system-unit-1);
/* panel vars */
--usa-theme-details-panel-background-color: var(--usa-theme-page-background-color);
--usa-theme-details-panel-text-color: var(--usa-theme-text-color);
--usa-theme-details-panel-padding-top: var(--usa-system-unit-3);
--usa-theme-details-panel-padding-right: var(--usa-system-unit-205);
--usa-theme-details-panel-padding-bottom: var(--usa-system-unit-3);
--usa-theme-details-panel-padding-left: var(--usa-system-unit-2);
/* summary vars */
--usa-theme-details-summary-background-color: var(--usa-theme-color-base-lightest);
--usa-theme-details-summary-text-color: var(--usa-theme-text-color);
--usa-theme-details-summary-text-size: var(--usa-theme-text-color);
--usa-theme-details-summary-text-weight: var(--usa-theme-text-bold);
--usa-theme-details-summary-padding-top: var(--usa-system-unit-2);
--usa-theme-details-summary-padding-right: var(--usa-system-unit-7);
--usa-theme-details-summary-padding-bottom: var(--usa-system-unit-2);
--usa-theme-details-summary-padding-left: var(--usa-system-unit-205);


.usa-details {
/* TODO: use var to define this */
font-family: var(--usa-theme-details-font-family);
font-size: 1.06rem;
line-height: 1.5;
margin-top: var(--usa-theme-details-margin-top);
}

.usa-details__summary {
background-image: var(--usa-theme-details-icon-open);
background-repeat: no-repeat;
background-size: var(--usa-theme-details-summary-icon-size);
background-color: var(--usa-theme-details-summary-background-color);
background-repeat: no-repeat;
background-position: var(--usa-theme-details-icon-position);
color: var(--usa-theme-details-summary-text-color);
cursor: pointer;
font-size: 1.06rem;
line-height: .9;
font-weight: var(--usa-theme-details-summary-text-weight);
padding-top: var(--usa-theme-details-summary-padding-top);
padding-right: var(--usa-theme-details-summary-padding-right);
padding-bottom: var(--usa-theme-details-summary-padding-bottom);
padding-left: var(--usa-theme-details-summary-padding-left);
}
.usa-details__content {
background-color: var(--usa-theme-details-panel-background-color);
color: var(--usa-theme-details-panel-text-color);
padding-top: var(--usa-theme-details-panel-padding-top);
padding-right: var(--usa-theme-details-panel-padding-right);
padding-bottom: var(--usa-theme-details-panel-padding-bottom);
padding-left: var(--usa-theme-details-panel-padding-left);
}
.usa-details__bordered {
border-color: var(--usa-theme-details-border-color);
border-width: var(--usa-theme-details-border-width);
border-style: solid;
}
.usa-details[open] .usa-details__summary {
/* TODO: use local files */
background-image: var(--usa-theme-details-icon-closed);
}
.usa-details > .usa-details__summary {
list-style: none;
}
.usa-details > .usa-details__summary ::-webkit-details-marker {
display: none;
}
}
`;
132 changes: 132 additions & 0 deletions src/components/usa-details/usa-details.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import "./index";
import { html, nothing } from "lit";
import readme from "./_readme.mdx";

export default {
title: "Components/Details",
component: "usa-details",
parameters: {
docs: {
page: readme,
}
},
argTypes: {
groupName: { name: "Details group name" },
bordered: { name: "Add border to panels" },
item1Summary: {name: "Item 1 - Summary text"},
item1Content: { name: "Item 1 - Panel content"},
item1Open: { name: "Item 1 - Open panel on load"},
item2Show: { name: "Include item 2 in preview"},
item2Summary: {name: "Item 2 - Summary text", if: { arg: 'item2Show' } },
item2Content: { name: "Item 2 - Panel content", if: { arg: 'item2Show' } },
item2Open: { name: "Item 2 - Open panel on load", if: { arg: 'item2Show' } },
item3Show: { name: "Include item 3 in preview"},
item3Summary: {name: "Item 3 - Summary text", if: { arg: 'item3Show' } },
item3Content: { name: "Item 3 - Panel content", if: { arg: 'item3Show' } },
item3Open: { name: "Item 3 - Open panel on load", if: { arg: 'item3Show' } },
CSSVarSummaryBackgroundColor: { name: "--usa-theme-details-summary-background-color" },
CSSVarPanelBackgroundColor: { name: "--usa-theme-details-panel-background-color" },
CSSVarBorderColor: { name: "--usa-theme-details-border-color" },
CSSVarBorderWidth: { name: "--usa-theme-details-border-width" },
},
args: {
groupName: "",
bordered: false,
item1Summary: "First Amendment",
item1Content: "Congress shall make no law respecting an establishment of religion, or prohibiting the free exercise thereof; or abridging the freedom of speech, or of the press; or the right of the people peaceably to assemble, and to petition the Government for a redress of grievances.",
item1Open: false,
item2Show: false,
item2Summary: "Second Amendment",
item2Content: "A well regulated Militia, being necessary to the security of a free State, the right of the people to keep and bear Arms, shall not be infringed.",
item2Open: false,
item3Show: false,
item3Summary: "Third Amendment",
item3Content: "No Soldier shall, in time of peace be quartered in any house, without the consent of the Owner, nor in time of war, but in a manner to be prescribed by law.",
item3Open: false,
CSSVarPanelBackgroundColor: "",
CSSVarSummaryBackgroundColor: "",
CSSVarBorderColor: "",
CSSVarBorderWidth: ""
},
render: ({
groupName,
bordered,
item1Summary,
item1Content,
item1Open,
item2Show,
item2Summary,
item2Content,
item2Open,
item3Show,
item3Summary,
item3Content,
item3Open,
CSSVarPanelBackgroundColor,
CSSVarSummaryBackgroundColor,
CSSVarBorderColor,
CSSVarBorderWidth,
}) => html`
<style>
usa-details::part(summary) {
${CSSVarSummaryBackgroundColor ? `background-color: ${CSSVarSummaryBackgroundColor};`: null};
}
usa-details::part(content) {
${CSSVarPanelBackgroundColor ? `background-color: ${CSSVarPanelBackgroundColor};`: null}
}
usa-details::part(wrapper) {
${CSSVarBorderColor ? `border-color: ${CSSVarBorderColor};`: null}
${CSSVarBorderWidth ? `border-width: ${CSSVarBorderWidth};`: null}
}
</style>
<usa-details bordered="${bordered || nothing}">
<details open=${item1Open || nothing} name=${groupName || nothing}>
<summary>${item1Summary}</summary>
<div slot="details-body">${item1Content}</div>
</details>
${item2Show ? html`
<details open=${item2Open || nothing} name=${groupName || nothing}>
<summary>${item2Summary}</summary>
<div slot="details-body">${item2Content}</div>
</details>
`: null}
${item3Show ? html`
<details open=${item3Open || nothing} name=${groupName || nothing}>
<summary>${item3Summary}</summary>
<div slot="details-body">${item3Content}</div>
</details>
`: null}
</usa-details>
`,
};

export const Default = {};

export const bordered = {
args: {
bordered: true
}
};

export const Open = {
args: {
item1Open: true,
},
}

export const GroupSingleSelect = {
args: {
groupName: "example-group-name",
item1Open: true,
item2Show: true,
item3Show: true,
},
}

export const GroupMultiSelect = {
args: {
item1Open: true,
item2Show: true,
item3Show: true,
},
}
Loading