Fixes SearchField in Backstage UI
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Fixed SearchField `startCollapsed` prop not working correctly in Backstage UI. The field now properly starts in a collapsed state, expands when clicked and focused, and collapses back when unfocused with no input. Also fixed CSS logic to work correctly in all layout contexts (flex row, flex column, and regular containers).
|
||||
@@ -40,24 +40,27 @@
|
||||
}
|
||||
|
||||
&[data-startCollapsed='true'] {
|
||||
transition: flex-basis 0.3s ease-in-out;
|
||||
transition:
|
||||
flex-basis 0.3s ease-in-out,
|
||||
width 0.3s ease-in-out,
|
||||
max-width 0.3s ease-in-out;
|
||||
padding: 0;
|
||||
flex: 0 1 auto;
|
||||
|
||||
&[data-collapsed='true'] {
|
||||
flex-basis: 200px;
|
||||
}
|
||||
|
||||
&[data-collapsed='false'] {
|
||||
cursor: pointer;
|
||||
|
||||
&[data-size='medium'] {
|
||||
flex-basis: 2.5rem;
|
||||
width: 2.5rem;
|
||||
max-width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
&[data-size='small'] {
|
||||
flex-basis: 2rem;
|
||||
width: 2rem;
|
||||
max-width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
@@ -79,6 +82,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[data-collapsed='false'] {
|
||||
flex-basis: 200px;
|
||||
width: 200px;
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,9 +165,12 @@ export const StartCollapsed: Story = {
|
||||
},
|
||||
|
||||
render: args => (
|
||||
<Flex direction="row" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="row" gap="4">
|
||||
<SearchField {...args} size="small" />
|
||||
<SearchField {...args} size="medium" />
|
||||
</Flex>
|
||||
<SearchField {...args} size="small" />
|
||||
<SearchField {...args} size="medium" />
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect, useState } from 'react';
|
||||
import { forwardRef, useEffect, useState, useRef } from 'react';
|
||||
import {
|
||||
Input,
|
||||
SearchField as AriaSearchField,
|
||||
@@ -39,9 +39,6 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
'aria-labelledby': ariaLabelledBy,
|
||||
} = props;
|
||||
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
const [shouldCollapse, setShouldCollapse] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!label && !ariaLabel && !ariaLabelledBy) {
|
||||
console.warn(
|
||||
@@ -71,6 +68,10 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
...rest
|
||||
} = cleanedProps;
|
||||
|
||||
const [isCollapsed, setIsCollapsed] = useState(startCollapsed);
|
||||
const [shouldCollapse, setShouldCollapse] = useState(true);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required.
|
||||
const secondaryLabelText =
|
||||
secondaryLabel || (isRequired ? 'Required' : null);
|
||||
@@ -79,13 +80,26 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
props.onFocusChange?.(isFocused);
|
||||
if (shouldCollapse) {
|
||||
if (isFocused) {
|
||||
setIsCollapsed(true);
|
||||
} else {
|
||||
// When focusing, expand the field
|
||||
setIsCollapsed(false);
|
||||
} else {
|
||||
// When blurring, collapse the field
|
||||
setIsCollapsed(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleContainerClick = () => {
|
||||
// If the field is collapsed (small), expand it and focus the input
|
||||
if (startCollapsed && isCollapsed) {
|
||||
setIsCollapsed(false);
|
||||
// Focus the input after state update
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
props.onChange?.(value);
|
||||
if (value.length > 0) {
|
||||
@@ -119,6 +133,7 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
styles[classNames.inputWrapper],
|
||||
)}
|
||||
data-size={dataAttributes['data-size']}
|
||||
onClick={handleContainerClick}
|
||||
>
|
||||
{icon !== false && (
|
||||
<div
|
||||
@@ -133,6 +148,7 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
</div>
|
||||
)}
|
||||
<Input
|
||||
ref={inputRef}
|
||||
className={clsx(classNames.input, styles[classNames.input])}
|
||||
{...(icon !== false && { 'data-icon': true })}
|
||||
placeholder={placeholder}
|
||||
|
||||
Reference in New Issue
Block a user