Improve SearchField

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-12-16 15:00:35 +00:00
parent 2650921d99
commit dde89f7ee5
3 changed files with 32 additions and 7 deletions
+2
View File
@@ -3,3 +3,5 @@
---
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).
Affected components: SearchField
@@ -175,6 +175,15 @@ export const StartCollapsed: Story = {
),
};
export const StartCollapsedWithValue: Story = {
args: {
...StartCollapsed.args,
defaultValue: 'https://example.com',
},
render: args => <SearchField {...args} size="small" />,
};
export const InHeader: Story = {
decorators: [
Story => (
@@ -68,19 +68,35 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
...rest
} = cleanedProps;
const [isCollapsed, setIsCollapsed] = useState(startCollapsed);
const [shouldCollapse, setShouldCollapse] = useState(true);
const hasProvidedValue = props.value || props.defaultValue;
const initialCollapsed = !!startCollapsed && !hasProvidedValue;
const inputRef = useRef<HTMLInputElement>(null);
const [isCollapsed, setIsCollapsed] = useState(initialCollapsed);
const [shouldCollapse, setShouldCollapse] = useState(!hasProvidedValue);
// If a value becomes available (controlled or defaultValue), force expand so the user can see it.
// If it becomes empty again and startCollapsed is enabled, allow collapse behavior again.
useEffect(() => {
if (hasProvidedValue) {
setShouldCollapse(false);
setIsCollapsed(false);
} else {
setShouldCollapse(true);
if (startCollapsed) {
setIsCollapsed(true);
}
}
}, [hasProvidedValue, startCollapsed]);
// If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required.
const secondaryLabelText =
secondaryLabel || (isRequired ? 'Required' : null);
const handleClick = (isFocused: boolean) => {
const handleFocusChange = (isFocused: boolean) => {
props.onFocusChange?.(isFocused);
if (shouldCollapse) {
if (isFocused) {
// When focusing, expand the field
// When focusing, expand the field (unless startCollapsed + a value/defaultValue is provided)
setIsCollapsed(false);
} else {
// When blurring, collapse the field
@@ -90,9 +106,7 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
};
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();
@@ -116,7 +130,7 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
data-collapsed={isCollapsed}
onFocusChange={handleClick}
onFocusChange={handleFocusChange}
onChange={handleChange}
style={style}
{...rest}