Merge branch 'backstage:master' into feature/git-wrapper-branch-command
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-msgraph': patch
|
||||
---
|
||||
|
||||
Added $select attribute to user query
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
---
|
||||
|
||||
Adds skipMetricsLookup to the kubernetes-backend schema
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Support displaying and ordering by counts in `EntityTagPicker` field. Add the `showCounts` option to enable this. Also support configuring `helperText`.
|
||||
@@ -188,19 +188,24 @@
|
||||
"clever-turtles-work",
|
||||
"cold-frogs-kiss",
|
||||
"cool-cameras-count",
|
||||
"cuddly-clocks-dance",
|
||||
"cuddly-needles-marry",
|
||||
"curly-hounds-wait",
|
||||
"dry-games-end",
|
||||
"dry-tips-build",
|
||||
"dull-spoons-doubt",
|
||||
"eight-shrimps-call",
|
||||
"eight-spies-protect",
|
||||
"eleven-bees-marry",
|
||||
"empty-colts-whisper",
|
||||
"famous-cups-roll",
|
||||
"famous-hounds-sit",
|
||||
"famous-pianos-kick",
|
||||
"famous-wombats-happen",
|
||||
"fast-paws-press",
|
||||
"five-carrots-pay",
|
||||
"flat-crabs-love",
|
||||
"flat-humans-dance",
|
||||
"flat-plums-shout",
|
||||
"flat-walls-kiss",
|
||||
"fresh-rabbits-juggle",
|
||||
@@ -211,17 +216,21 @@
|
||||
"gold-laws-attend",
|
||||
"good-avocados-grow",
|
||||
"good-papayas-dress",
|
||||
"gorgeous-boats-prove",
|
||||
"gorgeous-pillows-retire",
|
||||
"gorgeous-swans-kiss",
|
||||
"great-cats-sit",
|
||||
"great-rules-march",
|
||||
"grumpy-kiwis-juggle",
|
||||
"happy-kiwis-look",
|
||||
"heavy-ligers-laugh",
|
||||
"hot-brooms-drive",
|
||||
"hot-files-begin",
|
||||
"hot-suits-glow",
|
||||
"hungry-dogs-agree",
|
||||
"itchy-brooms-end",
|
||||
"itchy-kiwis-warn",
|
||||
"large-books-deny",
|
||||
"large-eagles-shop",
|
||||
"large-trainers-float",
|
||||
"late-turtles-listen",
|
||||
@@ -230,13 +239,17 @@
|
||||
"light-donuts-obey",
|
||||
"loud-comics-search",
|
||||
"lovely-ants-invite",
|
||||
"lucky-ads-worry",
|
||||
"lucky-points-wash",
|
||||
"many-rules-raise",
|
||||
"mean-tomatoes-visit",
|
||||
"metal-candles-tease",
|
||||
"metal-crabs-wash",
|
||||
"metal-weeks-kiss",
|
||||
"moody-berries-sneeze",
|
||||
"neat-kangaroos-turn",
|
||||
"nervous-rivers-sneeze",
|
||||
"new-taxis-vanish",
|
||||
"old-lemons-switch",
|
||||
"old-readers-provide",
|
||||
"old-rockets-doubt",
|
||||
@@ -247,7 +260,9 @@
|
||||
"purple-jeans-bathe",
|
||||
"purple-plums-travel",
|
||||
"purple-wolves-confess",
|
||||
"quick-items-invite",
|
||||
"quick-lamps-talk",
|
||||
"rare-tips-glow",
|
||||
"red-numbers-suffer",
|
||||
"renovate-16f36a0",
|
||||
"renovate-1a6cc1f",
|
||||
@@ -263,6 +278,7 @@
|
||||
"rotten-books-crash",
|
||||
"rude-books-rush",
|
||||
"rude-tips-argue",
|
||||
"rude-weeks-retire",
|
||||
"search-feet-flash",
|
||||
"search-planets-flash",
|
||||
"search-zebras-tap",
|
||||
@@ -273,11 +289,14 @@
|
||||
"shiny-lobsters-fix",
|
||||
"shiny-ties-leave",
|
||||
"shiny-walls-kiss",
|
||||
"short-mice-explode",
|
||||
"silent-kings-live",
|
||||
"slimy-stingrays-type",
|
||||
"slimy-zebras-reply",
|
||||
"slow-phones-count",
|
||||
"small-lemons-brake",
|
||||
"small-walls-promise",
|
||||
"smart-sloths-search",
|
||||
"smart-squids-change",
|
||||
"smooth-yaks-hug",
|
||||
"spicy-cherries-remember",
|
||||
@@ -286,10 +305,13 @@
|
||||
"strong-planes-return",
|
||||
"stupid-pigs-appear",
|
||||
"sweet-fishes-taste",
|
||||
"sweet-grapes-explain",
|
||||
"swift-readers-sin",
|
||||
"tall-trains-remain",
|
||||
"tame-papayas-protect",
|
||||
"tame-socks-sniff",
|
||||
"tame-trainers-rule",
|
||||
"tasty-clouds-cover",
|
||||
"techdocs-feet-dress",
|
||||
"techdocs-jeans-wait",
|
||||
"tender-ladybugs-relax",
|
||||
@@ -298,13 +320,16 @@
|
||||
"tiny-oranges-thank",
|
||||
"tiny-rocks-flash",
|
||||
"tough-dolphins-smile",
|
||||
"twenty-dolls-smoke",
|
||||
"two-planets-provide",
|
||||
"veka-fingrar-drar",
|
||||
"weak-camels-roll",
|
||||
"weak-radios-sin",
|
||||
"weak-yaks-learn",
|
||||
"wild-sheep-roll",
|
||||
"witty-cats-wink",
|
||||
"witty-queens-happen",
|
||||
"young-falcons-sleep",
|
||||
"young-trees-rescue"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
---
|
||||
'@backstage/plugin-search-react': minor
|
||||
---
|
||||
|
||||
The `<SearchResult/>` component now accepts a optional `query` prop to request results from the search api:
|
||||
|
||||
> Note: If a query prop is not defined, the results will by default be consumed from the context.
|
||||
|
||||
Example:
|
||||
|
||||
```jsx
|
||||
import React, { useState, useCallback } from 'react';
|
||||
|
||||
import { Grid, List, Paper } from '@material-ui/core';
|
||||
|
||||
import { Page, Header, Content, Lifecycle } from '@backstage/core-components';
|
||||
import {
|
||||
DefaultResultListItem,
|
||||
SearchBarBase,
|
||||
SearchResult,
|
||||
} from '@backstage/plugin-search-react';
|
||||
|
||||
const SearchPage = () => {
|
||||
const [query, setQuery] = useState({
|
||||
term: '',
|
||||
types: [],
|
||||
filters: {},
|
||||
});
|
||||
|
||||
const handleChange = useCallback(
|
||||
(term: string) => {
|
||||
setQuery(prevQuery => ({ ...prevQuery, term }));
|
||||
},
|
||||
[setQuery],
|
||||
);
|
||||
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header title="Search" subtitle={<Lifecycle alpha />} />
|
||||
<Content>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<Paper>
|
||||
<SearchBarBase debounceTime={100} onChange={handleChange} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
<SearchResult query={query}>
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(({ document }) => (
|
||||
<DefaultResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
)}
|
||||
</SearchResult>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
Additionally, a search page can also be composed using these two new results layout components:
|
||||
|
||||
```jsx
|
||||
// Example rendering results as list
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<SearchResultListLayout
|
||||
resultItems={results}
|
||||
renderResultItem={({ type, document }) => {
|
||||
switch (type) {
|
||||
case 'custom-result-item':
|
||||
return (
|
||||
<CustomResultListItem key={document.location} result={document} />
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</SearchResult>
|
||||
```
|
||||
|
||||
```jsx
|
||||
// Example rendering results as groups
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<>
|
||||
<SearchResultGroupLayout
|
||||
icon={<CustomIcon />}
|
||||
title="Custom"
|
||||
link="See all custom results"
|
||||
resultItems={results.filter(
|
||||
({ type }) => type === 'custom-result-item',
|
||||
)}
|
||||
renderResultItem={({ document }) => (
|
||||
<CustomResultListItem key={document.location} result={document} />
|
||||
)}
|
||||
/>
|
||||
<SearchResultGroupLayout
|
||||
icon={<DefaultIcon />}
|
||||
title="Default"
|
||||
resultItems={results.filter(
|
||||
({ type }) => type !== 'custom-result-item',
|
||||
)}
|
||||
renderResultItem={({ document }) => (
|
||||
<DefaultResultListItem key={document.location} result={document} />
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</SearchResult>
|
||||
```
|
||||
|
||||
A `SearchResultList` and `SearchResultGroup` components were also created for users who have search pages with multiple queries, both are specializations of `SearchResult` and also accept a `query` as a prop as well:
|
||||
|
||||
```jsx
|
||||
// Example using the <SearchResultList />
|
||||
const SearchPage = () => {
|
||||
const query = {
|
||||
term: 'example',
|
||||
};
|
||||
|
||||
return (
|
||||
<SearchResultList
|
||||
query={query}
|
||||
renderResultItem={({ type, document, highlight, rank }) => {
|
||||
switch (type) {
|
||||
case 'custom':
|
||||
return (
|
||||
<CustomResultListItem
|
||||
key={document.location}
|
||||
icon={<CatalogIcon />}
|
||||
result={document}
|
||||
highlight={highlight}
|
||||
rank={rank}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
// Example using the <SearchResultGroup /> for creating a component that search and group software catalog results
|
||||
import React, { useState, useCallback } from 'react';
|
||||
|
||||
import { MenuItem } from '@material-ui/core';
|
||||
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { CatalogIcon } from '@backstage/core-components';
|
||||
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
|
||||
import {
|
||||
SearchResultGroup,
|
||||
SearchResultGroupTextFilterField,
|
||||
SearchResultGroupSelectFilterField,
|
||||
} from @backstage/plugin-search-react;
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
|
||||
const CatalogResultsGroup = () => {
|
||||
const [query, setQuery] = useState<Partial<SearchQuery>>({
|
||||
types: ['software-catalog'],
|
||||
});
|
||||
|
||||
const filterOptions = [
|
||||
{
|
||||
label: 'Lifecycle',
|
||||
value: 'lifecycle',
|
||||
},
|
||||
{
|
||||
label: 'Owner',
|
||||
value: 'owner',
|
||||
},
|
||||
];
|
||||
|
||||
const handleFilterAdd = useCallback(
|
||||
(key: string) => () => {
|
||||
setQuery(prevQuery => {
|
||||
const { filters: prevFilters, ...rest } = prevQuery;
|
||||
const newFilters = { ...prevFilters, [key]: undefined };
|
||||
return { ...rest, filters: newFilters };
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleFilterChange = useCallback(
|
||||
(key: string) => (value: JsonValue) => {
|
||||
setQuery(prevQuery => {
|
||||
const { filters: prevFilters, ...rest } = prevQuery;
|
||||
const newFilters = { ...prevFilters, [key]: value };
|
||||
return { ...rest, filters: newFilters };
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleFilterDelete = useCallback(
|
||||
(key: string) => () => {
|
||||
setQuery(prevQuery => {
|
||||
const { filters: prevFilters, ...rest } = prevQuery;
|
||||
const newFilters = { ...prevFilters };
|
||||
delete newFilters[key];
|
||||
return { ...rest, filters: newFilters };
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<SearchResultGroup
|
||||
query={query}
|
||||
icon={<CatalogIcon />}
|
||||
title="Software Catalog"
|
||||
link="See all software catalog results"
|
||||
filterOptions={filterOptions}
|
||||
renderFilterOption={({ label, value }) => (
|
||||
<MenuItem key={value} onClick={handleFilterAdd(value)}>
|
||||
{label}
|
||||
</MenuItem>
|
||||
)}
|
||||
renderFilterField={(key: string) => {
|
||||
switch (key) {
|
||||
case 'lifecycle':
|
||||
return (
|
||||
<SearchResultGroupSelectFilterField
|
||||
key={key}
|
||||
label="Lifecycle"
|
||||
value={query.filters?.lifecycle}
|
||||
onChange={handleFilterChange('lifecycle')}
|
||||
onDelete={handleFilterDelete('lifecycle')}
|
||||
>
|
||||
<MenuItem value="production">Production</MenuItem>
|
||||
<MenuItem value="experimental">Experimental</MenuItem>
|
||||
</SearchResultGroupSelectFilterField>
|
||||
);
|
||||
case 'owner':
|
||||
return (
|
||||
<SearchResultGroupTextFilterField
|
||||
key={key}
|
||||
label="Owner"
|
||||
value={query.filters?.owner}
|
||||
onChange={handleFilterChange('owner')}
|
||||
onDelete={handleFilterDelete('owner')}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
renderResultItem={({ document, highlight, rank }) => (
|
||||
<CatalogSearchResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
highlight={highlight}
|
||||
rank={rank}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user