From 3472bc19b085dac36aa78cc1593e0ab825cb2c7c Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Wed, 6 Aug 2025 15:32:58 -0400 Subject: [PATCH 1/9] feat(search): adding collapsed search feature --- components/search/dist/metadata.json | 2 + components/search/index.css | 14 +++++ components/search/stories/search.stories.js | 13 +++++ components/search/stories/search.test.js | 7 +++ components/search/stories/template.js | 63 ++++++++++++++------- 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/components/search/dist/metadata.json b/components/search/dist/metadata.json index f57d2c77c0f..0cb36004dea 100644 --- a/components/search/dist/metadata.json +++ b/components/search/dist/metadata.json @@ -22,7 +22,9 @@ ".spectrum-Search-textfield.is-focused:hover .spectrum-Search-icon", ".spectrum-Search-textfield.is-keyboardFocused .spectrum-Search-icon", ".spectrum-Search-textfield:hover .spectrum-Search-icon", + ".spectrum-Search.is-collapsed", ".spectrum-Search.is-disabled .spectrum-Search-clearButton", + ".spectrum-Search.is-expanded", ".spectrum-Search:lang(ja)", ".spectrum-Search:lang(ko)", ".spectrum-Search:lang(zh)" diff --git a/components/search/index.css b/components/search/index.css index 5ca303824a4..6a3c181f190 100644 --- a/components/search/index.css +++ b/components/search/index.css @@ -149,6 +149,20 @@ .spectrum-HelpText { margin-block-start: var(--mod-search-to-help-text, var(--spectrum-search-to-help-text)); } + + /* Animation for collapsible search expansion */ + &.is-collapsed { + transition: inline-size 0.3s cubic-bezier(0.4, 0, 0.2, 1); + inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); + min-inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); + transform-origin: left center; + } + + &.is-expanded { + transition: inline-size 0.3s cubic-bezier(0.4, 0, 0.2, 1); + inline-size: var(--mod-search-inline-size, var(--spectrum-search-inline-size)); + transform-origin: left center; + } } .spectrum-Search-clearButton { diff --git a/components/search/stories/search.stories.js b/components/search/stories/search.stories.js index ad429e00554..cef0e53d1e3 100644 --- a/components/search/stories/search.stories.js +++ b/components/search/stories/search.stories.js @@ -28,6 +28,7 @@ export default { category: "Content", }, control: "boolean", + if: { arg: "isCollapsed", eq: false }, }, helpTextLabel: { name: "Help text (description)", @@ -50,6 +51,16 @@ export default { type: { summary: "string" }, category: "Content", }, + if: { arg: "isCollapsed", eq: false }, + }, + isCollapsed: { + name: "Collapsed", + type: { name: "boolean" }, + table: { + type: { summary: "boolean" }, + category: "Component", + }, + control: "boolean", }, }, args: { @@ -62,6 +73,7 @@ export default { showHelpText: false, helpTextLabel: "Help text with a suggestion of what to search for", inputValue: "", + isCollapsed: false, }, parameters: { actions: { @@ -69,6 +81,7 @@ export default { "change .spectrum-Search-input", "click .spectrum-Search-clearButton", "click .spectrum-Search-icon", + "click .spectrum-Search-actionButton", ], }, design: { diff --git a/components/search/stories/search.test.js b/components/search/stories/search.test.js index 889dc7a6ee1..88ba19ebaff 100644 --- a/components/search/stories/search.test.js +++ b/components/search/stories/search.test.js @@ -17,6 +17,10 @@ export const SearchGroup = Variants({ inputValue: "What should we search for?", withStates: false, }, + { + testHeading: "Collapsed", + isCollapsed: true, + } ], stateData: [ { @@ -30,15 +34,18 @@ export const SearchGroup = Variants({ { testHeading: "Focused", isFocused: true, + ignore: ["Collapsed"], }, { testHeading: "Focused + hovered", isFocused: true, isHovered: true, + ignore: ["Collapsed"], }, { testHeading: "Keyboard focused", isKeyboardFocused: true, + ignore: ["Collapsed"], }, ] }); diff --git a/components/search/stories/template.js b/components/search/stories/template.js index 9703bce7322..3e698b164d0 100644 --- a/components/search/stories/template.js +++ b/components/search/stories/template.js @@ -1,3 +1,4 @@ +import { Template as ActionButton } from "@spectrum-css/actionbutton/stories/template.js"; import { Template as ClearButton } from "@spectrum-css/clearbutton/stories/template.js"; import { Template as HelpText } from "@spectrum-css/helptext/stories/template.js"; import { Container } from "@spectrum-css/preview/decorators"; @@ -19,7 +20,9 @@ export const Template = ({ size = "m", showHelpText = false, helpTextLabel = "", + isCollapsed = false, } = {}, context = {}) => { + const { updateArgs } = context; return html`
({ ...a, [c]: true }), {}), })} aria-label="Search" > - ${TextField({ - isDisabled, - size, - customClasses: [ - `${rootClass}-textfield`, - isFocused && "is-focused", - isKeyboardFocused && "is-keyboardFocused", - isHovered && "is-hover" - ], - iconName: "Search", - setName: "workflow", - type: "search", - placeholder: "Search", - name: "search", - customInputClasses: [`${rootClass}-input`], - customIconClasses: [`${rootClass}-icon`], - autocomplete: false, - value: inputValue, - }, context)} - ${when(inputValue, () => + ${when(isCollapsed, () => + ActionButton({ + iconName: "Search", + size, + customClasses: [ + `${rootClass}-actionButton`, + isHovered && "is-hover", + isDisabled && "is-disabled", + ], + onclick: () => { + updateArgs({ isCollapsed: !isCollapsed }); + }, + }, context) + )} + ${when(!isCollapsed, () => + TextField({ + isDisabled, + size, + customClasses: [ + `${rootClass}-textfield`, + isFocused && "is-focused", + isKeyboardFocused && "is-keyboardFocused", + isHovered && "is-hover" + ], + iconName: "Search", + setName: "workflow", + type: "search", + placeholder: "Search", + name: "search", + customInputClasses: [`${rootClass}-input`], + customIconClasses: [`${rootClass}-icon`], + autocomplete: false, + value: inputValue, + }, context) + )} + ${when(inputValue && !isCollapsed, () => ClearButton({ isDisabled, size, @@ -59,7 +80,7 @@ export const Template = ({ isFocusable: false, }, context) )} - ${when(showHelpText, () => + ${when(showHelpText && !isCollapsed, () => HelpText({ text: helpTextLabel, size, From 79e4fbee293b701f1f11f67ab8ef02a4e9827dbb Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Wed, 6 Aug 2025 15:33:19 -0400 Subject: [PATCH 2/9] feat(search): adding changeset --- .changeset/long-carrots-sleep.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .changeset/long-carrots-sleep.md diff --git a/.changeset/long-carrots-sleep.md b/.changeset/long-carrots-sleep.md new file mode 100644 index 00000000000..8a7bb86864d --- /dev/null +++ b/.changeset/long-carrots-sleep.md @@ -0,0 +1,15 @@ +--- +"@spectrum-css/search": major +--- + +## S2 Minimized search field + +The search component allows for a minimized state where the search field is collapsed to a button. + +### Anatomy + +The collapsed state consists of a single action button that only has a hover and disabled state. This state is triggered by the `is-collapsed` class on the search component. When the search field is in this state, the textfield is hidden and the search button is displayed. The button can be hovered and focused, and will expand the search field when clicked. + +### Usage + +The collapsed state is used to reduce the amount of space taken up by the search field. It is most commonly used next a filter button to allow users to quickly search and filter content. From 3579e941da25a502b4f926df17f465259d0af27b Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Fri, 8 Aug 2025 12:24:39 -0400 Subject: [PATCH 3/9] feat(search): adding keyboard focused state for collapsed search --- components/search/stories/search.stories.js | 12 ++++++++++++ components/search/stories/search.test.js | 1 - components/search/stories/template.js | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/components/search/stories/search.stories.js b/components/search/stories/search.stories.js index cef0e53d1e3..5eaecf655f1 100644 --- a/components/search/stories/search.stories.js +++ b/components/search/stories/search.stories.js @@ -154,6 +154,18 @@ WithValue.parameters = { }; WithValue.storyName = "With value and clear button"; +/** + * A search field can be collapsed to show only the search button. This is useful when there is limited space available. It is most commonly used next a filter button to allow users to quickly search and filter content. + */ +export const Collapsed = Template.bind({}); +Collapsed.args = { + isCollapsed: true, +}; +Collapsed.tags = ["!dev"]; +Collapsed.parameters = { + chromatic: { disableSnapshot: true }, +}; + /** * The medium size is the default and most frequently used option. Use the other sizes sparingly; they should be used to create a hierarchy of importance within the page. */ diff --git a/components/search/stories/search.test.js b/components/search/stories/search.test.js index 88ba19ebaff..44b19ef2e6f 100644 --- a/components/search/stories/search.test.js +++ b/components/search/stories/search.test.js @@ -45,7 +45,6 @@ export const SearchGroup = Variants({ { testHeading: "Keyboard focused", isKeyboardFocused: true, - ignore: ["Collapsed"], }, ] }); diff --git a/components/search/stories/template.js b/components/search/stories/template.js index 3e698b164d0..a229de55bee 100644 --- a/components/search/stories/template.js +++ b/components/search/stories/template.js @@ -41,6 +41,7 @@ export const Template = ({ ActionButton({ iconName: "Search", size, + isFocused: isFocused || isKeyboardFocused, customClasses: [ `${rootClass}-actionButton`, isHovered && "is-hover", From 324c9169c02c097c596d1ce43283f1c0a5a7e766 Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Mon, 11 Aug 2025 11:12:41 -0400 Subject: [PATCH 4/9] feat(search): updating changeset copy --- .changeset/long-carrots-sleep.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/long-carrots-sleep.md b/.changeset/long-carrots-sleep.md index 8a7bb86864d..03362d6c3da 100644 --- a/.changeset/long-carrots-sleep.md +++ b/.changeset/long-carrots-sleep.md @@ -2,13 +2,13 @@ "@spectrum-css/search": major --- -## S2 Minimized search field +## S2 Collapsed search field The search component allows for a minimized state where the search field is collapsed to a button. ### Anatomy -The collapsed state consists of a single action button that only has a hover and disabled state. This state is triggered by the `is-collapsed` class on the search component. When the search field is in this state, the textfield is hidden and the search button is displayed. The button can be hovered and focused, and will expand the search field when clicked. +The collapsed state consists of a single action button that has a hover, keyboard focused, and disabled state. This state is triggered by the `is-collapsed` class on the search component. When the search field is in this state, the textfield is hidden and the search button is displayed. The button can be hovered and focused, and will expand the search field when clicked. ### Usage From e26ae559e2340d6157d4b9c574e4f07d2cb2b5f2 Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Thu, 14 Aug 2025 11:24:22 -0400 Subject: [PATCH 5/9] feat(search): adding quiet variant to button --- components/search/stories/template.js | 1 + 1 file changed, 1 insertion(+) diff --git a/components/search/stories/template.js b/components/search/stories/template.js index a229de55bee..dd0cb04a4e9 100644 --- a/components/search/stories/template.js +++ b/components/search/stories/template.js @@ -42,6 +42,7 @@ export const Template = ({ iconName: "Search", size, isFocused: isFocused || isKeyboardFocused, + isQuiet: true, customClasses: [ `${rootClass}-actionButton`, isHovered && "is-hover", From 9ae58b771e1581020fdafcf18d748e4c0851ed49 Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Thu, 14 Aug 2025 15:12:06 -0400 Subject: [PATCH 6/9] feat(search): adding min inline size for elongated animation --- components/search/index.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/search/index.css b/components/search/index.css index 6a3c181f190..5f418bae2eb 100644 --- a/components/search/index.css +++ b/components/search/index.css @@ -152,15 +152,16 @@ /* Animation for collapsible search expansion */ &.is-collapsed { - transition: inline-size 0.3s cubic-bezier(0.4, 0, 0.2, 1); + transition: inline-size 0.3s ease-in-out; inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); min-inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); transform-origin: left center; } &.is-expanded { - transition: inline-size 0.3s cubic-bezier(0.4, 0, 0.2, 1); + transition: inline-size 0.3s ease-in-out; inline-size: var(--mod-search-inline-size, var(--spectrum-search-inline-size)); + min-inline-size: auto; transform-origin: left center; } } From 57931fdd96f4c36445991c7ae8e5f19320d5d8ce Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Fri, 15 Aug 2025 10:42:34 -0400 Subject: [PATCH 7/9] feat(search): adding animation speed and proper disabled state --- components/search/dist/metadata.json | 1 + components/search/index.css | 4 ++-- components/search/stories/template.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/components/search/dist/metadata.json b/components/search/dist/metadata.json index 0cb36004dea..c0ce77fd78a 100644 --- a/components/search/dist/metadata.json +++ b/components/search/dist/metadata.json @@ -104,6 +104,7 @@ "--spectrum-search-top-to-text" ], "global": [ + "--spectrum-animation-duration-800", "--spectrum-border-width-200", "--spectrum-cjk-line-height-100", "--spectrum-component-bottom-to-text-100", diff --git a/components/search/index.css b/components/search/index.css index 5f418bae2eb..129c940e396 100644 --- a/components/search/index.css +++ b/components/search/index.css @@ -152,14 +152,14 @@ /* Animation for collapsible search expansion */ &.is-collapsed { - transition: inline-size 0.3s ease-in-out; + transition: inline-size var(--spectrum-animation-duration-800) ease-in-out; inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); min-inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); transform-origin: left center; } &.is-expanded { - transition: inline-size 0.3s ease-in-out; + transition: inline-size var(--spectrum-animation-duration-800) ease-in-out; inline-size: var(--mod-search-inline-size, var(--spectrum-search-inline-size)); min-inline-size: auto; transform-origin: left center; diff --git a/components/search/stories/template.js b/components/search/stories/template.js index dd0cb04a4e9..ac272e1cf96 100644 --- a/components/search/stories/template.js +++ b/components/search/stories/template.js @@ -41,7 +41,7 @@ export const Template = ({ ActionButton({ iconName: "Search", size, - isFocused: isFocused || isKeyboardFocused, + isFocused: !isDisabled && (isFocused || isKeyboardFocused), isQuiet: true, customClasses: [ `${rootClass}-actionButton`, From 9324fddbcb4db5ae81136876532f3f0caeff3458 Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Fri, 15 Aug 2025 12:31:45 -0400 Subject: [PATCH 8/9] feat(search): remove tabbing when button is disabled --- components/search/stories/template.js | 1 + 1 file changed, 1 insertion(+) diff --git a/components/search/stories/template.js b/components/search/stories/template.js index ac272e1cf96..49f1c398b19 100644 --- a/components/search/stories/template.js +++ b/components/search/stories/template.js @@ -40,6 +40,7 @@ export const Template = ({ ${when(isCollapsed, () => ActionButton({ iconName: "Search", + isDisabled, size, isFocused: !isDisabled && (isFocused || isKeyboardFocused), isQuiet: true, From 62498f8109df6e8c94205fc24f5d2ef8fb9e25f7 Mon Sep 17 00:00:00 2001 From: Aziz Ramos Date: Fri, 15 Aug 2025 12:41:47 -0400 Subject: [PATCH 9/9] feat(search): adding component specific collapsed animation token --- components/search/dist/metadata.json | 2 ++ components/search/index.css | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/search/dist/metadata.json b/components/search/dist/metadata.json index c0ce77fd78a..0d4bd84abfe 100644 --- a/components/search/dist/metadata.json +++ b/components/search/dist/metadata.json @@ -43,6 +43,7 @@ "--mod-search-border-width", "--mod-search-bottom-to-text", "--mod-search-button-inline-size", + "--mod-search-collapsed-animation-duration", "--mod-search-color-default", "--mod-search-color-disabled", "--mod-search-color-focus", @@ -79,6 +80,7 @@ "--spectrum-search-border-width", "--spectrum-search-bottom-to-text", "--spectrum-search-button-inline-size", + "--spectrum-search-collapsed-animation-duration", "--spectrum-search-color", "--spectrum-search-color-default", "--spectrum-search-color-disabled", diff --git a/components/search/index.css b/components/search/index.css index 129c940e396..675279bb8f0 100644 --- a/components/search/index.css +++ b/components/search/index.css @@ -66,6 +66,9 @@ /* stylelint-disable-next-line spectrum-tools/no-unused-custom-properties -- used to assign Textfield mods */ --spectrum-search-border-color-disabled: var(--spectrum-disabled-border-color); + /* Collapsed Search */ + --spectrum-search-collapsed-animation-duration: var(--spectrum-animation-duration-800); + &:lang(ja), &:lang(zh), &:lang(ko) { @@ -152,14 +155,14 @@ /* Animation for collapsible search expansion */ &.is-collapsed { - transition: inline-size var(--spectrum-animation-duration-800) ease-in-out; + transition: inline-size var(--mod-search-collapsed-animation-duration, var(--spectrum-search-collapsed-animation-duration)) ease-in-out; inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); min-inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size)); transform-origin: left center; } &.is-expanded { - transition: inline-size var(--spectrum-animation-duration-800) ease-in-out; + transition: inline-size var(--mod-search-collapsed-animation-duration, var(--spectrum-search-collapsed-animation-duration)) ease-in-out; inline-size: var(--mod-search-inline-size, var(--spectrum-search-inline-size)); min-inline-size: auto; transform-origin: left center;