feat: add i18n to plugin-catalog-react
Signed-off-by: mario ma <mario.ma.node@gmail.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Support i18n for catalog and catalog-react plugins
|
||||
@@ -13,6 +13,7 @@ import { PortableSchema } from '@backstage/frontend-plugin-api';
|
||||
import { ResolvedExtensionInputs } from '@backstage/frontend-plugin-api';
|
||||
import { ResourcePermission } from '@backstage/plugin-permission-common';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const catalogExtensionData: {
|
||||
@@ -24,6 +25,64 @@ export const catalogExtensionData: {
|
||||
entityFilterExpression: ConfigurableExtensionDataRef<string, {}>;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const catalogReactTranslationRef: TranslationRef<
|
||||
'catalog-react',
|
||||
{
|
||||
readonly 'catalogFilter.title': 'Filters';
|
||||
readonly 'catalogFilter.buttonTitle': 'Filters';
|
||||
readonly 'entityKindPicker.title': 'Kind';
|
||||
readonly 'entityKindPicker.errorMessage': 'Failed to load entity kinds';
|
||||
readonly entityLifecyclePickerTitle: 'Lifecycle';
|
||||
readonly entityNamespacePickerTitle: 'Namespace';
|
||||
readonly entityOwnerPickerTitle: 'Owner';
|
||||
readonly 'entityPeekAheadPopover.title': 'Drill into the entity to see all of the tags.';
|
||||
readonly 'entityPeekAheadPopover.entityCardActionsTitle': 'Show details';
|
||||
readonly 'entityPeekAheadPopover.emailCardAction.title': 'Email {{email}}';
|
||||
readonly 'entityPeekAheadPopover.emailCardAction.subTitle': 'mailto {{email}}';
|
||||
readonly entityProcessingStatusPickerTitle: 'Processing Status';
|
||||
readonly entitySearchBarPlaceholder: 'Search';
|
||||
readonly entityTagPickerTitle: 'Tags';
|
||||
readonly 'entityTypePicker.title': 'Type';
|
||||
readonly 'entityTypePicker.errorMessage': 'Failed to load entity types';
|
||||
readonly 'entityTypePicker.optionAllTitle': 'all';
|
||||
readonly 'favoriteEntity.addToFavorites': 'Add to favorites';
|
||||
readonly 'favoriteEntity.RemoveFromFavorites': 'Remove from favorites';
|
||||
readonly 'inspectEntityDialog.title': 'Entity Inspector';
|
||||
readonly 'inspectEntityDialog.closeButtonTitle': 'Close';
|
||||
readonly 'inspectEntityDialog.ancestryPage.title': 'Ancestry';
|
||||
readonly 'inspectEntityDialog.colocatedPage.title': 'Colocated';
|
||||
readonly 'inspectEntityDialog.colocatedPage.description': 'These are the entities that are colocated with this entity - as in, they originated from the same data source (e.g. came from the same YAML file), or from the same origin (e.g. the originally registered URL).';
|
||||
readonly 'inspectEntityDialog.colocatedPage.alertNoLocation': 'Entity had no location information.';
|
||||
readonly 'inspectEntityDialog.colocatedPage.alertNoEntity': 'There were no other entities on this location.';
|
||||
readonly 'inspectEntityDialog.jsonPage.title': 'Entity as JSON';
|
||||
readonly 'inspectEntityDialog.jsonPage.description': 'This is the raw entity data as received from the catalog, on JSON form.';
|
||||
readonly 'inspectEntityDialog.overviewPage.title': 'Overview';
|
||||
readonly 'inspectEntityDialog.yamlPage.title': 'Entity as YAML';
|
||||
readonly 'inspectEntityDialog.yamlPage.description': 'This is the raw entity data as received from the catalog, on YAML form.';
|
||||
readonly 'unregisterEntityDialog.title': 'Are you sure you want to unregister this entity?';
|
||||
readonly 'unregisterEntityDialog.cancelButtonTitle': 'Cancel';
|
||||
readonly 'unregisterEntityDialog.deleteButtonTitle': 'Delete Entity';
|
||||
readonly 'unregisterEntityDialog.deleteEntitySuccessMessage': 'Removed entity {{entityName}}';
|
||||
readonly 'unregisterEntityDialog.onlyDeleteStateTitle': 'This entity does not seem to originate from a registered location. You therefore only have the option to delete it outright from the catalog.';
|
||||
readonly 'unregisterEntityDialog.errorStateTitle': 'Internal error: Unknown state';
|
||||
readonly 'unregisterEntityDialog.bootstrapState.title': 'You cannot unregister this entity, since it originates from a protected Backstage configuration (location "{{location}}"). If you believe this is in error, please contact the {{appTitle}} integrator.';
|
||||
readonly 'unregisterEntityDialog.bootstrapState.advancedDescription': 'You have the option to delete the entity itself from the catalog. Note that this should only be done if you know that the catalog file has been deleted at, or moved from, its origin location. If that is not the case, the entity will reappear shortly as the next refresh round is performed by the catalog.';
|
||||
readonly 'unregisterEntityDialog.bootstrapState.advancedOptions': 'Advanced Options';
|
||||
readonly 'unregisterEntityDialog.unregisterState.title': 'This action will unregister the following entities:';
|
||||
readonly 'unregisterEntityDialog.unregisterState.description': 'To undo, just re-register the entity in {{appTitle}}.';
|
||||
readonly 'unregisterEntityDialog.unregisterState.subTitle': 'Located at the following location:';
|
||||
readonly 'unregisterEntityDialog.unregisterState.advancedDescription': 'You also have the option to delete the entity itself from the catalog. Note that this should only be done if you know that the catalog file has been deleted at, or moved from, its origin location. If that is not the case, the entity will reappear shortly as the next refresh round is performed by the catalog.';
|
||||
readonly 'unregisterEntityDialog.unregisterState.advancedOptions': 'Advanced Options';
|
||||
readonly 'unregisterEntityDialog.unregisterState.unregisterButtonTitle': 'Unregister Location';
|
||||
readonly 'userListPicker.defaultOrgName': 'Company';
|
||||
readonly 'userListPicker.orgFilterAllLabel': 'All';
|
||||
readonly 'userListPicker.personalFilter.title': 'Personal';
|
||||
readonly 'userListPicker.personalFilter.ownedLabel': 'Owned';
|
||||
readonly 'userListPicker.personalFilter.starredLabel': 'Starred';
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createEntityCardExtension<
|
||||
TConfig extends {
|
||||
|
||||
@@ -32,6 +32,7 @@ import { Expand } from '../../../packages/frontend-plugin-api/src/types';
|
||||
|
||||
export { useEntityPermission } from './hooks/useEntityPermission';
|
||||
export { isOwnerOf } from './utils';
|
||||
export * from './translation';
|
||||
|
||||
/** @alpha */
|
||||
export const catalogExtensionData = {
|
||||
|
||||
@@ -23,6 +23,8 @@ import Typography from '@material-ui/core/Typography';
|
||||
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
||||
import { Theme, useTheme } from '@material-ui/core/styles';
|
||||
import FilterListIcon from '@material-ui/icons/FilterList';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export const Filters = (props: {
|
||||
@@ -37,6 +39,7 @@ export const Filters = (props: {
|
||||
);
|
||||
const theme = useTheme();
|
||||
const [filterDrawerOpen, setFilterDrawerOpen] = useState<boolean>(false);
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
return isScreenSmallerThanBreakpoint ? (
|
||||
<>
|
||||
@@ -45,7 +48,7 @@ export const Filters = (props: {
|
||||
onClick={() => setFilterDrawerOpen(true)}
|
||||
startIcon={<FilterListIcon />}
|
||||
>
|
||||
Filters
|
||||
{t('catalogFilter.buttonTitle')}
|
||||
</Button>
|
||||
<Drawer
|
||||
open={filterDrawerOpen}
|
||||
@@ -61,7 +64,7 @@ export const Filters = (props: {
|
||||
component="h2"
|
||||
style={{ marginBottom: theme.spacing(1) }}
|
||||
>
|
||||
Filters
|
||||
{t('catalogFilter.title')}
|
||||
</Typography>
|
||||
{props.children}
|
||||
</Box>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { GetEntityFacetsResponse } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { fireEvent, waitFor, screen, within } from '@testing-library/react';
|
||||
import { capitalize } from 'lodash';
|
||||
import { default as React } from 'react';
|
||||
@@ -75,7 +75,7 @@ describe('<EntityKindPicker/>', () => {
|
||||
);
|
||||
|
||||
it('renders available entity kinds', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{ filters: { kind: new EntityKindFilter('component') } }}
|
||||
@@ -102,7 +102,7 @@ describe('<EntityKindPicker/>', () => {
|
||||
|
||||
it('sets the selected kind filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -128,7 +128,7 @@ describe('<EntityKindPicker/>', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { kind: 'group' };
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -148,7 +148,7 @@ describe('<EntityKindPicker/>', () => {
|
||||
});
|
||||
|
||||
it('renders unknown kinds provided in query parameters', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{ queryParameters: { kind: 'FROb' } }}
|
||||
@@ -162,7 +162,7 @@ describe('<EntityKindPicker/>', () => {
|
||||
});
|
||||
|
||||
it('limits kinds when allowedKinds is set', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider>
|
||||
<EntityKindPicker allowedKinds={['component', 'domain']} />
|
||||
@@ -183,7 +183,7 @@ describe('<EntityKindPicker/>', () => {
|
||||
});
|
||||
|
||||
it('renders kind from the query parameter even when not in allowedKinds', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{ queryParameters: { kind: 'Frob' } }}
|
||||
|
||||
@@ -21,6 +21,8 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { EntityKindFilter } from '../../filters';
|
||||
import { useEntityList } from '../../hooks';
|
||||
import { filterKinds, useAllKinds } from './kindFilterUtils';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
function useEntityKindFilter(opts: { initialFilter: string }): {
|
||||
loading: boolean;
|
||||
@@ -95,6 +97,7 @@ export interface EntityKindPickerProps {
|
||||
/** @public */
|
||||
export const EntityKindPicker = (props: EntityKindPickerProps) => {
|
||||
const { allowedKinds, hidden, initialFilter = 'component' } = props;
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const alertApi = useApi(alertApiRef);
|
||||
|
||||
@@ -106,11 +109,11 @@ export const EntityKindPicker = (props: EntityKindPickerProps) => {
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
alertApi.post({
|
||||
message: `Failed to load entity kinds`,
|
||||
message: t('entityKindPicker.errorMessage'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [error, alertApi]);
|
||||
}, [error, alertApi, t]);
|
||||
|
||||
if (error) return null;
|
||||
|
||||
@@ -124,7 +127,7 @@ export const EntityKindPicker = (props: EntityKindPickerProps) => {
|
||||
return hidden ? null : (
|
||||
<Box pb={1} pt={1}>
|
||||
<Select
|
||||
label="Kind"
|
||||
label={t('entityKindPicker.title')}
|
||||
items={items}
|
||||
selected={selectedKind.toLocaleLowerCase('en-US')}
|
||||
onChange={value => setSelectedKind(String(value))}
|
||||
|
||||
+9
-9
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { EntityLifecycleFilter } from '../../filters';
|
||||
import { EntityLifecyclePicker } from './EntityLifecyclePicker';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { TestApiProvider, renderInTestApp } from '@backstage/test-utils';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
|
||||
@@ -44,7 +44,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
});
|
||||
|
||||
it('renders all lifecycles', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityLifecyclePicker />
|
||||
@@ -61,7 +61,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { lifecycles: ['experimental'] };
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -83,7 +83,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
|
||||
it('adds lifecycles to filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -107,7 +107,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
|
||||
it('removes lifecycles from filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -136,7 +136,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -180,7 +180,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
});
|
||||
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -201,7 +201,7 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
|
||||
it('responds to initialFilter prop', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
|
||||
+4
-1
@@ -18,6 +18,8 @@ import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { EntityLifecycleFilter } from '../../filters';
|
||||
import { EntityAutocompletePicker } from '../EntityAutocompletePicker';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactEntityLifecyclePickerClassKey = 'input';
|
||||
@@ -35,10 +37,11 @@ const useStyles = makeStyles(
|
||||
export const EntityLifecyclePicker = (props: { initialFilter?: string[] }) => {
|
||||
const { initialFilter = [] } = props;
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
return (
|
||||
<EntityAutocompletePicker
|
||||
label="Lifecycle"
|
||||
label={t('entityLifecyclePickerTitle')}
|
||||
name="lifecycles"
|
||||
path="spec.lifecycle"
|
||||
Filter={EntityLifecycleFilter}
|
||||
|
||||
+10
-10
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { EntityNamespaceFilter } from '../../filters';
|
||||
import { EntityNamespacePicker } from './EntityNamespacePicker';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { TestApiProvider, renderInTestApp } from '@backstage/test-utils';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
} as unknown as CatalogApi;
|
||||
|
||||
it('renders all namespaces', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityNamespacePicker />
|
||||
@@ -56,7 +56,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
});
|
||||
|
||||
it('renders unique namespaces in alphabetical order', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityNamespacePicker />
|
||||
@@ -79,7 +79,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { namespace: ['namespace-1'] };
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -101,7 +101,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
|
||||
it('adds namespaces to filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -125,7 +125,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
|
||||
it('removes namespaces from filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -154,7 +154,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -197,7 +197,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
}),
|
||||
} as unknown as CatalogApi;
|
||||
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRefNoNamespace]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -227,7 +227,7 @@ describe('<EntityNamespacePicker/>', () => {
|
||||
},
|
||||
}),
|
||||
} as unknown as CatalogApi;
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[[catalogApiRef, mockCatalogApiRefDefaultNamespace]]}
|
||||
>
|
||||
|
||||
+5
-1
@@ -19,6 +19,8 @@ import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { EntityNamespaceFilter } from '../../filters';
|
||||
import { EntityAutocompletePicker } from '../EntityAutocompletePicker';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactEntityNamespacePickerClassKey = 'input';
|
||||
@@ -45,9 +47,11 @@ export interface EntityNamespacePickerProps {
|
||||
export const EntityNamespacePicker = (props: EntityNamespacePickerProps) => {
|
||||
const { initiallySelectedNamespaces } = props;
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
return (
|
||||
<EntityAutocompletePicker
|
||||
label="Namespace"
|
||||
label={t('entityNamespacePickerTitle')}
|
||||
name="namespace"
|
||||
path="metadata.namespace"
|
||||
Filter={EntityNamespaceFilter}
|
||||
|
||||
@@ -23,7 +23,7 @@ import { EntityOwnerPicker } from './EntityOwnerPicker';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import {
|
||||
MockErrorApi,
|
||||
renderWithEffects,
|
||||
renderInTestApp,
|
||||
TestApiRegistry,
|
||||
} from '@backstage/test-utils';
|
||||
import { catalogApiRef, CatalogApi } from '../..';
|
||||
@@ -155,7 +155,7 @@ describe('<EntityOwnerPicker mode="all" />', () => {
|
||||
});
|
||||
|
||||
it('renders all users and groups', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityOwnerPicker mode="all" />
|
||||
@@ -201,7 +201,7 @@ describe('<EntityOwnerPicker mode="all" />', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { owners: ['another-owner'] };
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -239,7 +239,7 @@ describe('<EntityOwnerPicker mode="all" />', () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -281,7 +281,7 @@ describe('<EntityOwnerPicker mode="all" />', () => {
|
||||
|
||||
it('adds owners to filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -308,7 +308,7 @@ describe('<EntityOwnerPicker mode="all" />', () => {
|
||||
|
||||
it('removes owners from filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -340,7 +340,7 @@ describe('<EntityOwnerPicker mode="all" />', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = await renderWithEffects(
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -398,7 +398,7 @@ describe('<EntityOwnerPicker mode="owners-only" />', () => {
|
||||
});
|
||||
|
||||
it('renders all users and groups', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityOwnerPicker mode="owners-only" />
|
||||
@@ -439,7 +439,7 @@ describe('<EntityOwnerPicker mode="owners-only" />', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { owners: ['another-owner'] };
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -459,7 +459,7 @@ describe('<EntityOwnerPicker mode="owners-only" />', () => {
|
||||
|
||||
it('adds owners to filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -486,7 +486,7 @@ describe('<EntityOwnerPicker mode="owners-only" />', () => {
|
||||
|
||||
it('removes owners from filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -515,7 +515,7 @@ describe('<EntityOwnerPicker mode="owners-only" />', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = await renderWithEffects(
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
|
||||
@@ -40,6 +40,8 @@ import { humanizeEntity, humanizeEntityRef } from '../EntityRefLink/humanize';
|
||||
import { useFetchEntities } from './useFetchEntities';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { useEntityPresentation } from '../../apis';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactEntityOwnerPickerClassKey = 'input';
|
||||
@@ -130,6 +132,7 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => {
|
||||
} = useEntityList();
|
||||
|
||||
const [text, setText] = useState('');
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const queryParamOwners = useMemo(
|
||||
() => [ownersParameter].flat().filter(Boolean) as string[],
|
||||
@@ -176,7 +179,7 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => {
|
||||
return (
|
||||
<Box className={classes.root} pb={1} pt={1}>
|
||||
<Typography className={classes.label} variant="button" component="label">
|
||||
Owner
|
||||
{t('entityOwnerPickerTitle')}
|
||||
<Autocomplete
|
||||
PopperComponent={popperProps => (
|
||||
<div {...popperProps}>{popperProps.children as ReactNode}</div>
|
||||
|
||||
+9
-2
@@ -18,6 +18,8 @@ import IconButton from '@material-ui/core/IconButton';
|
||||
import EmailIcon from '@material-ui/icons/Email';
|
||||
import React from 'react';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/**
|
||||
* Email Card action link
|
||||
@@ -25,12 +27,17 @@ import { Link } from '@backstage/core-components';
|
||||
* @private
|
||||
*/
|
||||
export const EmailCardAction = (props: { email: string }) => {
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
return (
|
||||
<IconButton
|
||||
component={Link}
|
||||
aria-label="Email"
|
||||
title={`Email ${props.email}`}
|
||||
to={`mailto:${props.email}`}
|
||||
title={t('entityPeekAheadPopover.emailCardAction.title', {
|
||||
email: props.email,
|
||||
})}
|
||||
to={t('entityPeekAheadPopover.emailCardAction.subTitle', {
|
||||
email: props.email,
|
||||
})}
|
||||
>
|
||||
<EmailIcon />
|
||||
</IconButton>
|
||||
|
||||
+4
-1
@@ -21,6 +21,8 @@ import React from 'react';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { Entity, getCompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/**
|
||||
* Card actions that show for all entities
|
||||
@@ -29,12 +31,13 @@ import { Link } from '@backstage/core-components';
|
||||
*/
|
||||
export const EntityCardActions = (props: { entity: Entity }) => {
|
||||
const entityRoute = useRouteRef(entityRouteRef);
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
component={Link}
|
||||
aria-label="Show"
|
||||
title="Show details"
|
||||
title={t('entityPeekAheadPopover.entityCardActionsTitle')}
|
||||
to={entityRoute(getCompoundEntityRef(props.entity))}
|
||||
>
|
||||
<InfoIcon />
|
||||
|
||||
+4
-2
@@ -40,6 +40,8 @@ import {
|
||||
GroupCardActions,
|
||||
} from './CardActionComponents';
|
||||
import { debounce } from 'lodash';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/**
|
||||
* Properties for an entity popover on hover of a component.
|
||||
@@ -75,7 +77,7 @@ const maxTagChips = 4;
|
||||
*/
|
||||
export const EntityPeekAheadPopover = (props: EntityPeekAheadPopoverProps) => {
|
||||
const { entityRef, children, delayTime = 500 } = props;
|
||||
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
const classes = useStyles();
|
||||
const apiHolder = useApiHolder();
|
||||
const popupState = usePopupState({
|
||||
@@ -171,7 +173,7 @@ export const EntityPeekAheadPopover = (props: EntityPeekAheadPopoverProps) => {
|
||||
})}
|
||||
{entity.metadata.tags?.length &&
|
||||
entity.metadata.tags?.length > maxTagChips && (
|
||||
<Tooltip title="Drill into the entity to see all of the tags.">
|
||||
<Tooltip title={t('entityPeekAheadPopover.title')}>
|
||||
<Chip key="other-tags" size="small" label="..." />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
+12
-11
@@ -14,15 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { EntityErrorFilter, EntityOrphanFilter } from '../../filters';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { EntityProcessingStatusPicker } from './EntityProcessingStatusPicker';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
describe('<EntityProcessingStatusPicker/>', () => {
|
||||
it('renders all processing status options', () => {
|
||||
render(
|
||||
it('renders all processing status options', async () => {
|
||||
await renderInTestApp(
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityProcessingStatusPicker />
|
||||
</MockEntityListContextProvider>,
|
||||
@@ -34,9 +35,9 @@ describe('<EntityProcessingStatusPicker/>', () => {
|
||||
expect(screen.getByText('Has Error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('adds orphan to orphan filter', () => {
|
||||
it('adds orphan to orphan filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
@@ -53,9 +54,9 @@ describe('<EntityProcessingStatusPicker/>', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('adds error to error filter', () => {
|
||||
it('adds error to error filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
@@ -72,9 +73,9 @@ describe('<EntityProcessingStatusPicker/>', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('remove orphan from orphan filter', () => {
|
||||
it('remove orphan from orphan filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
@@ -91,9 +92,9 @@ describe('<EntityProcessingStatusPicker/>', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('remove error from error filter', () => {
|
||||
it('remove error from error filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
|
||||
+4
-1
@@ -27,6 +27,8 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import React, { useState, ReactNode } from 'react';
|
||||
import { useEntityList } from '../../hooks';
|
||||
import Autocomplete from '@material-ui/lab/Autocomplete';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactEntityProcessingStatusPickerClassKey = 'input';
|
||||
@@ -49,6 +51,7 @@ const checkedIcon = <CheckBoxIcon fontSize="small" />;
|
||||
export const EntityProcessingStatusPicker = () => {
|
||||
const classes = useStyles();
|
||||
const { updateFilters } = useEntityList();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const [selectedAdvancedItems, setSelectedAdvancedItems] = useState<string[]>(
|
||||
[],
|
||||
@@ -71,7 +74,7 @@ export const EntityProcessingStatusPicker = () => {
|
||||
return (
|
||||
<Box className={classes.root} pb={1} pt={1}>
|
||||
<Typography className={classes.label} variant="button" component="label">
|
||||
Processing Status
|
||||
{t('entityProcessingStatusPickerTitle')}
|
||||
<Autocomplete<string, true>
|
||||
PopperComponent={popperProps => (
|
||||
<div {...popperProps}>{popperProps.children as ReactNode}</div>
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render, waitFor, screen } from '@testing-library/react';
|
||||
import { fireEvent, waitFor, screen } from '@testing-library/react';
|
||||
import { EntitySearchBar } from './EntitySearchBar';
|
||||
import { EntityTextFilter } from '../../filters';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
describe('EntitySearchBar', () => {
|
||||
it('should display search value and execute set callback', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
|
||||
render(
|
||||
renderInTestApp(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
@@ -52,4 +53,4 @@ describe('EntitySearchBar', () => {
|
||||
text: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -26,6 +26,8 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import { useEntityList } from '../../hooks/useEntityListProvider';
|
||||
import { EntityTextFilter } from '../../filters';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactEntitySearchBarClassKey = 'searchToolbar' | 'input';
|
||||
@@ -49,6 +51,7 @@ const useStyles = makeStyles(
|
||||
*/
|
||||
export const EntitySearchBar = () => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const {
|
||||
updateFilters,
|
||||
@@ -85,7 +88,7 @@ export const EntitySearchBar = () => {
|
||||
aria-label="search"
|
||||
id="input-with-icon-adornment"
|
||||
className={classes.input}
|
||||
placeholder="Search"
|
||||
placeholder={t('entitySearchBarPlaceholder')}
|
||||
autoComplete="off"
|
||||
onChange={event => setSearch(event.target.value)}
|
||||
value={search}
|
||||
|
||||
@@ -29,6 +29,8 @@ import {
|
||||
humanizeEntityRef,
|
||||
} from '../EntityRefLink';
|
||||
|
||||
// TODO: column title support i18n
|
||||
|
||||
/** @public */
|
||||
export const columnFactories = Object.freeze({
|
||||
createEntityRefColumn<T extends Entity>(options: {
|
||||
|
||||
@@ -14,18 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
waitFor,
|
||||
screen,
|
||||
act,
|
||||
} from '@testing-library/react';
|
||||
import { fireEvent, waitFor, screen, act } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { EntityTagFilter } from '../../filters';
|
||||
import { EntityTagPicker } from './EntityTagPicker';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { TestApiProvider, renderInTestApp } from '@backstage/test-utils';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
|
||||
@@ -41,7 +35,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
} as unknown as CatalogApi;
|
||||
|
||||
it('renders all tags', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityTagPicker />
|
||||
@@ -57,7 +51,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
});
|
||||
|
||||
it('renders unique tags in alphabetical order', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityTagPicker />
|
||||
@@ -77,7 +71,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
});
|
||||
|
||||
it('renders tags with counts', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<EntityTagPicker showCounts />
|
||||
@@ -99,7 +93,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { tags: ['tag3'] };
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -121,7 +115,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
|
||||
it('adds tags to filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -145,7 +139,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
|
||||
it('removes tags from filters', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -173,7 +167,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -209,7 +203,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
|
||||
it('verify that user can select tags after query string has been set', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRef]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -247,7 +241,7 @@ describe('<EntityTagPicker/>', () => {
|
||||
}),
|
||||
} as unknown as CatalogApi;
|
||||
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApiRefNoTags]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
|
||||
@@ -18,6 +18,8 @@ import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { EntityTagFilter } from '../../filters';
|
||||
import { EntityAutocompletePicker } from '../EntityAutocompletePicker/EntityAutocompletePicker';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactEntityTagPickerClassKey = 'input';
|
||||
@@ -35,10 +37,11 @@ const useStyles = makeStyles(
|
||||
/** @public */
|
||||
export const EntityTagPicker = (props: EntityTagPickerProps) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
return (
|
||||
<EntityAutocompletePicker
|
||||
label="Tags"
|
||||
label={t('entityTagPickerTitle')}
|
||||
name="tags"
|
||||
path="metadata.tags"
|
||||
Filter={EntityTagFilter}
|
||||
|
||||
@@ -23,7 +23,7 @@ import { catalogApiRef } from '../../api';
|
||||
import { EntityKindFilter, EntityTypeFilter } from '../../filters';
|
||||
import { alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { GetEntityFacetsResponse } from '@backstage/catalog-client';
|
||||
|
||||
const entities: Entity[] = [
|
||||
@@ -83,7 +83,7 @@ const apis = TestApiRegistry.from(
|
||||
|
||||
describe('<EntityTypePicker/>', () => {
|
||||
it('renders available entity types', async () => {
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{ filters: { kind: new EntityKindFilter('component') } }}
|
||||
@@ -106,7 +106,7 @@ describe('<EntityTypePicker/>', () => {
|
||||
|
||||
it('sets the selected type filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -137,7 +137,7 @@ describe('<EntityTypePicker/>', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { type: 'tool' };
|
||||
await renderWithEffects(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -158,7 +158,7 @@ describe('<EntityTypePicker/>', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = await renderWithEffects(
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
|
||||
@@ -20,6 +20,8 @@ import { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter';
|
||||
|
||||
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { Select } from '@backstage/core-components';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/**
|
||||
* Props for {@link EntityTypePicker}.
|
||||
@@ -37,23 +39,24 @@ export const EntityTypePicker = (props: EntityTypePickerProps) => {
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const { error, availableTypes, selectedTypes, setSelectedTypes } =
|
||||
useEntityTypeFilter();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
alertApi.post({
|
||||
message: `Failed to load entity types`,
|
||||
message: t('entityTypePicker.errorMessage'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
if (initialFilter) {
|
||||
setSelectedTypes([initialFilter]);
|
||||
}
|
||||
}, [error, alertApi, initialFilter, setSelectedTypes]);
|
||||
}, [error, alertApi, initialFilter, setSelectedTypes, t]);
|
||||
|
||||
if (availableTypes.length === 0 || error) return null;
|
||||
|
||||
const items = [
|
||||
{ value: 'all', label: 'all' },
|
||||
{ value: 'all', label: t('entityTypePicker.optionAllTitle') },
|
||||
...availableTypes.map((type: string) => ({
|
||||
value: type,
|
||||
label: type,
|
||||
@@ -63,7 +66,7 @@ export const EntityTypePicker = (props: EntityTypePickerProps) => {
|
||||
return hidden ? null : (
|
||||
<Box pb={1} pt={1}>
|
||||
<Select
|
||||
label="Type"
|
||||
label={t('entityTypePicker.title')}
|
||||
items={items}
|
||||
selected={(items.length > 1 ? selectedTypes[0] : undefined) ?? 'all'}
|
||||
onChange={value =>
|
||||
|
||||
@@ -22,6 +22,8 @@ import Star from '@material-ui/icons/Star';
|
||||
import StarBorder from '@material-ui/icons/StarBorder';
|
||||
import React, { ComponentProps } from 'react';
|
||||
import { useStarredEntity } from '../../hooks/useStarredEntity';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
export type FavoriteEntityProps = ComponentProps<typeof IconButton> & {
|
||||
@@ -43,6 +45,7 @@ export const FavoriteEntity = (props: FavoriteEntityProps) => {
|
||||
const { toggleStarredEntity, isStarredEntity } = useStarredEntity(
|
||||
props.entity,
|
||||
);
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
return (
|
||||
<IconButton
|
||||
aria-label="favorite"
|
||||
@@ -51,7 +54,11 @@ export const FavoriteEntity = (props: FavoriteEntityProps) => {
|
||||
onClick={() => toggleStarredEntity()}
|
||||
>
|
||||
<Tooltip
|
||||
title={isStarredEntity ? 'Remove from favorites' : 'Add to favorites'}
|
||||
title={
|
||||
isStarredEntity
|
||||
? t('favoriteEntity.RemoveFromFavorites')
|
||||
: t('favoriteEntity.addToFavorites')
|
||||
}
|
||||
>
|
||||
{isStarredEntity ? <YellowStar /> : <StarBorder />}
|
||||
</Tooltip>
|
||||
|
||||
@@ -30,6 +30,8 @@ import { ColocatedPage } from './components/ColocatedPage';
|
||||
import { JsonPage } from './components/JsonPage';
|
||||
import { OverviewPage } from './components/OverviewPage';
|
||||
import { YamlPage } from './components/YamlPage';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
fullHeightDialog: {
|
||||
@@ -95,6 +97,7 @@ export function InspectEntityDialog(props: {
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const [activeTab, setActiveTab] = React.useState(0);
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab(0);
|
||||
@@ -114,7 +117,7 @@ export function InspectEntityDialog(props: {
|
||||
PaperProps={{ className: classes.fullHeightDialog }}
|
||||
>
|
||||
<DialogTitle id="entity-inspector-dialog-title">
|
||||
Entity Inspector
|
||||
{t('inspectEntityDialog.title')}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<div className={classes.root}>
|
||||
@@ -152,7 +155,7 @@ export function InspectEntityDialog(props: {
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={props.onClose} color="primary">
|
||||
Close
|
||||
{t('inspectEntityDialog.closeButtonTitle')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
+6
-1
@@ -38,6 +38,8 @@ import { catalogApiRef } from '../../../api';
|
||||
import { humanizeEntityRef } from '../../EntityRefLink';
|
||||
import { entityRouteRef } from '../../../routes';
|
||||
import { EntityKindIcon } from './EntityKindIcon';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
node: {
|
||||
@@ -198,6 +200,7 @@ function CustomNode({ node }: DependencyGraphTypes.RenderNodeProps<NodeType>) {
|
||||
|
||||
export function AncestryPage(props: { entity: Entity }) {
|
||||
const { loading, error, nodes, edges } = useAncestry(props.entity);
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
} else if (error) {
|
||||
@@ -206,7 +209,9 @@ export function AncestryPage(props: { entity: Entity }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogContentText variant="h2">Ancestry</DialogContentText>
|
||||
<DialogContentText variant="h2">
|
||||
{t('inspectEntityDialog.ancestryPage.title')}
|
||||
</DialogContentText>
|
||||
<DialogContentText gutterBottom>
|
||||
This is the ancestry of entities above the current one - as in, the
|
||||
chain(s) of entities down to the current one, where{' '}
|
||||
|
||||
+12
-6
@@ -32,6 +32,8 @@ import useAsync from 'react-use/esm/useAsync';
|
||||
import { catalogApiRef } from '../../../api';
|
||||
import { EntityRefLink } from '../../EntityRefLink';
|
||||
import { KeyValueListItem, ListItemText } from './common';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
@@ -95,6 +97,7 @@ function EntityList(props: { entities: Entity[]; header?: [string, string] }) {
|
||||
|
||||
function Contents(props: { entity: Entity }) {
|
||||
const { entity } = props;
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const { loading, error, location, originLocation, colocatedEntities } =
|
||||
useColocated(entity);
|
||||
@@ -106,12 +109,14 @@ function Contents(props: { entity: Entity }) {
|
||||
|
||||
if (!location && !originLocation) {
|
||||
return (
|
||||
<Alert severity="warning">Entity had no location information.</Alert>
|
||||
<Alert severity="warning">
|
||||
{t('inspectEntityDialog.colocatedPage.alertNoLocation')}
|
||||
</Alert>
|
||||
);
|
||||
} else if (!colocatedEntities?.length) {
|
||||
return (
|
||||
<Alert severity="info">
|
||||
There were no other entities on this location.
|
||||
{t('inspectEntityDialog.colocatedPage.alertNoEntity')}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -148,13 +153,14 @@ function Contents(props: { entity: Entity }) {
|
||||
|
||||
export function ColocatedPage(props: { entity: Entity }) {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
return (
|
||||
<>
|
||||
<DialogContentText variant="h2">Colocated</DialogContentText>
|
||||
<DialogContentText variant="h2">
|
||||
{t('inspectEntityDialog.colocatedPage.title')}
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
These are the entities that are colocated with this entity - as in, they
|
||||
originated from the same data source (e.g. came from the same YAML
|
||||
file), or from the same origin (e.g. the originally registered URL).
|
||||
{t('inspectEntityDialog.colocatedPage.description')}
|
||||
</DialogContentText>
|
||||
<div className={classes.root}>
|
||||
<Contents entity={props.entity} />
|
||||
|
||||
@@ -19,13 +19,18 @@ import { CodeSnippet } from '@backstage/core-components';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import React from 'react';
|
||||
import { sortKeys } from './util';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
export function JsonPage(props: { entity: Entity }) {
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
return (
|
||||
<>
|
||||
<DialogContentText variant="h2">Entity as JSON</DialogContentText>
|
||||
<DialogContentText variant="h2">
|
||||
{t('inspectEntityDialog.jsonPage.title')}
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
This is the raw entity data as received from the catalog, on JSON form.
|
||||
{t('inspectEntityDialog.jsonPage.description')}
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
<div style={{ fontSize: '75%' }} data-testid="code-snippet">
|
||||
|
||||
+6
-1
@@ -36,6 +36,8 @@ import {
|
||||
} from './common';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { CopyTextButton } from '@backstage/core-components';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
@@ -59,11 +61,14 @@ export function OverviewPage(props: { entity: AlphaEntity }) {
|
||||
sortBy(relations, r => r.targetRef),
|
||||
'type',
|
||||
);
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const entityRef = stringifyEntityRef(props.entity);
|
||||
return (
|
||||
<>
|
||||
<DialogContentText variant="h2">Overview</DialogContentText>
|
||||
<DialogContentText variant="h2">
|
||||
{t('inspectEntityDialog.overviewPage.title')}
|
||||
</DialogContentText>
|
||||
<div className={classes.root}>
|
||||
<Container title="Identity">
|
||||
<List dense>
|
||||
|
||||
@@ -20,13 +20,18 @@ import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import React from 'react';
|
||||
import YAML from 'yaml';
|
||||
import { sortKeys } from './util';
|
||||
import { catalogReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
export function YamlPage(props: { entity: Entity }) {
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
return (
|
||||
<>
|
||||
<DialogContentText variant="h2">Entity as YAML</DialogContentText>
|
||||
<DialogContentText variant="h2">
|
||||
{t('inspectEntityDialog.yamlPage.title')}
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
This is the raw entity data as received from the catalog, on YAML form.
|
||||
{t('inspectEntityDialog.yamlPage.description')}
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
<div style={{ fontSize: '75%' }} data-testid="code-snippet">
|
||||
|
||||
+33
-30
@@ -32,6 +32,8 @@ import { useUnregisterEntityDialogState } from './useUnregisterEntityDialogState
|
||||
import { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { Progress, ResponseErrorPanel } from '@backstage/core-components';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
advancedButton: {
|
||||
@@ -58,6 +60,7 @@ const Contents = ({
|
||||
const [showDelete, setShowDelete] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const appTitle = configApi.getOptionalString('app.title') ?? 'Backstage';
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
|
||||
const onUnregister = useCallback(
|
||||
async function onUnregisterFn() {
|
||||
@@ -86,7 +89,9 @@ const Contents = ({
|
||||
const entityName = entity.metadata.title ?? entity.metadata.name;
|
||||
onConfirm();
|
||||
alertApi.post({
|
||||
message: `Removed entity ${entityName}`,
|
||||
message: t('unregisterEntityDialog.deleteEntitySuccessMessage', {
|
||||
entityName,
|
||||
}),
|
||||
severity: 'success',
|
||||
display: 'transient',
|
||||
});
|
||||
@@ -98,13 +103,13 @@ const Contents = ({
|
||||
}
|
||||
}
|
||||
},
|
||||
[alertApi, onConfirm, state, entity],
|
||||
[alertApi, onConfirm, state, entity, t],
|
||||
);
|
||||
|
||||
const DialogActionsPanel = () => (
|
||||
<DialogActions className={classes.dialogActions}>
|
||||
<Button onClick={onClose} color="primary">
|
||||
Cancel
|
||||
{t('unregisterEntityDialog.cancelButtonTitle')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
);
|
||||
@@ -121,10 +126,10 @@ const Contents = ({
|
||||
return (
|
||||
<>
|
||||
<Alert severity="info">
|
||||
You cannot unregister this entity, since it originates from a
|
||||
protected Backstage configuration (location "{state.location}"). If
|
||||
you believe this is in error, please contact the {appTitle}{' '}
|
||||
integrator.
|
||||
{t('unregisterEntityDialog.bootstrapState.title', {
|
||||
appTitle,
|
||||
location: state.location,
|
||||
})}
|
||||
</Alert>
|
||||
|
||||
<Box marginTop={2}>
|
||||
@@ -137,7 +142,7 @@ const Contents = ({
|
||||
className={classes.advancedButton}
|
||||
onClick={() => setShowDelete(true)}
|
||||
>
|
||||
Advanced Options
|
||||
{t('unregisterEntityDialog.bootstrapState.advancedOptions')}
|
||||
</Button>
|
||||
<DialogActionsPanel />
|
||||
</>
|
||||
@@ -146,11 +151,7 @@ const Contents = ({
|
||||
{showDelete && (
|
||||
<>
|
||||
<DialogContentText>
|
||||
You have the option to delete the entity itself from the
|
||||
catalog. Note that this should only be done if you know that the
|
||||
catalog file has been deleted at, or moved from, its origin
|
||||
location. If that is not the case, the entity will reappear
|
||||
shortly as the next refresh round is performed by the catalog.
|
||||
{t('unregisterEntityDialog.bootstrapState.advancedDescription')}
|
||||
</DialogContentText>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -158,7 +159,7 @@ const Contents = ({
|
||||
disabled={busy}
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete Entity
|
||||
{t('unregisterEntityDialog.deleteButtonTitle')}
|
||||
</Button>
|
||||
<DialogActionsPanel />
|
||||
</>
|
||||
@@ -172,8 +173,7 @@ const Contents = ({
|
||||
return (
|
||||
<>
|
||||
<DialogContentText>
|
||||
This entity does not seem to originate from a registered location. You
|
||||
therefore only have the option to delete it outright from the catalog.
|
||||
{t('unregisterEntityDialog.onlyDeleteStateTitle')}
|
||||
</DialogContentText>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -181,7 +181,7 @@ const Contents = ({
|
||||
disabled={busy}
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete Entity
|
||||
{t('unregisterEntityDialog.deleteButtonTitle')}
|
||||
</Button>
|
||||
<DialogActionsPanel />
|
||||
</>
|
||||
@@ -192,7 +192,7 @@ const Contents = ({
|
||||
return (
|
||||
<>
|
||||
<DialogContentText>
|
||||
This action will unregister the following entities:
|
||||
{t('unregisterEntityDialog.unregisterState.title')}
|
||||
</DialogContentText>
|
||||
<DialogContentText component="ul">
|
||||
{state.colocatedEntities.map(e => (
|
||||
@@ -202,13 +202,15 @@ const Contents = ({
|
||||
))}
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
Located at the following location:
|
||||
{t('unregisterEntityDialog.unregisterState.subTitle')}
|
||||
</DialogContentText>
|
||||
<DialogContentText component="ul">
|
||||
<li>{state.location}</li>
|
||||
</DialogContentText>
|
||||
<DialogContentText>
|
||||
To undo, just re-register the entity in {appTitle}.
|
||||
{t('unregisterEntityDialog.unregisterState.description', {
|
||||
appTitle,
|
||||
})}
|
||||
</DialogContentText>
|
||||
<Box marginTop={2}>
|
||||
<Button
|
||||
@@ -217,7 +219,7 @@ const Contents = ({
|
||||
disabled={busy}
|
||||
onClick={onUnregister}
|
||||
>
|
||||
Unregister Location
|
||||
{t('unregisterEntityDialog.unregisterState.unregisterButtonTitle')}
|
||||
</Button>
|
||||
{!showDelete && (
|
||||
<Box component="span" marginLeft={2}>
|
||||
@@ -228,7 +230,7 @@ const Contents = ({
|
||||
className={classes.advancedButton}
|
||||
onClick={() => setShowDelete(true)}
|
||||
>
|
||||
Advanced Options
|
||||
{t('unregisterEntityDialog.unregisterState.advancedOptions')}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
@@ -240,11 +242,7 @@ const Contents = ({
|
||||
<Divider />
|
||||
</Box>
|
||||
<DialogContentText>
|
||||
You also have the option to delete the entity itself from the
|
||||
catalog. Note that this should only be done if you know that the
|
||||
catalog file has been deleted at, or moved from, its origin
|
||||
location. If that is not the case, the entity will reappear
|
||||
shortly as the next refresh round is performed by the catalog.
|
||||
{t('unregisterEntityDialog.unregisterState.advancedDescription')}
|
||||
</DialogContentText>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -252,7 +250,7 @@ const Contents = ({
|
||||
disabled={busy}
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete Entity
|
||||
{t('unregisterEntityDialog.deleteButtonTitle')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
@@ -260,7 +258,11 @@ const Contents = ({
|
||||
);
|
||||
}
|
||||
|
||||
return <Alert severity="error">Internal error: Unknown state</Alert>;
|
||||
return (
|
||||
<Alert severity="error">
|
||||
{t('unregisterEntityDialog.errorStateTitle')}
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
/** @public */
|
||||
@@ -274,10 +276,11 @@ export type UnregisterEntityDialogProps = {
|
||||
/** @public */
|
||||
export const UnregisterEntityDialog = (props: UnregisterEntityDialogProps) => {
|
||||
const { open, onConfirm, onClose, entity } = props;
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
<DialogTitle id="responsive-dialog-title">
|
||||
Are you sure you want to unregister this entity?
|
||||
{t('unregisterEntityDialog.title')}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Contents entity={entity} onConfirm={onConfirm} onClose={onClose} />
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render, waitFor, screen } from '@testing-library/react';
|
||||
import { fireEvent, waitFor, screen } from '@testing-library/react';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
import { UserListPicker, UserListPickerProps } from './UserListPicker';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
@@ -30,7 +30,11 @@ import {
|
||||
QueryEntitiesInitialRequest,
|
||||
} from '@backstage/catalog-client';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { MockStorageApi, TestApiRegistry } from '@backstage/test-utils';
|
||||
import {
|
||||
MockStorageApi,
|
||||
TestApiRegistry,
|
||||
renderInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import {
|
||||
ConfigApi,
|
||||
@@ -145,7 +149,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('renders filter groups', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider value={{}}>
|
||||
<UserListPicker />
|
||||
@@ -164,7 +168,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('renders filters', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -208,7 +212,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('respects other frontend filters in counts', async () => {
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -246,7 +250,7 @@ describe('<UserListPicker />', () => {
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { user: 'owned', kind: 'component' };
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -288,7 +292,7 @@ describe('<UserListPicker />', () => {
|
||||
|
||||
it('updates user filter when a menuitem is selected', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -334,7 +338,7 @@ describe('<UserListPicker />', () => {
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
@@ -407,7 +411,7 @@ describe('<UserListPicker />', () => {
|
||||
it('does not reset the filter while entities are loading', async () => {
|
||||
mockCatalogApi.queryEntities?.mockReturnValue(new Promise(() => {}));
|
||||
|
||||
render(<Picker initialFilter="owned" />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalled(),
|
||||
@@ -433,7 +437,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="owned" />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
@@ -444,7 +448,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('does not reset the filter when request is empty', async () => {
|
||||
render(<Picker initialFilter="owned" filters={{}} />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" filters={{}} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(1);
|
||||
@@ -473,7 +477,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="owned" />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
@@ -489,7 +493,7 @@ describe('<UserListPicker />', () => {
|
||||
() => new Promise(() => {}),
|
||||
);
|
||||
|
||||
render(<Picker initialFilter="starred" />);
|
||||
await renderInTestApp(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalled(),
|
||||
@@ -512,7 +516,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="starred" />);
|
||||
await renderInTestApp(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
@@ -537,7 +541,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="starred" />);
|
||||
await renderInTestApp(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
@@ -563,7 +567,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="owned" />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
@@ -574,7 +578,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('does not reset the filter when entities are loaded', async () => {
|
||||
render(<Picker initialFilter="owned" />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
@@ -588,7 +592,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('does not reset the filter when request is empty xxxx', async () => {
|
||||
render(<Picker initialFilter="owned" filters={{}} />);
|
||||
await renderInTestApp(<Picker initialFilter="owned" filters={{}} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(1);
|
||||
@@ -619,7 +623,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="starred" />);
|
||||
await renderInTestApp(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
@@ -630,7 +634,7 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
|
||||
it('does not reset the filter when entities are loaded', async () => {
|
||||
render(<Picker initialFilter="starred" />);
|
||||
await renderInTestApp(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
|
||||
@@ -36,6 +36,11 @@ import { UserListFilterKind } from '../../types';
|
||||
import { useOwnedEntitiesCount } from './useOwnedEntitiesCount';
|
||||
import { useAllEntitiesCount } from './useAllEntitiesCount';
|
||||
import { useStarredEntitiesCount } from './useStarredEntitiesCount';
|
||||
import {
|
||||
TranslationFunction,
|
||||
useTranslationRef,
|
||||
} from '@backstage/core-plugin-api/alpha';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactUserListPickerClassKey =
|
||||
@@ -83,29 +88,32 @@ export type ButtonGroup = {
|
||||
}[];
|
||||
};
|
||||
|
||||
function getFilterGroups(orgName: string | undefined): ButtonGroup[] {
|
||||
function getFilterGroups(
|
||||
orgName: string,
|
||||
t: TranslationFunction<typeof catalogReactTranslationRef.T>,
|
||||
): ButtonGroup[] {
|
||||
return [
|
||||
{
|
||||
name: 'Personal',
|
||||
name: t('userListPicker.personalFilter.title'),
|
||||
items: [
|
||||
{
|
||||
id: 'owned',
|
||||
label: 'Owned',
|
||||
label: t('userListPicker.personalFilter.ownedLabel'),
|
||||
icon: SettingsIcon,
|
||||
},
|
||||
{
|
||||
id: 'starred',
|
||||
label: 'Starred',
|
||||
label: t('userListPicker.personalFilter.starredLabel'),
|
||||
icon: StarIcon,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: orgName ?? 'Company',
|
||||
name: orgName,
|
||||
items: [
|
||||
{
|
||||
id: 'all',
|
||||
label: 'All',
|
||||
label: t('userListPicker.orgFilterAllLabel'),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -123,7 +131,10 @@ export const UserListPicker = (props: UserListPickerProps) => {
|
||||
const { initialFilter, availableFilters } = props;
|
||||
const classes = useStyles();
|
||||
const configApi = useApi(configApiRef);
|
||||
const orgName = configApi.getOptionalString('organization.name') ?? 'Company';
|
||||
const { t } = useTranslationRef(catalogReactTranslationRef);
|
||||
const orgName =
|
||||
configApi.getOptionalString('organization.name') ??
|
||||
t('userListPicker.defaultOrgName');
|
||||
const {
|
||||
filters,
|
||||
updateFilters,
|
||||
@@ -133,7 +144,7 @@ export const UserListPicker = (props: UserListPickerProps) => {
|
||||
// Remove group items that aren't in availableFilters and exclude
|
||||
// any now-empty groups.
|
||||
const userAndGroupFilterIds = ['starred', 'all'];
|
||||
const filterGroups = getFilterGroups(orgName)
|
||||
const filterGroups = getFilterGroups(orgName, t)
|
||||
.map(filterGroup => ({
|
||||
...filterGroup,
|
||||
items: filterGroup.items.filter(({ id }) =>
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
alertApiRef,
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
errorApiRef,
|
||||
IdentityApi,
|
||||
identityApiRef,
|
||||
storageApiRef,
|
||||
@@ -39,6 +40,8 @@ import {
|
||||
} from '../filters';
|
||||
import { EntityListProvider, useEntityList } from './useEntityListProvider';
|
||||
import { useMountEffect } from '@react-hookz/web';
|
||||
import { translationApiRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { MockTranslationApi } from '@backstage/test-utils/alpha';
|
||||
|
||||
const entities: Entity[] = [
|
||||
{
|
||||
@@ -110,6 +113,8 @@ const createWrapper =
|
||||
[storageApiRef, MockStorageApi.create()],
|
||||
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
|
||||
[alertApiRef, { post: jest.fn() }],
|
||||
[translationApiRef, MockTranslationApi.create()],
|
||||
[errorApiRef, { error$: jest.fn(), post: jest.fn() }],
|
||||
]}
|
||||
>
|
||||
<EntityListProvider pagination={options.pagination}>
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @alpha */
|
||||
export const catalogReactTranslationRef = createTranslationRef({
|
||||
id: 'catalog-react',
|
||||
messages: {
|
||||
catalogFilter: {
|
||||
title: 'Filters',
|
||||
buttonTitle: 'Filters',
|
||||
},
|
||||
entityKindPicker: {
|
||||
title: 'Kind',
|
||||
errorMessage: 'Failed to load entity kinds',
|
||||
},
|
||||
entityLifecyclePickerTitle: 'Lifecycle',
|
||||
entityNamespacePickerTitle: 'Namespace',
|
||||
entityOwnerPickerTitle: 'Owner',
|
||||
entityPeekAheadPopover: {
|
||||
title: 'Drill into the entity to see all of the tags.',
|
||||
emailCardAction: {
|
||||
title: 'Email {{email}}',
|
||||
subTitle: 'mailto {{email}}',
|
||||
},
|
||||
entityCardActionsTitle: 'Show details',
|
||||
},
|
||||
entityProcessingStatusPickerTitle: 'Processing Status',
|
||||
entitySearchBarPlaceholder: 'Search',
|
||||
entityTagPickerTitle: 'Tags',
|
||||
entityTypePicker: {
|
||||
title: 'Type',
|
||||
errorMessage: 'Failed to load entity types',
|
||||
optionAllTitle: 'all',
|
||||
},
|
||||
favoriteEntity: {
|
||||
addToFavorites: 'Add to favorites',
|
||||
RemoveFromFavorites: 'Remove from favorites',
|
||||
},
|
||||
inspectEntityDialog: {
|
||||
title: 'Entity Inspector',
|
||||
closeButtonTitle: 'Close',
|
||||
ancestryPage: {
|
||||
title: 'Ancestry',
|
||||
},
|
||||
colocatedPage: {
|
||||
title: 'Colocated',
|
||||
description:
|
||||
'These are the entities that are colocated with this entity - as in, they originated from the same data source (e.g. came from the same YAML file), or from the same origin (e.g. the originally registered URL).',
|
||||
alertNoLocation: 'Entity had no location information.',
|
||||
alertNoEntity: 'There were no other entities on this location.',
|
||||
},
|
||||
jsonPage: {
|
||||
title: 'Entity as JSON',
|
||||
description:
|
||||
'This is the raw entity data as received from the catalog, on JSON form.',
|
||||
},
|
||||
overviewPage: {
|
||||
title: 'Overview',
|
||||
},
|
||||
yamlPage: {
|
||||
title: 'Entity as YAML',
|
||||
description:
|
||||
'This is the raw entity data as received from the catalog, on YAML form.',
|
||||
},
|
||||
},
|
||||
unregisterEntityDialog: {
|
||||
title: 'Are you sure you want to unregister this entity?',
|
||||
cancelButtonTitle: 'Cancel',
|
||||
deleteButtonTitle: 'Delete Entity',
|
||||
deleteEntitySuccessMessage: 'Removed entity {{entityName}}',
|
||||
bootstrapState: {
|
||||
title:
|
||||
'You cannot unregister this entity, since it originates from a protected Backstage configuration (location "{{location}}"). If you believe this is in error, please contact the {{appTitle}} integrator.',
|
||||
advancedDescription:
|
||||
'You have the option to delete the entity itself from the catalog. Note that this should only be done if you know that the catalog file has been deleted at, or moved from, its origin location. If that is not the case, the entity will reappear shortly as the next refresh round is performed by the catalog.',
|
||||
advancedOptions: 'Advanced Options',
|
||||
},
|
||||
onlyDeleteStateTitle:
|
||||
'This entity does not seem to originate from a registered location. You therefore only have the option to delete it outright from the catalog.',
|
||||
unregisterState: {
|
||||
title: 'This action will unregister the following entities:',
|
||||
subTitle: 'Located at the following location:',
|
||||
description: 'To undo, just re-register the entity in {{appTitle}}.',
|
||||
unregisterButtonTitle: 'Unregister Location',
|
||||
advancedOptions: 'Advanced Options',
|
||||
advancedDescription:
|
||||
'You also have the option to delete the entity itself from the catalog. Note that this should only be done if you know that the catalog file has been deleted at, or moved from, its origin location. If that is not the case, the entity will reappear shortly as the next refresh round is performed by the catalog.',
|
||||
},
|
||||
errorStateTitle: 'Internal error: Unknown state',
|
||||
},
|
||||
userListPicker: {
|
||||
defaultOrgName: 'Company',
|
||||
personalFilter: {
|
||||
title: 'Personal',
|
||||
ownedLabel: 'Owned',
|
||||
starredLabel: 'Starred',
|
||||
},
|
||||
orgFilterAllLabel: 'All',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -19,6 +19,81 @@ export const catalogTranslationRef: TranslationRef<
|
||||
{
|
||||
readonly 'indexPage.title': '{{orgName}} Catalog';
|
||||
readonly 'indexPage.createButtonTitle': 'Create';
|
||||
readonly 'deleteEntity.description': 'This entity is not referenced by any location and is therefore not receiving updates. Click here to delete.';
|
||||
readonly 'deleteEntity.cancelButtonTitle': 'Cancel';
|
||||
readonly 'deleteEntity.deleteButtonTitle': 'Delete';
|
||||
readonly 'deleteEntity.dialogTitle': 'Are you sure you want to delete this entity?';
|
||||
readonly 'indexPage.title': '{{orgName}} Catalog';
|
||||
readonly 'indexPage.createButtonTitle': 'Create';
|
||||
readonly 'indexPage.supportButtonContent': 'All your software catalog entities';
|
||||
readonly 'aboutCard.title': 'About';
|
||||
readonly 'aboutCard.refreshButtonTitle': 'Schedule entity refresh';
|
||||
readonly 'aboutCard.editButtonTitle': 'Edit Metadata';
|
||||
readonly 'aboutCard.refreshScheduledMessage': 'Refresh scheduled';
|
||||
readonly 'aboutCard.launchTemplate': 'Launch Template';
|
||||
readonly 'aboutCard.viewTechdocs': 'View TechDocs';
|
||||
readonly 'aboutCard.viewSource': 'View Source';
|
||||
readonly 'aboutCard.descriptionField.value': 'No description';
|
||||
readonly 'aboutCard.descriptionField.label': 'Description';
|
||||
readonly 'aboutCard.ownerField.value': 'No Owner';
|
||||
readonly 'aboutCard.ownerField.label': 'Owner';
|
||||
readonly 'aboutCard.domainField.value': 'No Domain';
|
||||
readonly 'aboutCard.domainField.label': 'Domain';
|
||||
readonly 'aboutCard.systemField.value': 'No System';
|
||||
readonly 'aboutCard.systemField.label': 'System';
|
||||
readonly 'aboutCard.parentComponentField.value': 'No Parent Component';
|
||||
readonly 'aboutCard.parentComponentField.label': 'Parent Component';
|
||||
readonly 'aboutCard.typeField.label': 'Type';
|
||||
readonly 'aboutCard.lifecycleField.label': 'Lifecycle';
|
||||
readonly 'aboutCard.tagsField.value': 'No Tags';
|
||||
readonly 'aboutCard.tagsField.label': 'Tags';
|
||||
readonly 'aboutCard.targetsField.label': 'Targets';
|
||||
readonly 'searchResultItem.lifecycle': 'Lifecycle';
|
||||
readonly 'searchResultItem.Owner': 'Owner';
|
||||
readonly 'catalogTable.warningPanelTitle': 'Could not fetch catalog entities.';
|
||||
readonly 'catalogTable.viewActionTitle': 'View';
|
||||
readonly 'catalogTable.editActionTitle': 'Edit';
|
||||
readonly 'catalogTable.starActionTitle': 'Add to favorites';
|
||||
readonly 'catalogTable.unStarActionTitle': 'Remove from favorites';
|
||||
readonly 'dependencyOfComponentsCard.title': 'Dependency of components';
|
||||
readonly 'dependencyOfComponentsCard.emptyMessage': 'No component depends on this component';
|
||||
readonly 'dependsOnComponentsCard.title': 'Depends on components';
|
||||
readonly 'dependsOnComponentsCard.emptyMessage': 'No component is a dependency of this component';
|
||||
readonly 'dependsOnResourcesCard.title': 'Depends on resources';
|
||||
readonly 'dependsOnResourcesCard.emptyMessage': 'No resource is a dependency of this component';
|
||||
readonly 'entityContextMenu.copiedMessage': 'Copied!';
|
||||
readonly 'entityContextMenu.moreButtonTitle': 'More';
|
||||
readonly 'entityContextMenu.inspectMenuTitle': 'Inspect entity';
|
||||
readonly 'entityContextMenu.copyURLMenuTitle': 'Copy entity URL';
|
||||
readonly 'entityContextMenu.unregisterMenuTitle': 'Unregister entity';
|
||||
readonly 'entityLabelsCard.title': 'Labels';
|
||||
readonly 'entityLabelsCard.emptyDescription': 'No labels defined for this entity. You can add labels to your entity YAML as shown in the highlighted example below:';
|
||||
readonly 'entityLabelsCard.readMoreButtonTitle': 'Read more';
|
||||
readonly 'entityLabels.warningPanelTitle': 'Entity not found';
|
||||
readonly 'entityLabels.ownerLabel': 'Owner';
|
||||
readonly 'entityLabels.lifecycleLabel': 'Lifecycle';
|
||||
readonly 'entityLinksCard.title': 'Links';
|
||||
readonly 'entityLinksCard.emptyDescription': 'No links defined for this entity. You can add links to your entity YAML as shown in the highlighted example below:';
|
||||
readonly 'entityLinksCard.readMoreButtonTitle': 'Read more';
|
||||
readonly 'entityNotFound.title': 'Entity was not found';
|
||||
readonly 'entityNotFound.description': 'Want to help us build this? Check out our Getting Started documentation.';
|
||||
readonly 'entityNotFound.docButtonTitle': 'DOCS';
|
||||
readonly entityProcessingErrorsDescription: 'The error below originates from';
|
||||
readonly entityRelationWarningDescription: "This entity has relations to other entities, which can't be found in the catalog.\n Entities not found are: ";
|
||||
readonly 'hasComponentsCard.title': 'Has components';
|
||||
readonly 'hasComponentsCard.emptyMessage': 'No component is part of this system';
|
||||
readonly 'hasResourcesCard.title': 'Has resources';
|
||||
readonly 'hasResourcesCard.emptyMessage': 'No resource is part of this system';
|
||||
readonly 'hasSubcomponentsCard.title': 'Has subcomponents';
|
||||
readonly 'hasSubcomponentsCard.emptyMessage': 'No subcomponent is part of this component';
|
||||
readonly 'hasSystemsCard.title': 'Has systems';
|
||||
readonly 'hasSystemsCard.emptyMessage': 'No system is part of this domain';
|
||||
readonly 'relatedEntitiesCard.emptyHelpLinkTitle': 'Learn how to change this.';
|
||||
readonly 'systemDiagramCard.title': 'System Diagram';
|
||||
readonly 'systemDiagramCard.description': 'Use pinch & zoo to move around the diagram.';
|
||||
readonly 'systemDiagramCard.edgeLabels.dependsOn': 'depends on';
|
||||
readonly 'systemDiagramCard.edgeLabels.partOf': 'part of';
|
||||
readonly 'systemDiagramCard.edgeLabels.provides': 'provides';
|
||||
}
|
||||
>;
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
export * from './alpha/index';
|
||||
export { default } from './alpha/index';
|
||||
export { catalogTranslationRef } from './translation';
|
||||
export * from './translation';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { CardHeader } from './CardHeader';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
@@ -30,7 +30,7 @@ import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
|
||||
describe('CardHeader', () => {
|
||||
it('should select the correct theme from the theme provider from the header', () => {
|
||||
it('should select the correct theme from the theme provider from the header', async () => {
|
||||
// Can't really test what we want here.
|
||||
// But we can check that we call the getPage theme with the right type of template at least.
|
||||
const mockTheme = {
|
||||
@@ -38,7 +38,7 @@ describe('CardHeader', () => {
|
||||
getPageTheme: jest.fn(lightTheme.getPageTheme),
|
||||
};
|
||||
|
||||
render(
|
||||
await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user