Skip to content

Commit e467fe0

Browse files
authored
feat(search): collapsed search (#4115)
* feat(search): adding collapsed search feature * feat(search): adding changeset * feat(search): adding keyboard focused state for collapsed search * feat(search): updating changeset copy * feat(search): adding quiet variant to button * feat(search): adding min inline size for elongated animation * feat(search): adding animation speed and proper disabled state * feat(search): remove tabbing when button is disabled * feat(search): adding component specific collapsed animation token
1 parent 7ac2952 commit e467fe0

File tree

6 files changed

+114
-21
lines changed

6 files changed

+114
-21
lines changed

.changeset/long-carrots-sleep.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@spectrum-css/search": major
3+
---
4+
5+
## S2 Collapsed search field
6+
7+
The search component allows for a minimized state where the search field is collapsed to a button.
8+
9+
### Anatomy
10+
11+
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.
12+
13+
### Usage
14+
15+
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.

components/search/dist/metadata.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
".spectrum-Search-textfield.is-focused:hover .spectrum-Search-icon",
2323
".spectrum-Search-textfield.is-keyboardFocused .spectrum-Search-icon",
2424
".spectrum-Search-textfield:hover .spectrum-Search-icon",
25+
".spectrum-Search.is-collapsed",
2526
".spectrum-Search.is-disabled .spectrum-Search-clearButton",
27+
".spectrum-Search.is-expanded",
2628
".spectrum-Search:lang(ja)",
2729
".spectrum-Search:lang(ko)",
2830
".spectrum-Search:lang(zh)"
@@ -41,6 +43,7 @@
4143
"--mod-search-border-width",
4244
"--mod-search-bottom-to-text",
4345
"--mod-search-button-inline-size",
46+
"--mod-search-collapsed-animation-duration",
4447
"--mod-search-color-default",
4548
"--mod-search-color-disabled",
4649
"--mod-search-color-focus",
@@ -77,6 +80,7 @@
7780
"--spectrum-search-border-width",
7881
"--spectrum-search-bottom-to-text",
7982
"--spectrum-search-button-inline-size",
83+
"--spectrum-search-collapsed-animation-duration",
8084
"--spectrum-search-color",
8185
"--spectrum-search-color-default",
8286
"--spectrum-search-color-disabled",
@@ -102,6 +106,7 @@
102106
"--spectrum-search-top-to-text"
103107
],
104108
"global": [
109+
"--spectrum-animation-duration-800",
105110
"--spectrum-border-width-200",
106111
"--spectrum-cjk-line-height-100",
107112
"--spectrum-component-bottom-to-text-100",

components/search/index.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
/* stylelint-disable-next-line spectrum-tools/no-unused-custom-properties -- used to assign Textfield mods */
6767
--spectrum-search-border-color-disabled: var(--spectrum-disabled-border-color);
6868

69+
/* Collapsed Search */
70+
--spectrum-search-collapsed-animation-duration: var(--spectrum-animation-duration-800);
71+
6972
&:lang(ja),
7073
&:lang(zh),
7174
&:lang(ko) {
@@ -149,6 +152,21 @@
149152
.spectrum-HelpText {
150153
margin-block-start: var(--mod-search-to-help-text, var(--spectrum-search-to-help-text));
151154
}
155+
156+
/* Animation for collapsible search expansion */
157+
&.is-collapsed {
158+
transition: inline-size var(--mod-search-collapsed-animation-duration, var(--spectrum-search-collapsed-animation-duration)) ease-in-out;
159+
inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size));
160+
min-inline-size: var(--mod-search-button-inline-size, var(--spectrum-search-block-size));
161+
transform-origin: left center;
162+
}
163+
164+
&.is-expanded {
165+
transition: inline-size var(--mod-search-collapsed-animation-duration, var(--spectrum-search-collapsed-animation-duration)) ease-in-out;
166+
inline-size: var(--mod-search-inline-size, var(--spectrum-search-inline-size));
167+
min-inline-size: auto;
168+
transform-origin: left center;
169+
}
152170
}
153171

154172
.spectrum-Search-clearButton {

components/search/stories/search.stories.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export default {
2828
category: "Content",
2929
},
3030
control: "boolean",
31+
if: { arg: "isCollapsed", eq: false },
3132
},
3233
helpTextLabel: {
3334
name: "Help text (description)",
@@ -50,6 +51,16 @@ export default {
5051
type: { summary: "string" },
5152
category: "Content",
5253
},
54+
if: { arg: "isCollapsed", eq: false },
55+
},
56+
isCollapsed: {
57+
name: "Collapsed",
58+
type: { name: "boolean" },
59+
table: {
60+
type: { summary: "boolean" },
61+
category: "Component",
62+
},
63+
control: "boolean",
5364
},
5465
},
5566
args: {
@@ -62,13 +73,15 @@ export default {
6273
showHelpText: false,
6374
helpTextLabel: "Help text with a suggestion of what to search for",
6475
inputValue: "",
76+
isCollapsed: false,
6577
},
6678
parameters: {
6779
actions: {
6880
handles: [
6981
"change .spectrum-Search-input",
7082
"click .spectrum-Search-clearButton",
7183
"click .spectrum-Search-icon",
84+
"click .spectrum-Search-actionButton",
7285
],
7386
},
7487
design: {
@@ -141,6 +154,18 @@ WithValue.parameters = {
141154
};
142155
WithValue.storyName = "With value and clear button";
143156

157+
/**
158+
* 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.
159+
*/
160+
export const Collapsed = Template.bind({});
161+
Collapsed.args = {
162+
isCollapsed: true,
163+
};
164+
Collapsed.tags = ["!dev"];
165+
Collapsed.parameters = {
166+
chromatic: { disableSnapshot: true },
167+
};
168+
144169
/**
145170
* 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.
146171
*/

components/search/stories/search.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export const SearchGroup = Variants({
1717
inputValue: "What should we search for?",
1818
withStates: false,
1919
},
20+
{
21+
testHeading: "Collapsed",
22+
isCollapsed: true,
23+
}
2024
],
2125
stateData: [
2226
{
@@ -30,11 +34,13 @@ export const SearchGroup = Variants({
3034
{
3135
testHeading: "Focused",
3236
isFocused: true,
37+
ignore: ["Collapsed"],
3338
},
3439
{
3540
testHeading: "Focused + hovered",
3641
isFocused: true,
3742
isHovered: true,
43+
ignore: ["Collapsed"],
3844
},
3945
{
4046
testHeading: "Keyboard focused",

components/search/stories/template.js

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Template as ActionButton } from "@spectrum-css/actionbutton/stories/template.js";
12
import { Template as ClearButton } from "@spectrum-css/clearbutton/stories/template.js";
23
import { Template as HelpText } from "@spectrum-css/helptext/stories/template.js";
34
import { Container } from "@spectrum-css/preview/decorators";
@@ -19,7 +20,9 @@ export const Template = ({
1920
size = "m",
2021
showHelpText = false,
2122
helpTextLabel = "",
23+
isCollapsed = false,
2224
} = {}, context = {}) => {
25+
const { updateArgs } = context;
2326
return html`
2427
<form
2528
class=${classMap({
@@ -28,38 +31,59 @@ export const Template = ({
2831
typeof size !== "undefined" && size !== "m",
2932
"is-disabled": isDisabled,
3033
"is-keyboardFocused": isKeyboardFocused,
34+
"is-collapsed": isCollapsed,
35+
"is-expanded": !isCollapsed,
3136
...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}),
3237
})}
3338
aria-label="Search"
3439
>
35-
${TextField({
36-
isDisabled,
37-
size,
38-
customClasses: [
39-
`${rootClass}-textfield`,
40-
isFocused && "is-focused",
41-
isKeyboardFocused && "is-keyboardFocused",
42-
isHovered && "is-hover"
43-
],
44-
iconName: "Search",
45-
setName: "workflow",
46-
type: "search",
47-
placeholder: "Search",
48-
name: "search",
49-
customInputClasses: [`${rootClass}-input`],
50-
customIconClasses: [`${rootClass}-icon`],
51-
autocomplete: false,
52-
value: inputValue,
53-
}, context)}
54-
${when(inputValue, () =>
40+
${when(isCollapsed, () =>
41+
ActionButton({
42+
iconName: "Search",
43+
isDisabled,
44+
size,
45+
isFocused: !isDisabled && (isFocused || isKeyboardFocused),
46+
isQuiet: true,
47+
customClasses: [
48+
`${rootClass}-actionButton`,
49+
isHovered && "is-hover",
50+
isDisabled && "is-disabled",
51+
],
52+
onclick: () => {
53+
updateArgs({ isCollapsed: !isCollapsed });
54+
},
55+
}, context)
56+
)}
57+
${when(!isCollapsed, () =>
58+
TextField({
59+
isDisabled,
60+
size,
61+
customClasses: [
62+
`${rootClass}-textfield`,
63+
isFocused && "is-focused",
64+
isKeyboardFocused && "is-keyboardFocused",
65+
isHovered && "is-hover"
66+
],
67+
iconName: "Search",
68+
setName: "workflow",
69+
type: "search",
70+
placeholder: "Search",
71+
name: "search",
72+
customInputClasses: [`${rootClass}-input`],
73+
customIconClasses: [`${rootClass}-icon`],
74+
autocomplete: false,
75+
value: inputValue,
76+
}, context)
77+
)}
78+
${when(inputValue && !isCollapsed, () =>
5579
ClearButton({
5680
isDisabled,
5781
size,
5882
customClasses: [`${rootClass}-clearButton`],
5983
isFocusable: false,
6084
}, context)
6185
)}
62-
${when(showHelpText, () =>
86+
${when(showHelpText && !isCollapsed, () =>
6387
HelpText({
6488
text: helpTextLabel,
6589
size,

0 commit comments

Comments
 (0)