From 0626aa0f5ea4e0499b9761c8b0d75e32cf397f88 Mon Sep 17 00:00:00 2001 From: Melissa Thompson Date: Tue, 19 Mar 2024 14:27:52 -0400 Subject: [PATCH] feat(downstate): docs + implementation for example components (#2520) * feat(downstate): docs + implementation for example components * docs: update mdx * docs: reorg, stories live within foundations * docs: decorator for down state dimension tokens * docs: fix mdx hierarchy console error * fix: small iconOnly button gets min perspective * docs: use markdown, update language * fix: disabled, readonly checkbox doesnt have down state * chore(button,checkbox): update package versions --- .storybook/decorators/index.js | 1 + .../withDownStateDimensionCapture.js | 19 ++++++++ .../down-state/button-down-state.stories.js | 32 ++++++++++++++ .../down-state/checkbox-down-state.stories.js | 27 ++++++++++++ .../foundations/down-state/down-state.mdx | 44 +++++++++++++++++++ .storybook/main.js | 2 + .storybook/manager.js | 2 +- .storybook/preview.js | 12 ++--- components/button/index.css | 13 ++++++ components/button/package.json | 2 +- components/button/stories/button.stories.js | 3 ++ components/checkbox/index.css | 6 +++ 12 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 .storybook/decorators/withDownStateDimensionCapture.js create mode 100644 .storybook/foundations/down-state/button-down-state.stories.js create mode 100644 .storybook/foundations/down-state/checkbox-down-state.stories.js create mode 100644 .storybook/foundations/down-state/down-state.mdx diff --git a/.storybook/decorators/index.js b/.storybook/decorators/index.js index 9ca62537dc3..6fa9a859f73 100644 --- a/.storybook/decorators/index.js +++ b/.storybook/decorators/index.js @@ -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"; /** diff --git a/.storybook/decorators/withDownStateDimensionCapture.js b/.storybook/decorators/withDownStateDimensionCapture.js new file mode 100644 index 00000000000..616852d34ce --- /dev/null +++ b/.storybook/decorators/withDownStateDimensionCapture.js @@ -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(); + }, 100); + }); + + return Story(context); +}; diff --git a/.storybook/foundations/down-state/button-down-state.stories.js b/.storybook/foundations/down-state/button-down-state.stories.js new file mode 100644 index 00000000000..e938e302aa6 --- /dev/null +++ b/.storybook/foundations/down-state/button-down-state.stories.js @@ -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" + } +}; diff --git a/.storybook/foundations/down-state/checkbox-down-state.stories.js b/.storybook/foundations/down-state/checkbox-down-state.stories.js new file mode 100644 index 00000000000..889c4fe4ce4 --- /dev/null +++ b/.storybook/foundations/down-state/checkbox-down-state.stories.js @@ -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", +}; diff --git a/.storybook/foundations/down-state/down-state.mdx b/.storybook/foundations/down-state/down-state.mdx new file mode 100644 index 00000000000..f07e65d2f8d --- /dev/null +++ b/.storybook/foundations/down-state/down-state.mdx @@ -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'; + + + +# 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: + + + +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: + + + +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.* diff --git a/.storybook/main.js b/.storybook/main.js index 8910d9da2e9..8a024ddafa8 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -12,6 +12,8 @@ module.exports = { stories: [ "../components/*/stories/*.stories.js", "./guides/*.mdx", + "./foundations/*/*.mdx", + "./foundations/*/*.stories.js", "./deprecated/*/*.stories.js", ], rootDir: "../", diff --git a/.storybook/manager.js b/.storybook/manager.js index ceacc56a50d..fb843f695a3 100644 --- a/.storybook/manager.js +++ b/.storybook/manager.js @@ -50,7 +50,7 @@ addons.setConfig({ sidebar: { showRoots: false, filters: { - patterns: (item) => !item.id.includes('forced-colors'), + patterns: (item) => !(item.id.includes('forced-colors') || item.tags.includes('foundation')), } }, }); diff --git a/.storybook/preview.js b/.storybook/preview.js index 5578d8c2b73..8a3b42114e9 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -3,11 +3,11 @@ import DocumentationTemplate from './DocumentationTemplate.mdx'; import { withActions } from "@storybook/addon-actions/decorator"; import { - withContextWrapper, - withLanguageWrapper, - withReducedMotionWrapper, - withTestingPreviewWrapper, - withTextDirectionWrapper, + withContextWrapper, + withLanguageWrapper, + withReducedMotionWrapper, + withTestingPreviewWrapper, + withTextDirectionWrapper, } from "./decorators/index.js"; // https://github.com/storybookjs/storybook-addon-console @@ -191,7 +191,7 @@ export const parameters = { options: { storySort: { method: "alphabetical", - order: ['Guides', ['Contributing', '*', 'Adobe Code of Conduct', 'Changelog'], 'Components', ['Docs', 'Default', '*'], '*'], + order: ['Guides', ['Contributing', '*', 'Adobe Code of Conduct', 'Changelog'], 'Foundations', 'Components', ['Docs', 'Default', '*'], '*'], includeNames: true, }, }, diff --git a/components/button/index.css b/components/button/index.css index 748097bbc91..fbacf6151e1 100644 --- a/components/button/index.css +++ b/components/button/index.css @@ -26,7 +26,12 @@ governing permissions and limitations under the License. --spectrum-button-focus-indicator-color: var(--spectrum-focus-indicator-color); --spectrum-button-intended-icon-size: var(--spectrum-workflow-icon-size-50); +<<<<<<< HEAD --mod-progress-circle-position: absolute; +======= + /* 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)); +>>>>>>> 5a532e656 (feat(downstate): docs + implementation for example components (#2520)) } .spectrum-Button--sizeS { @@ -45,6 +50,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 { @@ -131,6 +140,10 @@ 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. */ diff --git a/components/button/package.json b/components/button/package.json index 0cd7fa29489..846939b9d7d 100644 --- a/components/button/package.json +++ b/components/button/package.json @@ -1,6 +1,6 @@ { "name": "@spectrum-css/button", - "version": "13.0.0", + "version": "14.0.0-next.2", "description": "The Spectrum CSS button component", "license": "Apache-2.0", "author": "Adobe", diff --git a/components/button/stories/button.stories.js b/components/button/stories/button.stories.js index 508cb2762e7..4489269f7bc 100644 --- a/components/button/stories/button.stories.js +++ b/components/button/stories/button.stories.js @@ -1,5 +1,7 @@ import { html } from "lit"; 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"; @@ -12,6 +14,7 @@ import { Template } from "./template"; export default { title: "Components/Button", component: "Button", + decorators: [withDownStateDimensionCapture('.spectrum-Button:not(:disabled)')], argTypes: { size: { name: "Size", diff --git a/components/checkbox/index.css b/components/checkbox/index.css index 2cb3aac560f..d4fbd4b9655 100644 --- a/components/checkbox/index.css +++ b/components/checkbox/index.css @@ -152,6 +152,12 @@ governing permissions and limitations under the License. } } + &:not(.is-readOnly):active { + .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,