Skip to content

feat(downstate): docs + implementation for example components #2520

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

Merged
merged 9 commits into from
Mar 19, 2024
Merged
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
1 change: 1 addition & 0 deletions .storybook/decorators/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { makeDecorator, useEffect } from "@storybook/preview-api";
import { html } from "lit";

export { withContextWrapper } from "./contextsWrapper.js";
export { withDownStateDimensionCapture } from "./withDownStateDimensionCapture.js";
export { withTestingPreviewWrapper } from "./withTestingPreviewWrapper.js";

/**
Expand Down
19 changes: 19 additions & 0 deletions .storybook/decorators/withDownStateDimensionCapture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const withDownStateDimensionCapture = (selector) => (Story, context) => {
const captureDownStateDimensions = () => {
const components = document.querySelectorAll(selector);
components.forEach((component) => {
const { width, height } = component.getBoundingClientRect();
component.style.setProperty('--spectrum-downstate-width', `${width}px`);
component.style.setProperty('--spectrum-downstate-height', `${height}px`);
});
};

document.addEventListener("DOMContentLoaded", () => {
// Wait to make sure the story is fully rendered (otherwise width/height can be wrong)
setTimeout(() => {
captureDownStateDimensions();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Awesome! Looks like these dimensions are being captured well in the Storybook buttons!

}, 100);
});

return Story(context);
};
32 changes: 32 additions & 0 deletions .storybook/foundations/down-state/button-down-state.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Template } from "../../../components/button/stories/template";

export default {
title: "Foundations/Down state",
description:
"Buttons allow users to perform an action or to navigate to another page. They have multiple styles for various needs, and are ideal for calling attention to where a user needs to do something in order to move forward in a flow.",
component: "Button",
args: {
rootClass: "spectrum-Button",
},
parameters: {
actions: {
handles: ['click .spectrum-Button'],
},
status: {
type: process.env.MIGRATED_PACKAGES.includes("button")
? "migrated"
: undefined,
},
},
tags: ['foundation'],
};

export const ButtonDownState = Template.bind({});
ButtonDownState.args = {
label: "Edit",
variant: "accent",
customStyles: {
"--spectrum-downstate-width": "72px",
"--spectrum-downstate-height": "32px"
}
};
27 changes: 27 additions & 0 deletions .storybook/foundations/down-state/checkbox-down-state.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Template } from "../../../components/checkbox/stories/template";

export default {
title: "Foundations/Down state",
description:
"Checkboxes allow users to select multiple items from a list of individual items, or mark one individual item as selected.",
component: "Checkbox",
args: {
rootClass: "spectrum-Checkbox",
},
parameters: {
actions: {
handles: ['click input[type="checkbox"]'],
},
status: {
type: process.env.MIGRATED_PACKAGES.includes("checkbox")
? "migrated"
: undefined,
},
},
tags: ['foundation'],
};

export const CheckboxDownState = Template.bind({});
CheckboxDownState.args = {
label: "Checkbox",
};
44 changes: 44 additions & 0 deletions .storybook/foundations/down-state/down-state.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Meta, Story } from '@storybook/blocks';
import * as Checkbox from './checkbox-down-state.stories.js';
import * as Button from './button-down-state.stories.js';

<Meta title="Foundations/Down state" />

# Down state

Down state is a Spectrum 2 feature that creates the illusion of components being pressed-in when active. This functionality is already included in Spectrum 2 components that require down state in this project. It is implemented with a CSS transform. The implementation depends on the size of the interactable element, as shown in the examples below.

## Examples

### Minimum perspective

For elements that have a width of 24px or less, the minimum perspective token is used to apply the down state. One example of a component that uses this token is the checkbox:

<Story of={Checkbox.CheckboxDownState} />

In this case, we use the minimum perspective token:

```
transform:
perspective(var(--spectrum-component-size-minimum-perspective-down))
translateZ(var(--spectrum-component-size-difference-down));
```

### Calculated perspective

For elements that have a width of greater than 24px, we need to use the component's width and height to apply the down state. One example of a component that uses this logic is the button:

<Story of={Button.ButtonDownState} />

In this case, we use a max formula to calculate the perspective based on component width and height (this helps us account for components that may be very wide):

```
transform:
perspective(max(
var(--spectrum-downstate-height),
var(--spectrum-downstate-width) * var(--spectrum-component-size-width-ratio-down)
))
translateZ(var(--spectrum-component-size-difference-down));
```

*Note that in this case, users are required to develop an implementation to determine the width and height of the component. Assign these values to the `--spectrum-downstate-width` and `--spectrum-downstate-height` custom properties in a `style` attribute on the HTML element to expose them for use in the CSS.*
2 changes: 2 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module.exports = {
stories: [
"../components/*/stories/*.stories.js",
"./guides/*.mdx",
"./foundations/*/*.mdx",
"./foundations/*/*.stories.js",
"./deprecated/*/*.stories.js",
],
rootDir: "../",
Expand Down
7 changes: 6 additions & 1 deletion .storybook/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import logo from "./assets/logo.svg";
import pkg from "./package.json";

// Load global styles
import "@spectrum-css/vars/css/globals/index.css";
import "@spectrum-css/vars/css/components/index.css";
import "@spectrum-css/vars/css/globals/index.css";

import "@spectrum-css/vars/css/scales/spectrum-medium.css";
import "@spectrum-css/vars/css/themes/spectrum-light.css";
Expand Down Expand Up @@ -53,5 +53,10 @@ addons.setConfig({
}),
sidebar: {
showRoots: false,
filters: {
patterns: (item) => {
return !item.tags.includes('foundation');
}
}
},
});
2 changes: 1 addition & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export const parameters = {
options: {
storySort: {
method: "alphabetical",
order: ['Guides', ['Contributing', '*', 'Adobe Code of Conduct', 'Changelog'], 'Components', '*'],
order: ['Guides', ['Contributing', '*', 'Adobe Code of Conduct', 'Changelog'], 'Foundations', 'Components', '*'],
includeNames: true,
},
},
Expand Down
15 changes: 13 additions & 2 deletions components/button/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ governing permissions and limitations under the License.
--spectrum-button-focus-ring-thickness: var(--spectrum-focus-indicator-thickness);
--spectrum-button-focus-indicator-color: var(--spectrum-focus-indicator-color);
--spectrum-button-intended-icon-size: var(--spectrum-workflow-icon-size-50);

/* stylelint-disable-next-line spectrum-tools/no-unknown-custom-properties */
--spectrum-downstate-perspective: max(var(--spectrum-downstate-height), var(--spectrum-downstate-width) * var(--spectrum-component-size-width-ratio-down));
}

.spectrum-Button--sizeS {
Expand All @@ -42,6 +45,10 @@ governing permissions and limitations under the License.
--spectrum-button-bottom-to-text: var(--spectrum-button-bottom-to-text-small);
--spectrum-button-top-to-icon: var(--spectrum-component-top-to-workflow-icon-75);
--spectrum-button-intended-icon-size: var(--spectrum-workflow-icon-size-75);

&.spectrum-Button--iconOnly {
--spectrum-downstate-perspective: var(--spectrum-component-size-minimum-perspective-down);
}
}

.spectrum-Button--sizeM {
Expand Down Expand Up @@ -147,17 +154,21 @@ governing permissions and limitations under the License.
box-shadow: none;
}

&:active {
transform: perspective(var(--spectrum-downstate-perspective)) translateZ(var(--spectrum-component-size-difference-down));
}

.spectrum-Icon {
/* Any block-size difference between the intended workflow icon size and actual icon used.
Helps support any existing use of smaller UI icons instead of intended Workflow icons. */
--_icon-size-difference: max(0px,
var(--spectrum-button-intended-icon-size) -
var(--spectrum-button-intended-icon-size) -
var(--spectrum-icon-block-size, var(--spectrum-button-intended-icon-size))
);

margin-block-start: var(--mod-button-icon-margin-block-start,
max(0px,
var(--mod-button-top-to-icon, var(--spectrum-button-top-to-icon)) -
var(--mod-button-top-to-icon, var(--spectrum-button-top-to-icon)) -
var(--mod-button-border-width, var(--spectrum-button-border-width)) +
(var(--_icon-size-difference, 0px) / 2)
)
Expand Down
2 changes: 1 addition & 1 deletion components/button/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@spectrum-css/button",
"version": "12.0.3-next.0",
"version": "14.0.0-next.2",
"description": "The Spectrum CSS button component",
"license": "Apache-2.0",
"author": "Adobe",
Expand Down
6 changes: 4 additions & 2 deletions components/button/stories/button.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { html } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
import { styleMap } from "lit/directives/style-map.js";
import { when } from "lit/directives/when.js";
import { withDownStateDimensionCapture } from "../../../.storybook/decorators";

import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js";
import { Template as Typography } from "@spectrum-css/typography/stories/template.js";
Expand All @@ -12,6 +13,7 @@ export default {
description:
"Buttons allow users to perform an action or to navigate to another page. They have multiple styles for various needs, and are ideal for calling attention to where a user needs to do something in order to move forward in a flow.",
component: "Button",
decorators: [withDownStateDimensionCapture('.spectrum-Button:not(:disabled)')],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This will need to be added to all stories of components that require the capture of width/height as they migrate to S2. I'll try to note that in the appropriate Jira tickets.

argTypes: {
size: {
name: "Size",
Expand Down Expand Up @@ -100,7 +102,7 @@ export default {
name: "Layout",
description: "How the buttons align in the preview (Storybook only).",
type: { name: "string" },
table: {
table: {
type: { summary: "string" },
category: "Advanced"
},
Expand Down Expand Up @@ -349,4 +351,4 @@ Wrapping.args = {
showIconOnlyButton: false,
variant: "accent",
label: "An example of text overflow behavior within the button component. When the button text is too long for the horizontal space available, it wraps to form another line.",
};
};
9 changes: 8 additions & 1 deletion components/checkbox/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ governing permissions and limitations under the License.
}
}

&:not(.is-readOnly):active {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This approach will not work in case the SWC wants to disable downstate for checkbox. This is tightly coupled with the css transform. We need to cascade the --spectrum-downstate-width and --spectrum-downstate-height to this css.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hey Rajdeep! Can you help me understand what needs to change? One piece of context that might be helpful is for components like Checkbox which are smaller than 24px, the transform perspective is not based on the width and height of the component, it is based on a set value per the design spec.

.spectrum-Checkbox-input:not(:disabled) + .spectrum-Checkbox-box {
transform: perspective(var(--spectrum-component-size-minimum-perspective-down)) translateZ(var(--spectrum-component-size-difference-down));
}
}

/* Selected Invalid */
&.is-invalid {
.spectrum-Checkbox-input:checked + .spectrum-Checkbox-box,
Expand Down Expand Up @@ -372,6 +378,7 @@ governing permissions and limitations under the License.
}
}
}

/* stylelint-enable max-nesting-depth */

.spectrum-Checkbox-label {
Expand Down Expand Up @@ -571,7 +578,7 @@ governing permissions and limitations under the License.
outline-style: auto;
outline-offset: var(--highcontrast-checkbox-focus-indicator-gap, var(--mod-checkbox-focus-indicator-gap, var(--spectrum-checkbox-focus-indicator-gap)));
/* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */
outline-width: var(--mod-focus-indicator-thickness, var(--spectrum-focus-indicator-thickness));
outline-width: var(--mod-focus-indicator-thickness, var(--spectrum-focus-indicator-thickness));

&::after {
box-shadow:
Expand Down
2 changes: 1 addition & 1 deletion components/checkbox/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@spectrum-css/checkbox",
"version": "8.1.6-next.0",
"version": "14.0.0-next.2",
"description": "The Spectrum CSS checkbox component",
"license": "Apache-2.0",
"author": "Adobe",
Expand Down