Skip to content

Commit

Permalink
docs(popover): docs page to storybook migration
Browse files Browse the repository at this point in the history
Moves and re-organizes all of the docs site documentation for Popover
to Storybook, and creates new stories to represent missing examples
from the docs.

Displays the formerly chromatic-only list of all positions in a story
for the docs, now organized within columns in a grid.

Refactors and improves some of the play() functions. Fixes the issue
where clicking between the different Storybook stories for Popover would
result in the Popover sometimes closing instead of being open.
  • Loading branch information
jawinn committed Jul 29, 2024
1 parent 37c6260 commit c42aff5
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 25 deletions.
181 changes: 167 additions & 14 deletions components/popover/stories/popover.stories.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import { Template as ActionButton } from "@spectrum-css/actionbutton/stories/template.js";
import { Template as Dialog } from "@spectrum-css/dialog/stories/template.js";
import { Template as Menu } from "@spectrum-css/menu/stories/template.js";
import { disableDefaultModes } from "@spectrum-css/preview/modes";
import { isOpen } from "@spectrum-css/preview/types";
import { Template as Typography } from "@spectrum-css/typography/stories/template.js";
import { html } from "lit";
import { version } from "../package.json";
import { Template, Variants } from "./template";
import { FixedWidthSourceTemplate, Template, TipPlacementVariants } from "./template";

/**
* A popover is used to display transient content (menus, options, additional actions etc.) and appears when clicking/tapping on a source (tools, buttons, etc.). It stands out via its visual style (stroke and drop shadow) and floats on top of the rest of the interface.
* A popover is used to display transient content (menus, options, additional actions, etc.) and appears when clicking/tapping on a source (tools, buttons, etc.).
* It stands out via its visual style (stroke and drop shadow) and floats on top of the rest of the interface.
*
* - Popover's position and distance to its source should be handled by the implementation. Positioning in Storybook is only for demonstration purposes.
* - When the `.is-open` class is present, popover is offset from the source by the spacing value defined in `--spectrum-popover-animation-distance`. This
* offset is done with a CSS transform and animates with a CSS transition.
*/
export default {
title: "Popover",
component: "Popover",
argTypes: {
trigger: { table: { disable: true } },
triggerId: { table: { disable: true } },
content: { table: { disable: true } },
nested: { table: { disable: true } },
isOpen,
withTip: {
name: "Show with tip",
type: { name: "boolean" },
defaultValue: { summary: "false" },
table: {
type: { summary: "boolean" },
category: "Component",
Expand All @@ -28,7 +38,8 @@ export default {
},
position: {
name: "Positioning",
type: { name: "string" },
description: "Determines which side the popover is positioned on and where the tip appears. The first position term is the popover's position and the second term is the source's position.",
type: { name: "string", required: true },
table: {
type: { summary: "string" },
category: "Component",
Expand Down Expand Up @@ -69,10 +80,9 @@ export default {
content: [html`<div style="padding-inline: 8px;">Basic popover text content with some added padding.</div>`],
},
parameters: {
layout: "centered",
docs: {
story: {
height: "300px"
height: "300px",
}
},
componentVersion: version,
Expand All @@ -90,7 +100,7 @@ Default.args = {
triggerId: "trigger",
trigger: (passthroughs) => ActionButton({
isSelected: passthroughs.isOpen,
label: "Hop on pop(over)",
label: "Toggle popover",
id: "trigger",
...passthroughs,
}),
Expand Down Expand Up @@ -118,12 +128,15 @@ Default.args = {
}),
],
};

export const WithTip = Variants.bind({});
WithTip.args = {
withTip: true,
// This is excluded from the Docs page because the toggle interactions do not work correctly there.
Default.tags = ["!autodocs"];
Default.parameters = {
layout: "centered",
};

/**
* A popover can be nested within another popover.
*/
export const Nested = Template.bind({});
Nested.args = {
nested: true,
Expand All @@ -135,7 +148,7 @@ Nested.args = {
"margin-inline-start": "8px",
},
trigger: (passthroughs) => ActionButton({
label: "Hop on pop(over)",
label: "Source 1",
id: "trigger-nested",
...passthroughs,
}),
Expand All @@ -155,11 +168,12 @@ Nested.args = {
triggerId: "trigger-nested-2",
isOpen: true,
customStyles: {
"margin-inline-start": "136px",
// @todo This manual test of positioning does not support "Large" scale.
"margin-inline-start": "73px",
"margin-block-start": "32px"
},
trigger: (passthroughs) => ActionButton({
label: "Hop on pop(over) 2",
label: "Source 2",
id: "trigger-nested-2",
...passthroughs,
}),
Expand Down Expand Up @@ -188,14 +202,153 @@ Nested.args = {
}),
],
};
Nested.tags = ["!dev"];

/**
* Popovers can display different elements within their content area. This example uses the
* [dialog](?path=/docs/components-dialog--docs) component within the popover.
*/
export const DialogStyle = Template.bind({});
DialogStyle.storyName = "Dialog style content";
DialogStyle.args = {
withTip: true,
isOpen: true,
trigger: () => null,
content: [
() => Dialog({
showModal: false,
// @todo replace custom class with 'size: "small"' arg when added Dialog control is merged.
customClasses: ["spectrum-Dialog--small"],
isDismissable: false,
heading: "Example heading",
content: [
() => Typography({
semantics: "body",
size: "m",
content: [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud."
]
}),
],
}),
],
};
DialogStyle.tags = ["!dev"];

/**
* Popover has 22 available positions. Ten of those positions use logical properties. Position classes using
* the following naming convention: the first term is the popover's position and the second term is its
* source's position. For example, for the `spectrum-Popover--top-left` class, the popover is positioned at the top and the
* source is to left.
*
* #### Tip SVG
* Depending on its position, the tip uses one of two different SVGs.
* - Top and bottom popover positions use the same SVG. The CSS handles flipping the SVG vertically.
* - Left, right, start, and end popover positions use the same SVG. The CSS handles flipping the SVG horizontally.
*/
export const Positioning = TipPlacementVariants.bind({});
Positioning.storyName = "Positioning options";
Positioning.args = {
withTip: true,
isOpen: true,
trigger: () => null,
};
Positioning.tags = ["!dev"];

/**
* #### Default tip positioning
* - The tip position is centered on the edge for top, bottom, left, right, start, and end positions.
* - The tip position distance from edge is equal to the popover corner radius for all other positions.
*
* #### Centering the tip with the source
* In implementations, the tip position can be overridden to center it with the source by setting the
* custom property `--spectrum-popover-pointer-edge-offset` equal to half the width of the source for
* top and bottom popovers, or half the height of the source for side popovers. The following
* example sets this custom property to `50px` for a source button that is `100px` wide.
*/
export const TipOffset = FixedWidthSourceTemplate.bind({});
TipOffset.storyName = "Tip positioning and inline offset";
TipOffset.args = {
withTip: true,
isOpen: true,
trigger: () => null,
content: [
() => Menu({
items: [
{
iconName: "Edit",
label: "Example longer menu item",
},
{
iconName: "Copy",
label: "Copy",
},
{
iconName: "Move",
label: "Move",
},
],
}),
],
customStyles: {
"--spectrum-popover-pointer-edge-offset": "50px",
}
};
TipOffset.tags = ["!dev"];

// ********* VRT ONLY ********* //
export const WithForcedColors = Default.bind({});
WithForcedColors.args = Default.args;
WithForcedColors.tags = ["!autodocs", "!dev", "test"];
WithForcedColors.tags = ["!autodocs", "!dev"];
WithForcedColors.parameters = {
chromatic: {
forcedColors: "active",
modes: disableDefaultModes
},
};

// ********* DOCS ONLY ********* //
/**
* By default, popovers do not have a tip. Popovers without a tip should be used when the source has a
* visually distinct down state, in order to show the connection between the popover and its source.
*
* This example uses the [menu](?path=/docs/components-menu--docs) component within the popover.
*/
export const Standard = Template.bind({});
Standard.storyName = "Default";
Standard.args = {
...Default.args,
isOpen: true,
trigger: () => null,
};
Standard.tags = ["!dev"];
Standard.parameters = {
docs: {
story: {
height: "200px",
},
},
chromatic: { disableSnapshot: true },
};

/**
* Popovers can have a tip. A tip should be used to help show the connection to its source, in cases
* where the source does not have a visually distinct down state.
*/
export const StandardWithTip = Template.bind({});
StandardWithTip.storyName = "Default with tip";
StandardWithTip.args = {
...Default.args,
withTip: true,
isOpen: true,
trigger: () => null,
};
StandardWithTip.tags = ["!dev"];
StandardWithTip.parameters = {
docs: {
story: {
height: "200px",
},
},
chromatic: { disableSnapshot: true },
};
44 changes: 33 additions & 11 deletions components/popover/stories/template.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Template as ActionButton } from "@spectrum-css/actionbutton/stories/template.js";
import { renderContent } from "@spectrum-css/preview/decorators";
import { Template as Typography } from "@spectrum-css/typography/stories/template.js";
import { html } from "lit";
Expand Down Expand Up @@ -179,19 +180,21 @@ export const Template = ({
`;
};

export const Variants = (args, context) => {
/**
* Template that displays a Popover with every value of the "position" option.
*/
export const TipPlacementVariants = (args, context) => {
const placementOptions = context?.argTypes?.position?.options ?? [];
return html`
<div style=${styleMap({
"display": window.isChromatic() ? "none" : "contents",
})}>
${Template(args, context)}
</div>
<div style=${styleMap({
"display": window.isChromatic() ? "flex" : "none",
"flex-direction": "column",
"align-items": "flex-start",
})} class="spectrum-Typography">
<div
style=${styleMap({
"display": "grid",
"gap": "16px",
"grid-template-columns": "repeat(auto-fit, minmax(232px, 1fr))",
"max-width": "1000px",
})}
class="spectrum-Typography"
>
${placementOptions.map(option => {
let optionDescription;
if (option.startsWith("start") || option.startsWith("end"))
Expand Down Expand Up @@ -237,3 +240,22 @@ export const Variants = (args, context) => {
</div>
`;
};

/**
* Contains a source button with a fixed width, and an always open Popover.
*/
export const FixedWidthSourceTemplate = (args) => html`
${ActionButton({
label: "Source",
customStyles: {
width: "100px",
display: "block",
},
})}
${Template({
...args,
position: "bottom-start",
isOpen: true,
trigger: () => null,
})}
`;

0 comments on commit c42aff5

Please sign in to comment.