@@ -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?
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user