Skip to content

feat(accordion): add direct actions #4020

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

Draft
wants to merge 3 commits into
base: spectrum-two
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions .changeset/spicy-rings-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@spectrum-css/accordion": minor
---

Accordion now support direct actions. Direct actions, which may consist of a quiet action button or a switch, or both, may be added to each accordion item's heading. Direct action items are vertically centered within the heading's first line of text for all sizes and densities, and maintain their own individual key focus states.

To allow the same level of customizability found in other elements within this component, the following --mod custom properties have been added:

- "--mod-accordion-item-direct-actions-height",
- "--mod-accordion-item-direct-actions-spacing",
- "--mod-accordion-item-direct-actions-vertical-spacing"
10 changes: 10 additions & 0 deletions components/accordion/dist/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
".spectrum-Accordion-item.is-open > .spectrum-Accordion-itemHeading .spectrum-Accordion-itemIndicator",
".spectrum-Accordion-item:first-child",
".spectrum-Accordion-itemContent",
".spectrum-Accordion-itemDirectActions",
".spectrum-Accordion-itemDirectActions .spectrum-ActionButton",
".spectrum-Accordion-itemDirectActions .spectrum-Switch",
".spectrum-Accordion-itemHeader",
".spectrum-Accordion-itemHeader.spectrum-Accordion-itemHeader:active",
".spectrum-Accordion-itemHeader:focus-visible",
Expand Down Expand Up @@ -60,6 +63,9 @@
"--mod-accordion-item-content-font-style",
"--mod-accordion-item-content-font-weight",
"--mod-accordion-item-content-line-height",
"--mod-accordion-item-direct-actions-height",
"--mod-accordion-item-direct-actions-spacing",
"--mod-accordion-item-direct-actions-vertical-spacing",
"--mod-accordion-item-focus-indicator-color",
"--mod-accordion-item-focus-indicator-gap",
"--mod-accordion-item-focus-indicator-thickness",
Expand Down Expand Up @@ -131,6 +137,9 @@
"--spectrum-accordion-item-content-font-style",
"--spectrum-accordion-item-content-font-weight",
"--spectrum-accordion-item-content-line-height",
"--spectrum-accordion-item-direct-actions-height",
"--spectrum-accordion-item-direct-actions-spacing",
"--spectrum-accordion-item-direct-actions-vertical-spacing",
"--spectrum-accordion-item-focus-indicator-color",
"--spectrum-accordion-item-focus-indicator-gap",
"--spectrum-accordion-item-focus-indicator-thickness",
Expand Down Expand Up @@ -227,6 +236,7 @@
"--spectrum-neutral-content-color-hover",
"--spectrum-neutral-content-color-key-focus",
"--spectrum-sans-font-family-stack",
"--spectrum-spacing-100",
"--spectrum-transparent-black-100",
"--spectrum-transparent-black-25",
"--spectrum-transparent-black-300"
Expand Down
29 changes: 28 additions & 1 deletion components/accordion/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
--spectrum-accordion-item-content-area-top-to-content: var(--spectrum-accordion-content-area-top-to-content);
--spectrum-accordion-item-content-area-bottom-to-content: var(--spectrum-accordion-content-area-bottom-to-content);
--spectrum-accordion-divider-thickness: var(--spectrum-divider-thickness-small);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-100);

/* Text header */
--spectrum-accordion-item-header-font: var(--spectrum-sans-font-family-stack);
Expand All @@ -40,6 +41,7 @@
--spectrum-accordion-item-header-line-height: var(--spectrum-line-height-100);

--spectrum-accordion-item-header-cursor: pointer;
--spectrum-accordion-item-direct-actions-spacing: var(--spectrum-spacing-100);
--spectrum-accordion-animation-duration: var(--spectrum-animation-duration-100);

/* Text body */
Expand Down Expand Up @@ -72,6 +74,12 @@
var(--mod-accordion-item-header-top-to-text-space, var(--spectrum-accordion-item-header-top-to-text-space)) + var(--mod-accordion-item-header-bottom-to-text-space, var(--spectrum-accordion-item-header-bottom-to-text-space)) + (var(--mod-accordion-item-header-font-size, var(--spectrum-accordion-item-header-font-size)) * var(--mod-accordion-item-header-line-height, var(--spectrum-accordion-item-header-line-height)))
);

/* Calculated vertical spacing for action button and switch to center them within the accordion item */
--spectrum-accordion-item-direct-actions-vertical-spacing: calc(
(var(--mod-accordion-item-min-block-size, var(--spectrum-accordion-item-min-block-size)) -
var(--mod-accordion-item-direct-actions-height, var(--spectrum-accordion-item-direct-actions-height))) / 2
);

Comment on lines +77 to +82
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Did I miss a better way to do vertical spacing for this situation? I was finding that this got pretty complex, pretty fast:

  • An align-items: center on the .spectrum-Accordion-itemDirectActions isn't ideal when the text wraps because the designs show that we want direct actions to align with the first line of text, instead of all of them.
  • Accordion items have a min-height of component-height-75/component-height-100/component-height-200/component-height-300, same with the direct actions, but we don't usually see that min-height in accordion (except for compact) because of the calculations for spacing and font-size * line-height making it larger

/* Right-to-left adjustments for transforms */
&:dir(rtl) {
--spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0);
Expand Down Expand Up @@ -100,6 +108,7 @@
--spectrum-accordion-item-header-top-to-text-space: var(--spectrum-accordion-top-to-text-small);
--spectrum-accordion-item-header-bottom-to-text-space: var(--spectrum-accordion-bottom-to-text-small);
--spectrum-accordion-top-to-disclosure-indicator: var(--spectrum-field-top-to-disclosure-icon-small);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-75); /* component height for switch and action button */
}

.spectrum-Accordion--sizeL {
Expand All @@ -116,6 +125,7 @@
--spectrum-accordion-item-header-top-to-text-space: var(--spectrum-accordion-top-to-text-large);
--spectrum-accordion-item-header-bottom-to-text-space: var(--spectrum-accordion-bottom-to-text-large);
--spectrum-accordion-top-to-disclosure-indicator: var(--spectrum-field-top-to-disclosure-icon-large);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-200);
}

.spectrum-Accordion--sizeXL {
Expand All @@ -132,6 +142,7 @@
--spectrum-accordion-item-header-top-to-text-space: var(--spectrum-accordion-top-to-text-extra-large);
--spectrum-accordion-item-header-bottom-to-text-space: var(--spectrum-accordion-bottom-to-text-extra-large);
--spectrum-accordion-top-to-disclosure-indicator: var(--spectrum-field-top-to-disclosure-icon-extra-large);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-300);
}

.spectrum-Accordion--compact {
Expand Down Expand Up @@ -237,6 +248,7 @@
margin: 0;
position: relative;
box-sizing: border-box;
display: flex;
}

.spectrum-Accordion-itemIndicator {
Expand Down Expand Up @@ -268,15 +280,16 @@

/* Focusable button that expands/collapses the accordion item. */
.spectrum-Accordion-itemHeader {
overflow-wrap: anywhere;
box-sizing: border-box;
position: relative;

display: flex;
align-items: flex-start;
justify-content: flex-start;

/* start spacing controlled by edge to disclosure icon spacing */
padding-inline: 0 var(--mod-accordion-edge-to-content-area, var(--spectrum-accordion-edge-to-content-area));
padding-block: 0; /* reset user-agent styles */
line-height: var(--mod-accordion-item-header-line-height, var(--spectrum-accordion-item-header-line-height));

text-overflow: ellipsis;
Expand Down Expand Up @@ -315,6 +328,20 @@
}
}

.spectrum-Accordion-itemDirectActions {
margin-inline-end: var(--mod-accordion-edge-to-content-area, var(--spectrum-accordion-edge-to-content-area));
display: inline-flex;
gap: var(--mod-accordion-item-direct-actions-spacing, var(--spectrum-accordion-item-direct-actions-spacing));

& .spectrum-ActionButton,
& .spectrum-Switch {
margin-block-start: var(--mod-accordion-item-direct-actions-vertical-spacing, var(--spectrum-accordion-item-direct-actions-vertical-spacing));

/* needs to be set to prevent compact XL items from growing vertically to accommodate the direct actions */
margin-block-end: var(--mod-accordion-item-direct-actions-vertical-spacing, var(--spectrum-accordion-item-direct-actions-vertical-spacing));
}
}

.spectrum-Accordion-item.is-open {
> .spectrum-Accordion-itemHeading .spectrum-Accordion-itemIndicator {
transform: var(--spectrum-logical-rotation,) rotate(90deg);
Expand Down
53 changes: 49 additions & 4 deletions components/accordion/stories/accordion.stories.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js";
import { Sizes } from "@spectrum-css/preview/decorators";
import { disableDefaultModes } from "@spectrum-css/preview/modes";
import { isQuiet, size } from "@spectrum-css/preview/types";
import metadata from "../dist/metadata.json";
import packageJson from "../package.json";
import { AccordionGroup, testsContent as accordionContent } from "./accordion.test.js";
import { AccordionGroup, testsContent as accordionContent, directActionsContent, longerContent } from "./accordion.test.js";
import { Template } from "./template.js";

/**
Expand Down Expand Up @@ -60,7 +61,36 @@ export default {
},
control: { type: "boolean" },
},
isQuiet
isQuiet,
hasActionButtons: {
name: "Has action buttons",
description: "Adds an action button to each accordion item header, in the direct actions section.",
type: { name: "boolean" },
table: {
type: { summary: "boolean" },
category: "Direct actions",
},
control: { type: "boolean" },
},
actionButtonIconName: {
name: "Action button icon",
...(IconStories?.argTypes?.iconName ?? {}),
if: { arg: "hasActionButtons", truthy: true },
table: {
type: { summary: "string" },
category: "Direct actions",
},
},
hasSwitches: {
name: "Has switches",
description: "Adds a switch to each accordion item header, in the direct actions section.",
type: { name: "boolean" },
table: {
type: { summary: "boolean" },
category: "Direct actions",
},
control: { type: "boolean" },
},
},
args: {
rootClass: "spectrum-Accordion",
Expand All @@ -70,6 +100,9 @@ export default {
disableAll: false,
isQuiet: false,
hasNoInlinePadding: false,
hasActionButtons: false,
actionButtonIconName: "Circle",
hasSwitches: false,
},
parameters: {
actions: {
Expand Down Expand Up @@ -121,9 +154,9 @@ export const CustomWidth = AccordionGroup.bind({});
CustomWidth.tags = ["!dev"];
CustomWidth.storyName = "Custom width";
CustomWidth.args = {
items: accordionContent,
items: longerContent,
customStyles: {
"--mod-accordion-item-width": "500px",
"--mod-accordion-item-width": "auto",
},
};
CustomWidth.parameters = {
Expand Down Expand Up @@ -158,6 +191,18 @@ Spacious.parameters = {
};
Spacious.storyName = "Density: Spacious";

/**
* Direct actions within accordion items are supported. A quiet
* [action button](/?path=/docs/actionbutton--default), a
* [switch](/?path=/docs/switch--default), or both can be added to
* each accordion item header.
*/
export const DirectActions = Template.bind({});
DirectActions.tags = ["!dev"];
DirectActions.args = {
items: directActionsContent
};

/**
* Individual accordion items can be disabled by applying the `.is-disabled` class to the
* `.spectrum-Accordion-item` element. This example also demonstrates the use of the disabled
Expand Down
110 changes: 105 additions & 5 deletions components/accordion/stories/accordion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,71 @@ import { Template } from "./template.js";

/**
* General accordion item content for tests.
* Content sourced from: https://www.adobe.com/products/catalog.html#:~:text=Frequently%20asked%20questions.
*/
export const testsContent = new Map([
[
"Abstract",
{
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend est mollis ligula lobortis, tempus ultricies sapien lacinia. Nulla ut turpis velit. Sed finibus dapibus diam et sollicitudin. Phasellus in ipsum nec ante elementum congue eget in leo. Morbi eleifend justo non rutrum venenatis. Fusce cursus et lectus eu facilisis. Ut laoreet felis in magna dignissim feugiat.",
},
],
[
"Architecture",
{
content: Typography({
semantics: "body",
content: [
"This is an example of text content within the content area that uses the spectrum-Body typography class on the paragraphs. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend est mollis ligula lobortis, tempus ultricies sapien lacinia. Nulla ut turpis velit.",
Link({
url: "https://www.adobe.com/creativecloud/plans.html",
text: "Learn more about Adobe Creative Cloud plans and pricing.",
}),
],
}),
isOpen: true,
},
],
[
"Nature",
{
content: Typography({
semantics: "body",
content: [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend est mollis ligula lobortis, tempus ultricies sapien lacinia. Nulla ut turpis velit. Sed finibus dapibus diam et sollicitudin. Phasellus in ipsum nec ante elementum congue eget in leo. Morbi eleifend justo non rutrum venenatis. Fusce cursus et lectus eu facilisis. Ut laoreet felis in magna dignissim feugiat.",
Link({
url: "https://www.adobe.com/acrobat.html",
text: "Learn more about Acrobat.",
}),
],
}),
},
],
[
"Illustrations",
{
content: "This is an example of text content within the content area that is not wrapped by any typography classes or elements. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend est mollis ligula lobortis, tempus ultricies sapien lacinia. Nulla ut turpis velit.",
isOpen: true,
},
],
[
"Business",
{
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend est mollis ligula lobortis, tempus ultricies sapien lacinia. Nulla ut turpis velit. Sed finibus dapibus diam et sollicitudin. Phasellus in ipsum nec ante elementum congue eget in leo. Morbi eleifend justo non rutrum venenatis. Fusce cursus et lectus eu facilisis. Ut laoreet felis in magna dignissim feugiat.",
},
],
[
"Landscape and longer text that wraps",
{
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend est mollis ligula lobortis, tempus ultricies sapien lacinia. Nulla ut turpis velit. Sed finibus dapibus diam et sollicitudin. Phasellus in ipsum nec ante elementum congue eget in leo. Morbi eleifend justo non rutrum venenatis. Fusce cursus et lectus eu facilisis. Ut laoreet felis in magna dignissim feugiat.",
},
],
]);

/**
* Longer content for a custom width accordion.
* Content sourced from: https://www.adobe.com/products/catalog.html#:~:text=Frequently%20asked%20questions.
*/
export const longerContent = new Map([
[
"Are any Adobe products free?",
{
Expand Down Expand Up @@ -60,6 +122,32 @@ export const testsContent = new Map([
],
]);

export const directActionsContent = new Map([
[
"Heading 1",
{
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
hasActionButton: true,
},
],
[
"Heading 2",
{
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
hasSwitch: true,
},
],
[
"Heading 3",
{
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
hasActionButton: true,
hasSwitch: true,
isOpen: true,
},
],
]);

const defaultContentText = "This is an example of text content within the content area that is not wrapped by any typography classes or elements.";

/**
Expand Down Expand Up @@ -121,6 +209,7 @@ const statesTestContent = new Map([
]);

export const AccordionGroup = Variants({
withSizes: false,
Template,
testData: [
{
Expand Down Expand Up @@ -177,8 +266,8 @@ export const AccordionGroup = Variants({
withStates: false,
},
{
testHeading: "Compact",
items: testsContent,
testHeading: "Compact density",
items: new Map([...testsContent, ...directActionsContent]),
Template: (args, context) => { return Sizes({Template: Template, ...args}, context); },
density: "compact",
collapseAll: true,
Expand All @@ -188,8 +277,19 @@ export const AccordionGroup = Variants({
withStates: false,
},
{
testHeading: "Spacious",
items: testsContent,
testHeading: "Regular density",
items: new Map([...testsContent, ...directActionsContent]),
Template: (args, context) => { return Sizes({Template: Template, ...args}, context); },
density: "regular",
collapseAll: true,
customStyles: {
maxInlineSize: "500px",
},
withStates: false,
},
{
testHeading: "Spacious density",
items: new Map([...testsContent, ...directActionsContent]),
Template: (args, context) => { return Sizes({Template: Template, ...args}, context); },
density: "spacious",
collapseAll: true,
Expand Down
Loading
Loading