type fixes for React 18

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-13 00:05:25 +02:00
parent b2817daf0c
commit 0296f272b4
50 changed files with 193 additions and 94 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog': patch
'@backstage/plugin-techdocs': patch
---
The `spec.lifecycle' field in entities will now always be rendered as a string.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-code-coverage': patch
---
The warning for missing code coverage will now render the entity as a reference.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Fixed the type declaration of `DependencyGraphProps`, the `defs` prop now expects `JSX.Element`s.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': patch
---
The `spec.type` field in entities will now always be rendered as a string.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-import': patch
---
The `app.title` configuration is now properly required to be a string.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search-react': patch
---
The filter options passed to `SearchResultGroupLayout` are now always explicitly rendered as strings by default.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search': patch
---
Minor internal code cleanup.
@@ -31,9 +31,8 @@ jest.mock('react-router-dom', () =>
jest.requireActual('react-router-dom-beta'),
);
const element = () => null;
const rest = {
element,
element: null,
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
plugins: new Set<BackstagePlugin>(),
@@ -25,9 +25,8 @@ import {
} from '@backstage/core-plugin-api';
import { MATCH_ALL_ROUTE } from './collectors';
const element = () => null;
const rest = {
element,
element: null,
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
plugins: new Set<BackstagePlugin>(),
@@ -31,9 +31,8 @@ jest.mock('react-router-dom', () =>
jest.requireActual('react-router-dom-stable'),
);
const element = () => null;
const rest = {
element,
element: null,
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
plugins: new Set<BackstagePlugin>(),
@@ -353,7 +353,9 @@ describe('v1 consumer', () => {
initialProps: {
routeRef: routeRef1 as AnyRouteRef,
},
wrapper: ({ children }: React.PropsWithChildren<{}>) => (
wrapper: ({
children,
}: React.PropsWithChildren<{ routeRef: AnyRouteRef }>) => (
<RoutingProvider
routePaths={
new Map<RouteRef<any>, string>([
@@ -385,7 +385,9 @@ describe('v1 consumer', () => {
initialProps: {
routeRef: routeRef1 as AnyRouteRef,
},
wrapper: ({ children }: React.PropsWithChildren<{}>) => (
wrapper: ({
children,
}: React.PropsWithChildren<{ routeRef: AnyRouteRef }>) => (
<RoutingProvider
routePaths={
new Map<RouteRef<any>, string>([
+1 -1
View File
@@ -249,7 +249,7 @@ export interface DependencyGraphProps<NodeData, EdgeData>
acyclicer?: 'greedy';
align?: DependencyGraphTypes.Alignment;
curve?: 'curveStepBefore' | 'curveMonotoneX';
defs?: SVGDefsElement | SVGDefsElement[];
defs?: JSX.Element | JSX.Element[];
direction?: DependencyGraphTypes.Direction;
edgeMargin?: number;
edgeRanks?: number;
@@ -144,7 +144,7 @@ export interface DependencyGraphProps<NodeData, EdgeData>
* {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | Defs} shared by rendered SVG to be used by
* {@link DependencyGraphProps.renderNode} and/or {@link DependencyGraphProps.renderLabel}
*/
defs?: SVGDefsElement | SVGDefsElement[];
defs?: JSX.Element | JSX.Element[];
/**
* Controls zoom behavior of graph
*
@@ -455,7 +455,7 @@ export function Table<T extends object = {}>(props: TableProps<T>) {
const hasFilters = !!filters?.length;
const Toolbar = useCallback(
toolbarProps => {
(toolbarProps: any /* no type for this in material-table */) => {
return (
<TableToolbar
setSearch={setSearch}
@@ -472,7 +472,7 @@ export function Table<T extends object = {}>(props: TableProps<T>) {
const hasNoRows = typeof data !== 'function' && data.length === 0;
const columnCount = columns.length;
const Body = useCallback(
bodyProps => {
(bodyProps: any /* no type for this in material-table */) => {
if (isLoading) {
return (
<tbody data-testid="loading-indicator">
@@ -312,7 +312,11 @@ const sidebarSubmenuType = React.createElement(SidebarSubmenu).type;
// properly yet, matching for example /foobar with /foo.
export const WorkaroundNavLink = React.forwardRef<
HTMLAnchorElement,
NavLinkProps & { activeStyle?: CSSProperties; activeClassName?: string }
NavLinkProps & {
children?: ReactNode;
activeStyle?: CSSProperties;
activeClassName?: string;
}
>(function WorkaroundNavLinkWithRef(
{
to,
@@ -361,7 +365,10 @@ export const WorkaroundNavLink = React.forwardRef<
/**
* Common component used by SidebarItem & SidebarItemWithSubmenu
*/
const SidebarItemBase = forwardRef<any, SidebarItemProps>((props, ref) => {
const SidebarItemBase = forwardRef<
any,
SidebarItemProps & { children: ReactNode }
>((props, ref) => {
const {
icon: Icon,
text,
@@ -553,7 +560,10 @@ const SidebarItemWithSubmenu = ({
* @remarks
* If children contain a `SidebarSubmenu` component the `SidebarItem` will have a expandable submenu
*/
export const SidebarItem = forwardRef<any, SidebarItemProps>((props, ref) => {
export const SidebarItem = forwardRef<
any,
SidebarItemProps & { children: ReactNode }
>((props, ref) => {
// Filter children for SidebarSubmenu components
const [submenu] = useElementFilter(props.children, elements =>
// Directly comparing child.type with SidebarSubmenu will not work with in
@@ -25,7 +25,7 @@ import Typography from '@material-ui/core/Typography';
import CloseIcon from '@material-ui/icons/Close';
import MenuIcon from '@material-ui/icons/Menu';
import { orderBy } from 'lodash';
import React, { useEffect, useState, useContext } from 'react';
import React, { useEffect, useState, useContext, ReactNode } from 'react';
import { useLocation } from 'react-router-dom';
import { SidebarOpenStateProvider } from './SidebarOpenStateContext';
import { SidebarGroup } from './SidebarGroup';
@@ -206,8 +206,7 @@ export const MobileSidebar = (props: MobileSidebarProps) => {
onClose={() => setSelectedMenuItemIndex(-1)}
>
{sidebarGroups[selectedMenuItemIndex] &&
(sidebarGroups[selectedMenuItemIndex].props
.children as React.ReactChildren)}
(sidebarGroups[selectedMenuItemIndex].props.children as ReactNode)}
</OverlayMenu>
<BottomNavigation
className={classes.root}
@@ -59,7 +59,7 @@ const handleSearch = (input: string) => {
export const SampleSidebar = () => (
<SidebarPage>
<Sidebar>
<SidebarGroup label="Menu" icon={MenuIcon}>
<SidebarGroup label="Menu" icon={<MenuIcon />}>
<SidebarSearchField onSearch={handleSearch} to="/search" />
<SidebarDivider />
<SidebarItem icon={HomeOutlinedIcon} to="#" text="Plugins" />
@@ -96,7 +96,9 @@ export function TabbedCard(props: PropsWithChildren<Props>) {
} else {
React.Children.map(children, child => {
if (
React.isValidElement<{ children?: unknown; value?: unknown }>(child) &&
React.isValidElement<{ children?: ReactNode; value?: unknown }>(
child,
) &&
child?.props.value === value
) {
selectedTabContent = child?.props.children;
@@ -41,7 +41,12 @@ const FeatureFlagComponent = (_props: {
}) => null;
attachComponentData(FeatureFlagComponent, 'core.featureFlagged', true);
const mockFeatureFlagsApi = new LocalStorageFeatureFlags();
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
const Wrapper = ({
children,
}: {
children?: React.ReactNode;
tree?: ReactNode;
}) => (
<TestApiProvider apis={[[featureFlagsApiRef, mockFeatureFlagsApi]]}>
{children}
</TestApiProvider>
@@ -303,7 +303,12 @@ describe('useTranslationRef', () => {
const translationApi = I18nextTranslationApi.create({ languageApi });
const { result, rerender } = renderHook(
({ translationRef }) => useTranslationRef(translationRef),
({
translationRef,
}: {
translationRef: TranslationRef;
children?: ReactNode;
}) => useTranslationRef(translationRef),
{
wrapper: ({ children }) => (
<TestApiProvider
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Select } from '@backstage/core-components';
import { Select, SelectedItems } from '@backstage/core-components';
import { Box } from '@material-ui/core';
import React, { useCallback } from 'react';
@@ -31,7 +31,10 @@ export type Props = {
const curves: Array<Curve> = ['curveMonotoneX', 'curveStepBefore'];
export const CurveFilter = ({ value, onChange }: Props) => {
const handleChange = useCallback(v => onChange(v as Curve), [onChange]);
const handleChange = useCallback(
(v: SelectedItems) => onChange(v as Curve),
[onChange],
);
return (
<Box pb={1} pt={1}>
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Select } from '@backstage/core-components';
import { Select, SelectedItems } from '@backstage/core-components';
import { Box } from '@material-ui/core';
import React, { useCallback } from 'react';
import { Direction } from '../EntityRelationsGraph';
@@ -31,7 +31,10 @@ export type Props = {
};
export const DirectionFilter = ({ value, onChange }: Props) => {
const handleChange = useCallback(v => onChange(v as Direction), [onChange]);
const handleChange = useCallback(
(v: SelectedItems) => onChange(v as Direction),
[onChange],
);
return (
<Box pb={1} pt={1}>
@@ -36,7 +36,7 @@ export const DefaultImportPage = () => {
const theme = useTheme();
const configApi = useApi(configApiRef);
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const appTitle = configApi.getOptional('app.title') || 'Backstage';
const appTitle = configApi.getOptionalString('app.title') || 'Backstage';
const contentItems = [
<Grid item xs={12} md={4} lg={6} xl={8}>
@@ -43,7 +43,7 @@ export const ImportInfoCard = (props: ImportInfoCardProps) => {
} = props;
const configApi = useApi(configApiRef);
const appTitle = configApi.getOptional('app.title') || 'Backstage';
const appTitle = configApi.getOptionalString('app.title') || 'Backstage';
const catalogImportApi = useApi(catalogImportApiRef);
const hasGithubIntegration = configApi.has('integrations.github');
@@ -42,7 +42,7 @@ export const StepReviewLocation = ({
const configApi = useApi(configApiRef);
const analytics = useAnalytics();
const appTitle = configApi.getOptional('app.title') || 'Backstage';
const appTitle = configApi.getOptionalString('app.title') || 'Backstage';
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState<string>();
@@ -164,7 +164,7 @@ export const EntityPeekAheadPopover = (props: EntityPeekAheadPopoverProps) => {
{entity.metadata.description}
</Typography>
)}
<Typography>{entity.spec?.type}</Typography>
<Typography>{entity.spec?.type?.toString()}</Typography>
<Box marginTop="0.5em">
{(entity.metadata.tags || [])
.slice(0, maxTagChips)
@@ -74,7 +74,10 @@ export function OverviewPage(props: { entity: AlphaEntity }) {
</ListItem>
{spec?.type && (
<ListItem>
<ListItemText primary="spec.type" secondary={spec.type} />
<ListItemText
primary="spec.type"
secondary={spec.type?.toString()}
/>
</ListItem>
)}
{metadata.uid && (
@@ -22,7 +22,7 @@ import {
renderHook,
RenderHookResult,
} from '@testing-library/react-hooks';
import React from 'react';
import React, { ReactNode } from 'react';
import {
UseUnregisterEntityDialogState,
useUnregisterEntityDialogState,
@@ -85,7 +85,10 @@ describe('useUnregisterEntityDialogState', () => {
});
it('goes through the happy unregister path', async () => {
let rendered: RenderHookResult<unknown, UseUnregisterEntityDialogState>;
let rendered: RenderHookResult<
{ children?: ReactNode },
UseUnregisterEntityDialogState
>;
act(() => {
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
wrapper: Wrapper,
@@ -114,7 +117,10 @@ describe('useUnregisterEntityDialogState', () => {
entity.metadata.annotations![ANNOTATION_ORIGIN_LOCATION] =
'bootstrap:bootstrap';
let rendered: RenderHookResult<unknown, UseUnregisterEntityDialogState>;
let rendered: RenderHookResult<
{ children?: ReactNode },
UseUnregisterEntityDialogState
>;
act(() => {
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
wrapper: Wrapper,
@@ -137,7 +143,10 @@ describe('useUnregisterEntityDialogState', () => {
it('chooses only-delete when there was no location annotation', async () => {
delete entity.metadata.annotations![ANNOTATION_ORIGIN_LOCATION];
let rendered: RenderHookResult<unknown, UseUnregisterEntityDialogState>;
let rendered: RenderHookResult<
{ children?: ReactNode },
UseUnregisterEntityDialogState
>;
act(() => {
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
wrapper: Wrapper,
@@ -157,7 +166,10 @@ describe('useUnregisterEntityDialogState', () => {
});
it('chooses only-delete when the location could not be found', async () => {
let rendered: RenderHookResult<unknown, UseUnregisterEntityDialogState>;
let rendered: RenderHookResult<
{ children?: ReactNode },
UseUnregisterEntityDialogState
>;
act(() => {
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
wrapper: Wrapper,
@@ -128,7 +128,10 @@ function EntityLabels(props: { entity: Entity }) {
/>
)}
{entity.spec?.lifecycle && (
<HeaderLabel label="Lifecycle" value={entity.spec.lifecycle} />
<HeaderLabel
label="Lifecycle"
value={entity.spec.lifecycle?.toString()}
/>
)}
</>
);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { useEntity } from '@backstage/plugin-catalog-react';
import { humanizeEntityRef, useEntity } from '@backstage/plugin-catalog-react';
import { Box, Modal, makeStyles } from '@material-ui/core';
import FolderIcon from '@material-ui/icons/Folder';
import FileOutlinedIcon from '@material-ui/icons/InsertDriveFileOutlined';
@@ -181,7 +181,9 @@ export const FileExplorer = () => {
}
if (!value) {
return (
<Alert severity="warning">No code coverage found for ${entity}</Alert>
<Alert severity="warning">
No code coverage found for {humanizeEntityRef(entity)}
</Alert>
);
}
@@ -33,8 +33,9 @@ const items = [
},
];
const tooltipItems = () =>
items.map(item => <BarChartTooltipItem key={item.label} item={item} />);
const tooltipItems = items.map(item => (
<BarChartTooltipItem key={item.label} item={item} />
));
describe('<BarChartTooltip/>', () => {
it('formats label and tooltip item text correctly', async () => {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React from 'react';
import React, { ComponentType } from 'react';
import { getByRole, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ProjectSelect } from './ProjectSelect';
@@ -28,7 +28,7 @@ const mockProjects = [
];
describe('<ProjectSelect />', () => {
let Component: React.ReactNode;
let Component: ComponentType;
beforeEach(() => {
Component = () => (
<MockFilterProvider>
@@ -44,7 +44,9 @@ import { useApi } from '@backstage/core-plugin-api';
export const LIMIT = 10;
const AuditList = () => {
const [dismissedStored] = useLocalStorage(LIGHTHOUSE_INTRO_LOCAL_STORAGE);
const [dismissedStored] = useLocalStorage<boolean>(
LIGHTHOUSE_INTRO_LOCAL_STORAGE,
);
const [dismissed, setDismissed] = useState(dismissedStored);
const query = useQuery();
@@ -74,7 +74,7 @@ export const GroupListPicker = (props: GroupListPickerProps) => {
}, [catalogApi, groupTypes]);
const handleChange = useCallback(
(_, v: GroupEntity | null) => {
(_: unknown, v: GroupEntity | null) => {
onChange(v ?? undefined);
setAnchorEl(null);
},
+1
View File
@@ -59,6 +59,7 @@
"@backstage/cli": "workspace:^",
"@backstage/core-app-api": "workspace:^",
"@backstage/dev-utils": "workspace:^",
"@backstage/plugin-search-common": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
@@ -15,6 +15,7 @@
*/
import {
CompoundEntityRef,
Entity,
getCompoundEntityRef,
stringifyEntityRef,
@@ -22,6 +23,7 @@ import {
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
import type { SearchDocument } from '@backstage/plugin-search-common';
import {
SearchBar,
SearchContextProvider,
@@ -131,13 +133,13 @@ export const AddEntitiesDrawer = ({
};
const addEntity = useCallback(
entityResult => {
(entityResult: SearchDocument) => {
// TODO(kuangp): this parsing of the location is not great. Ideally `CatalogEntityDocument`
// contains the `metadata.name` field so we can derive the full ref and we only fall back to
// parsing location if it's missing (ie. for older versions)
const match = entityResult.location.match(entityLocationRegex);
if (match?.groups) {
onAdd(stringifyEntityRef(match?.groups));
onAdd(stringifyEntityRef(match?.groups as CompoundEntityRef));
} else {
// eslint-disable-next-line no-console
console.error(
@@ -72,7 +72,7 @@ export const PlaylistEntitiesTable = ({
);
const removeEntity = useCallback(
async (_, entity: Entity | Entity[]) => {
async (_: unknown, entity: Entity | Entity[]) => {
try {
const entityArray = [entity].flat();
const entityNames = entityArray.map(
@@ -29,7 +29,7 @@ import {
useRouteRefParams,
useApi,
} from '@backstage/core-plugin-api';
import { FormProps, IChangeEvent, withTheme } from '@rjsf/core';
import { FormProps, IChangeEvent, ISubmitEvent, withTheme } from '@rjsf/core';
import { Theme as MuiTheme } from '@rjsf/material-ui';
import React, { ComponentType, useState } from 'react';
import { transformSchemaToProps } from './schema';
@@ -185,7 +185,7 @@ export const MultistepJsonForm = (props: MultistepJsonFormProps) => {
formData={formData}
formContext={{ formData }}
onChange={onChange}
onSubmit={e => {
onSubmit={(e: ISubmitEvent<any>) => {
if (e.errors.length === 0) handleNext();
}}
{...formProps}
@@ -28,7 +28,7 @@ import {
Select,
} from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import { withTheme } from '@rjsf/core';
import { ISubmitEvent, withTheme } from '@rjsf/core';
import { Theme as MuiTheme } from '@rjsf/material-ui';
import CodeMirror from '@uiw/react-codemirror';
import React, { useCallback, useMemo, useState } from 'react';
@@ -104,7 +104,7 @@ export const CustomFieldExplorer = ({
}, [customFieldExtensions]);
const handleSelectionChange = useCallback(
selection => {
(selection: FieldExtensionOptions) => {
setSelectedField(selection);
setFieldFormState({});
setFormState({});
@@ -113,7 +113,7 @@ export const CustomFieldExplorer = ({
);
const handleFieldConfigChange = useCallback(
state => {
(state: {}) => {
setFieldFormState(state);
setFormState({});
// Force TemplateEditorForm to re-render since some fields
@@ -134,7 +134,9 @@ export const CustomFieldExplorer = ({
value={selectedField}
label="Choose Custom Field Extension"
labelId="select-field-label"
onChange={e => handleSelectionChange(e.target.value)}
onChange={e =>
handleSelectionChange(e.target.value as FieldExtensionOptions)
}
>
{fieldOptions.map((option, idx) => (
<MenuItem key={idx} value={option as any}>
@@ -158,7 +160,9 @@ export const CustomFieldExplorer = ({
noHtml5Validate
formData={fieldFormState}
formContext={{ fieldFormState }}
onSubmit={e => handleFieldConfigChange(e.formData)}
onSubmit={(e: ISubmitEvent<any>) =>
handleFieldConfigChange(e.formData)
}
schema={selectedField.schema?.uiOptions || {}}
>
<Button
@@ -164,7 +164,8 @@ export const TemplateFormPreviewer = ({
);
const handleSelectChange = useCallback(
selected => {
// TODO(Rugvip): Afaik this should be Entity, but didn't want to make runtime changes while fixing types
(selected: any) => {
setSelectedTemplate(selected);
setTemplateYaml(yaml.stringify(selected.spec));
},
@@ -102,7 +102,7 @@ export const CustomFieldExplorer = ({
}, [customFieldExtensions]);
const handleSelectionChange = useCallback(
selection => {
(selection: NextFieldExtensionOptions) => {
setSelectedField(selection);
setFieldFormState({});
},
@@ -110,7 +110,7 @@ export const CustomFieldExplorer = ({
);
const handleFieldConfigChange = useCallback(
state => {
(state: {}) => {
setFieldFormState(state);
// Force TemplateEditorForm to re-render since some fields
// may not be responsive to ui:option changes
@@ -130,7 +130,9 @@ export const CustomFieldExplorer = ({
value={selectedField}
label="Choose Custom Field Extension"
labelId="select-field-label"
onChange={e => handleSelectionChange(e.target.value)}
onChange={e =>
handleSelectionChange(e.target.value as NextFieldExtensionOptions)
}
>
{fieldOptions.map((option, idx) => (
<MenuItem key={idx} value={option as any}>
@@ -161,7 +161,8 @@ export const TemplateFormPreviewer = ({
);
const handleSelectChange = useCallback(
selected => {
// TODO(Rugvip): Afaik this should be Entity, but didn't want to make runtime changes while fixing types
(selected: any) => {
setSelectedTemplate(selected);
setTemplateYaml(yaml.stringify(selected.spec));
},
@@ -29,7 +29,7 @@ const SearchContextFilterSpy = ({ name }: { name: string }) => {
const value = filters[name];
return (
<span data-testid={`${name}-filter-spy`}>
{Array.isArray(value) ? value.join(',') : value}
{Array.isArray(value) ? value.join(',') : value?.toString()}
</span>
);
};
@@ -193,7 +193,7 @@ export const SearchResultGroupTextFilterField = (
contentEditable
suppressContentEditableWarning
>
{value}
{value?.toString()}
</Typography>
</SearchResultGroupFilterFieldLayout>
);
@@ -377,7 +377,7 @@ export function SearchResultGroupLayout<FilterOption>(
filterOptions,
renderFilterOption = filterOption => (
<MenuItem key={String(filterOption)} value={String(filterOption)}>
{filterOption}
{String(filterOption)}
</MenuItem>
),
filterFields,
+18 -16
View File
@@ -198,22 +198,24 @@ export const SearchPage = createPageExtension({
<Grid item xs>
<SearchPagination />
<SearchResults>
{({ results }) =>
results.map((result, index) => {
const { noTrack } = config;
const { document, ...rest } = result;
const SearchResultListItem =
getResultItemComponent(result);
return (
<SearchResultListItem
{...rest}
key={index}
result={document}
noTrack={noTrack}
/>
);
})
}
{({ results }) => (
<>
{results.map((result, index) => {
const { noTrack } = config;
const { document, ...rest } = result;
const SearchResultListItem =
getResultItemComponent(result);
return (
<SearchResultListItem
{...rest}
key={index}
result={document}
noTrack={noTrack}
/>
);
})}
</>
)}
</SearchResults>
<SearchResultPager />
</Grid>
@@ -57,18 +57,11 @@ export const HomePageSearchBar = (props: HomePageSearchBarProps) => {
handleSearch({ query: ref.current?.value ?? '' });
}, [handleSearch]);
const handleChange = useCallback(
value => {
setQuery(value);
},
[setQuery],
);
return (
<SearchBarBase
value={query}
onSubmit={handleSubmit}
onChange={handleChange}
onChange={setQuery}
inputProps={{ ref }}
InputProps={{
...props.InputProps,
@@ -74,12 +74,15 @@ const SentryIssuesTable = (props: SentryIssuesTableProps) => {
const { sentryIssues, statsFor, tableOptions } = props;
const [selected, setSelected] = useState(ONE_DAY_IN_MILLIS);
const filterByDate = useCallback((issue, selectedFilter) => {
return (
DateTime.fromISO(issue.lastSeen) >
DateTime.now().minus(Duration.fromMillis(selectedFilter))
);
}, []);
const filterByDate = useCallback(
(issue: SentryIssue, selectedFilter: number) => {
return (
DateTime.fromISO(issue.lastSeen) >
DateTime.now().minus(Duration.fromMillis(selectedFilter))
);
},
[],
);
const [filteredIssues, setFilteredIssues] = useState(
sentryIssues.filter(i => filterByDate(i, selected)),
);
@@ -133,7 +133,9 @@ export const TechDocsReaderPageHeader = (
}
/>
)}
{lifecycle ? <HeaderLabel label="Lifecycle" value={lifecycle} /> : null}
{lifecycle ? (
<HeaderLabel label="Lifecycle" value={String(lifecycle)} />
) : null}
{locationMetadata &&
locationMetadata.type !== 'dir' &&
locationMetadata.type !== 'file' ? (
+1
View File
@@ -8516,6 +8516,7 @@ __metadata:
"@backstage/plugin-permission-common": "workspace:^"
"@backstage/plugin-permission-react": "workspace:^"
"@backstage/plugin-playlist-common": "workspace:^"
"@backstage/plugin-search-common": "workspace:^"
"@backstage/plugin-search-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"