Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-09-05 16:38:59 +02:00
parent 767db0bd62
commit dbbd93ec7f
4 changed files with 177 additions and 160 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-components': patch
'@backstage/plugin-search-react': patch
---
Internal update to match recent React types
@@ -63,27 +63,57 @@ import { TableLoadingBody } from './TableLoadingBody';
// Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51
const tableIcons: Icons = {
Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
DetailPanel: forwardRef((props, ref) => (
Add: forwardRef<SVGSVGElement>((props, ref) => (
<AddBox {...props} ref={ref} />
)),
Check: forwardRef<SVGSVGElement>((props, ref) => (
<Check {...props} ref={ref} />
)),
Clear: forwardRef<SVGSVGElement>((props, ref) => (
<Clear {...props} ref={ref} />
)),
Delete: forwardRef<SVGSVGElement>((props, ref) => (
<DeleteOutline {...props} ref={ref} />
)),
DetailPanel: forwardRef<SVGSVGElement>((props, ref) => (
<ChevronRight {...props} ref={ref} />
)),
Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
PreviousPage: forwardRef((props, ref) => (
Edit: forwardRef<SVGSVGElement>((props, ref) => (
<Edit {...props} ref={ref} />
)),
Export: forwardRef<SVGSVGElement>((props, ref) => (
<SaveAlt {...props} ref={ref} />
)),
Filter: forwardRef<SVGSVGElement>((props, ref) => (
<FilterList {...props} ref={ref} />
)),
FirstPage: forwardRef<SVGSVGElement>((props, ref) => (
<FirstPage {...props} ref={ref} />
)),
LastPage: forwardRef<SVGSVGElement>((props, ref) => (
<LastPage {...props} ref={ref} />
)),
NextPage: forwardRef<SVGSVGElement>((props, ref) => (
<ChevronRight {...props} ref={ref} />
)),
PreviousPage: forwardRef<SVGSVGElement>((props, ref) => (
<ChevronLeft {...props} ref={ref} />
)),
ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
Search: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
SortArrow: forwardRef((props, ref) => <ArrowUpward {...props} ref={ref} />),
ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />),
ResetSearch: forwardRef<SVGSVGElement>((props, ref) => (
<Clear {...props} ref={ref} />
)),
Search: forwardRef<SVGSVGElement>((props, ref) => (
<FilterList {...props} ref={ref} />
)),
SortArrow: forwardRef<SVGSVGElement>((props, ref) => (
<ArrowUpward {...props} ref={ref} />
)),
ThirdStateCheck: forwardRef<SVGSVGElement>((props, ref) => (
<Remove {...props} ref={ref} />
)),
ViewColumn: forwardRef<SVGSVGElement>((props, ref) => (
<ViewColumn {...props} ref={ref} />
)),
};
// TODO: Material table might already have such a function internally that we can use?
+2 -11
View File
@@ -132,21 +132,12 @@ export type SearchAutocompleteProps<Option> = Omit<
// @public
export const SearchBar: React_2.ForwardRefExoticComponent<
Omit<
Omit<Partial<SearchBarBaseProps>, 'ref'> &
React_2.RefAttributes<HTMLDivElement>,
'ref'
> &
React_2.RefAttributes<HTMLDivElement>
Omit<Partial<SearchBarBaseProps>, 'ref'> & React_2.RefAttributes<unknown>
>;
// @public
export const SearchBarBase: React_2.ForwardRefExoticComponent<
Omit<
Omit<SearchBarBaseProps, 'ref'> & React_2.RefAttributes<HTMLDivElement>,
'ref'
> &
React_2.RefAttributes<HTMLDivElement>
Omit<SearchBarBaseProps, 'ref'> & React_2.RefAttributes<unknown>
>;
// @public
@@ -28,7 +28,6 @@ import { TextFieldProps } from '@material-ui/core/TextField';
import DefaultSearchIcon from '@material-ui/icons/Search';
import React, {
ChangeEvent,
ComponentType,
forwardRef,
KeyboardEvent,
useCallback,
@@ -37,17 +36,8 @@ import React, {
useState,
} from 'react';
import useDebounce from 'react-use/esm/useDebounce';
import { SearchContextProvider, useSearch } from '../../context';
function withContext<T>(Component: ComponentType<T>) {
return forwardRef<HTMLDivElement, T>((props, ref) => (
<SearchContextProvider inheritParentContextIfAvailable>
<Component {...props} ref={ref} />
</SearchContextProvider>
));
}
/**
* Props for {@link SearchBarBase}.
*
@@ -69,111 +59,111 @@ export type SearchBarBaseProps = Omit<TextFieldProps, 'onChange'> & {
*
* @public
*/
export const SearchBarBase = withContext(
forwardRef<HTMLDivElement, SearchBarBaseProps>((props, ref) => {
const {
onChange,
onKeyDown = () => {},
onClear = () => {},
onSubmit = () => {},
debounceTime = 200,
clearButton = true,
fullWidth = true,
value: defaultValue,
label,
placeholder,
inputProps = {},
InputProps = {},
endAdornment,
...rest
} = props;
export const SearchBarBase = forwardRef((props: SearchBarBaseProps, ref) => {
const {
onChange,
onKeyDown = () => {},
onClear = () => {},
onSubmit = () => {},
debounceTime = 200,
clearButton = true,
fullWidth = true,
value: defaultValue,
label,
placeholder,
inputProps = {},
InputProps = {},
endAdornment,
...rest
} = props;
const configApi = useApi(configApiRef);
const [value, setValue] = useState<string>('');
const forwardedValueRef = useRef<string>('');
const configApi = useApi(configApiRef);
const [value, setValue] = useState<string>('');
const forwardedValueRef = useRef<string>('');
useEffect(() => {
setValue(prevValue => {
// We only update the value if our current value is the same as it was
// for the most recent onChange call. Otherwise it means that the users
// has continued typing and we should not replace their input.
if (prevValue === forwardedValueRef.current) {
return String(defaultValue);
}
return prevValue;
});
}, [defaultValue, forwardedValueRef]);
useDebounce(
() => {
forwardedValueRef.current = value;
onChange(value);
},
debounceTime,
[value],
);
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
},
[setValue],
);
const handleKeyDown = useCallback(
(e: KeyboardEvent<HTMLDivElement>) => {
if (onKeyDown) onKeyDown(e);
if (onSubmit && e.key === 'Enter') {
onSubmit();
}
},
[onKeyDown, onSubmit],
);
const handleClear = useCallback(() => {
forwardedValueRef.current = '';
onChange('');
setValue('');
if (onClear) {
onClear();
useEffect(() => {
setValue(prevValue => {
// We only update the value if our current value is the same as it was
// for the most recent onChange call. Otherwise it means that the users
// has continued typing and we should not replace their input.
if (prevValue === forwardedValueRef.current) {
return String(defaultValue);
}
}, [onChange, onClear]);
return prevValue;
});
}, [defaultValue, forwardedValueRef]);
const ariaLabel: string | undefined = label ? undefined : 'Search';
useDebounce(
() => {
forwardedValueRef.current = value;
onChange(value);
},
debounceTime,
[value],
);
const inputPlaceholder =
placeholder ??
`Search in ${configApi.getOptionalString('app.title') || 'Backstage'}`;
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
},
[setValue],
);
const SearchIcon = useApp().getSystemIcon('search') || DefaultSearchIcon;
const handleKeyDown = useCallback(
(e: KeyboardEvent<HTMLDivElement>) => {
if (onKeyDown) onKeyDown(e);
if (onSubmit && e.key === 'Enter') {
onSubmit();
}
},
[onKeyDown, onSubmit],
);
const startAdornment = (
<InputAdornment position="start">
<IconButton aria-label="Query" size="small" disabled>
<SearchIcon />
</IconButton>
</InputAdornment>
);
const handleClear = useCallback(() => {
forwardedValueRef.current = '';
onChange('');
setValue('');
if (onClear) {
onClear();
}
}, [onChange, onClear]);
const clearButtonEndAdornment = (
<InputAdornment position="end">
<Button
aria-label="Clear"
size="small"
onClick={handleClear}
onKeyDown={event => {
if (event.key === 'Enter') {
// write your functionality here
event.stopPropagation();
}
}}
>
Clear
</Button>
</InputAdornment>
);
const ariaLabel: string | undefined = label ? undefined : 'Search';
return (
const inputPlaceholder =
placeholder ??
`Search in ${configApi.getOptionalString('app.title') || 'Backstage'}`;
const SearchIcon = useApp().getSystemIcon('search') || DefaultSearchIcon;
const startAdornment = (
<InputAdornment position="start">
<IconButton aria-label="Query" size="small" disabled>
<SearchIcon />
</IconButton>
</InputAdornment>
);
const clearButtonEndAdornment = (
<InputAdornment position="end">
<Button
aria-label="Clear"
size="small"
onClick={handleClear}
onKeyDown={event => {
if (event.key === 'Enter') {
// write your functionality here
event.stopPropagation();
}
}}
>
Clear
</Button>
</InputAdornment>
);
return (
<SearchContextProvider inheritParentContextIfAvailable>
<TextField
id="search-bar-text-field"
data-testid="search-bar-next"
@@ -197,9 +187,9 @@ export const SearchBarBase = withContext(
onKeyDown={handleKeyDown}
{...rest}
/>
);
}),
);
</SearchContextProvider>
);
});
/**
* Props for {@link SearchBar}.
@@ -213,30 +203,30 @@ export type SearchBarProps = Partial<SearchBarBaseProps>;
*
* @public
*/
export const SearchBar = withContext(
forwardRef<HTMLDivElement, SearchBarProps>((props, ref) => {
const { value: initialValue = '', onChange, ...rest } = props;
export const SearchBar = forwardRef((props: SearchBarProps, ref) => {
const { value: initialValue = '', onChange, ...rest } = props;
const { term, setTerm } = useSearch();
const { term, setTerm } = useSearch();
useEffect(() => {
if (initialValue) {
setTerm(String(initialValue));
useEffect(() => {
if (initialValue) {
setTerm(String(initialValue));
}
}, [initialValue, setTerm]);
const handleChange = useCallback(
(newValue: string) => {
if (onChange) {
onChange(newValue);
} else {
setTerm(newValue);
}
}, [initialValue, setTerm]);
},
[onChange, setTerm],
);
const handleChange = useCallback(
(newValue: string) => {
if (onChange) {
onChange(newValue);
} else {
setTerm(newValue);
}
},
[onChange, setTerm],
);
return (
return (
<SearchContextProvider inheritParentContextIfAvailable>
<AnalyticsContext
attributes={{ pluginId: 'search', extension: 'SearchBar' }}
>
@@ -247,6 +237,6 @@ export const SearchBar = withContext(
onChange={handleChange}
/>
</AnalyticsContext>
);
}),
);
</SearchContextProvider>
);
});