diff --git a/.changeset/green-lies-hammer.md b/.changeset/green-lies-hammer.md
new file mode 100644
index 0000000000..af1c2e6e92
--- /dev/null
+++ b/.changeset/green-lies-hammer.md
@@ -0,0 +1,8 @@
+---
+'@backstage/core-components': patch
+'@backstage/test-utils': patch
+'@backstage/plugin-api-docs': patch
+'@backstage/plugin-catalog': patch
+---
+
+Updated the layout of catalog and API index pages to handle smaller screen sizes. This adds responsive wrappers to the entity tables, and switches filters to a drawer when width-constrained. If you have created a custom catalog or API index page, you will need to update the page structure to match the updated [catalog customization](https://backstage.io/docs/features/software-catalog/catalog-customization) documentation.
diff --git a/docs/features/software-catalog/catalog-customization.md b/docs/features/software-catalog/catalog-customization.md
index e0228bf7d0..0c11d8f476 100644
--- a/docs/features/software-catalog/catalog-customization.md
+++ b/docs/features/software-catalog/catalog-customization.md
@@ -27,27 +27,33 @@ default catalog page and create a component in a
```tsx
// imports, etc omitted for brevity. for full source see:
// https://github.com/backstage/backstage/blob/master/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx
-export const CustomCatalogPage = () => {
+export const CustomCatalogPage = ({
+ columns,
+ actions,
+ initiallySelectedFilter = 'owned',
+}: CatalogPageProps) => {
return (
-
+ All your software catalog entities
-
-
+ >
);
};
diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerLayout.tsx b/packages/core-components/src/layout/Page/PageWithHeader.tsx
similarity index 50%
rename from plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerLayout.tsx
rename to packages/core-components/src/layout/Page/PageWithHeader.tsx
index 5638c23634..70a6c1f693 100644
--- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerLayout.tsx
+++ b/packages/core-components/src/layout/Page/PageWithHeader.tsx
@@ -14,27 +14,22 @@
* limitations under the License.
*/
-import React from 'react';
+import React, { PropsWithChildren, ComponentProps } from 'react';
-import { Header, Page } from '@backstage/core-components';
-import { useApi, configApiRef } from '@backstage/core-plugin-api';
+import { Header } from '../Header';
+import { Page } from './Page';
-type Props = {
- children?: React.ReactNode;
-};
-export const ApiExplorerLayout = ({ children }: Props) => {
- const configApi = useApi(configApiRef);
- const generatedSubtitle = `${
- configApi.getOptionalString('organization.name') ?? 'Backstage'
- } API Explorer`;
- return (
-
-
- {children}
-
- );
+type PageWithHeaderProps = ComponentProps & {
+ themeId: string;
};
+
+export const PageWithHeader = ({
+ themeId,
+ children,
+ ...props
+}: PropsWithChildren) => (
+
+
+ {children}
+
+);
diff --git a/packages/core-components/src/layout/Page/index.ts b/packages/core-components/src/layout/Page/index.ts
index d2523e8467..91db73657e 100644
--- a/packages/core-components/src/layout/Page/index.ts
+++ b/packages/core-components/src/layout/Page/index.ts
@@ -15,3 +15,4 @@
*/
export { Page } from './Page';
+export { PageWithHeader } from './PageWithHeader';
diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md
index d85db1feb7..187867d24f 100644
--- a/packages/test-utils/api-report.md
+++ b/packages/test-utils/api-report.md
@@ -15,16 +15,15 @@ import { RouteRef } from '@backstage/core-plugin-api';
import { StorageApi } from '@backstage/core-plugin-api';
import { StorageValueChange } from '@backstage/core-plugin-api';
-// Warning: (ae-forgotten-export) The symbol "Breakpoint" needs to be exported by the entry point index.d.ts
+// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (ae-missing-release-tag) "mockBreakpoint" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public (undocumented)
-export function mockBreakpoint(
- initialBreakpoint?: Breakpoint,
-): {
- set(breakpoint: Breakpoint): void;
- remove(): void;
-};
+// @public
+export function mockBreakpoint({
+ matches,
+}: {
+ matches?: boolean | undefined;
+}): void;
// Warning: (ae-missing-release-tag) "MockErrorApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
diff --git a/packages/test-utils/src/testUtils/mockBreakpoint.ts b/packages/test-utils/src/testUtils/mockBreakpoint.ts
index 37f0e761b6..3335284f88 100644
--- a/packages/test-utils/src/testUtils/mockBreakpoint.ts
+++ b/packages/test-utils/src/testUtils/mockBreakpoint.ts
@@ -14,80 +14,30 @@
* limitations under the License.
*/
-import { act } from '@testing-library/react';
-
-type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
-
-const queryToBreakpoint = {
- '(min-width:1920px)': 'xl',
- '(min-width:1280px)': 'lg',
- '(min-width:960px)': 'md',
- '(min-width:600px)': 'sm',
- '(min-width:0px)': 'xs',
-} as Record;
-
-function toBreakpoint(query: string) {
- const breakpoint = queryToBreakpoint[query];
- if (!breakpoint) {
- throw new Error(
- `received unknown media query in breakpoint mock: '${query}'`,
- );
- }
- return breakpoint;
-}
-
-type Listener = (event: { matches: boolean }) => void;
-
-interface QueryList {
- addListener(listener: Listener): void;
- removeListener(listener: Listener): void;
- matches: boolean;
-}
-
-interface Query {
- query: string;
- queryList: QueryList;
- listeners: Set;
-}
-
-export default function mockBreakpoint(initialBreakpoint: Breakpoint = 'xl') {
- let currentBreakpoint = initialBreakpoint;
- const queries = Array();
-
- const previousMatchMedia: any = (window as any).matchMedia;
-
- (window as any).matchMedia = (query: string): QueryList => {
- const listeners = new Set();
-
- const queryList: QueryList = {
- addListener(listener) {
- listeners.add(listener);
- },
- removeListener(listener) {
- listeners.delete(listener);
- },
- matches: toBreakpoint(query) === currentBreakpoint,
- };
-
- queries.push({ query, queryList, listeners });
-
- return queryList;
- };
-
- return {
- set(breakpoint: Breakpoint) {
- currentBreakpoint = breakpoint;
-
- act(() => {
- queries.forEach(({ query, queryList, listeners }) => {
- const matches = toBreakpoint(query) === breakpoint;
- queryList.matches = matches;
- listeners.forEach(listener => listener({ matches }));
- });
- });
- },
- remove() {
- (window as any).matchMedia = previousMatchMedia;
- },
- };
+/**
+ * This is a mocking method suggested in the Jest Doc's, as it is not implemented in JSDOM yet.
+ * It can be used to mock values when the MUI `useMediaQuery` hook if it is used in a tested component.
+ *
+ * For issues checkout the documentation:
+ * https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
+ *
+ * If there are any updates from MUI React on testing `useMediaQuery` this mock should be replaced
+ * https://material-ui.com/components/use-media-query/#testing
+ *
+ * @param matchMediaOptions
+ */
+export default function mockBreakpoint({ matches = false }) {
+ Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: matches,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // deprecated
+ removeListener: jest.fn(), // deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+ });
}
diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md
index a368cf5675..639485a42d 100644
--- a/plugins/api-docs/api-report.md
+++ b/plugins/api-docs/api-report.md
@@ -52,14 +52,16 @@ const apiDocsPlugin: BackstagePlugin<
export { apiDocsPlugin };
export { apiDocsPlugin as plugin };
-// Warning: (ae-forgotten-export) The symbol "ApiExplorerPageProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ApiExplorerPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const ApiExplorerPage: ({
initiallySelectedFilter,
columns,
-}: ApiExplorerPageProps) => JSX.Element;
+}: {
+ initiallySelectedFilter?: UserListFilterKind | undefined;
+ columns?: TableColumn[] | undefined;
+}) => JSX.Element;
// Warning: (ae-missing-release-tag) "ApiTypeTitle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx
index 9a8b725ffc..361ce7b55b 100644
--- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx
+++ b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx
@@ -14,6 +14,21 @@
* limitations under the License.
*/
+import {
+ Content,
+ ContentHeader,
+ PageWithHeader,
+ SupportButton,
+ TableColumn,
+} from '@backstage/core-components';
+import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
+import {
+ CatalogTable,
+ CatalogTableRow,
+ FilteredEntityLayout,
+ EntityListContainer,
+ FilterContainer,
+} from '@backstage/plugin-catalog';
import {
EntityKindPicker,
EntityLifecyclePicker,
@@ -24,29 +39,10 @@ import {
UserListFilterKind,
UserListPicker,
} from '@backstage/plugin-catalog-react';
-import { CatalogTable, CatalogTableRow } from '@backstage/plugin-catalog';
-import { Button, makeStyles } from '@material-ui/core';
+import { Button } from '@material-ui/core';
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { createComponentRouteRef } from '../../routes';
-import { ApiExplorerLayout } from './ApiExplorerLayout';
-
-import {
- Content,
- ContentHeader,
- SupportButton,
- TableColumn,
-} from '@backstage/core-components';
-import { useRouteRef } from '@backstage/core-plugin-api';
-
-const useStyles = makeStyles(theme => ({
- contentWrapper: {
- display: 'grid',
- gridTemplateAreas: "'filters' 'table'",
- gridTemplateColumns: '250px 1fr',
- gridColumnGap: theme.spacing(2),
- },
-}));
const defaultColumns: TableColumn[] = [
CatalogTable.columns.createNameColumn({ defaultKind: 'API' }),
@@ -58,7 +54,7 @@ const defaultColumns: TableColumn[] = [
CatalogTable.columns.createTagsColumn(),
];
-export type ApiExplorerPageProps = {
+type ApiExplorerPageProps = {
initiallySelectedFilter?: UserListFilterKind;
columns?: TableColumn[];
};
@@ -67,11 +63,19 @@ export const ApiExplorerPage = ({
initiallySelectedFilter = 'all',
columns,
}: ApiExplorerPageProps) => {
- const styles = useStyles();
const createComponentLink = useRouteRef(createComponentRouteRef);
+ const configApi = useApi(configApiRef);
+ const generatedSubtitle = `${
+ configApi.getOptionalString('organization.name') ?? 'Backstage'
+ } API Explorer`;
return (
-
+
{createComponentLink && (
@@ -86,20 +90,22 @@ export const ApiExplorerPage = ({
)}
All your APIs
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
-
+
);
};
diff --git a/plugins/catalog-react/src/testUtils/providers.tsx b/plugins/catalog-react/src/testUtils/providers.tsx
index 3639715da2..1f9e4f40d6 100644
--- a/plugins/catalog-react/src/testUtils/providers.tsx
+++ b/plugins/catalog-react/src/testUtils/providers.tsx
@@ -52,8 +52,8 @@ export const MockEntityListContextProvider = ({
const defaultContext: EntityListContextProps = {
entities: [],
backendEntities: [],
- updateFilters: updateFilters,
- filters: filters,
+ updateFilters,
+ filters,
loading: false,
queryParameters: {},
};
diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md
index ec8bd66385..a416e70038 100644
--- a/plugins/catalog/api-report.md
+++ b/plugins/catalog/api-report.md
@@ -39,7 +39,7 @@ export function AboutCard({ variant }: AboutCardProps): JSX.Element;
// Warning: (ae-missing-release-tag) "AboutContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const AboutContent: ({ entity }: Props_2) => JSX.Element;
+export const AboutContent: ({ entity }: Props) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "AboutField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -50,7 +50,7 @@ export const AboutField: ({
value,
gridSizes,
children,
-}: Props_3) => JSX.Element;
+}: Props_2) => JSX.Element;
// Warning: (ae-missing-release-tag) "CatalogClientWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -109,17 +109,11 @@ export const CatalogEntityPage: () => JSX.Element;
//
// @public (undocumented)
export const CatalogIndexPage: ({
- initiallySelectedFilter,
columns,
actions,
+ initiallySelectedFilter,
}: CatalogPageProps) => JSX.Element;
-// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
-// Warning: (ae-missing-release-tag) "CatalogLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const CatalogLayout: ({ children }: Props) => JSX.Element;
-
// Warning: (ae-missing-release-tag) "catalogPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -305,6 +299,13 @@ export const EntityLinksCard: ({
variant?: 'gridItem' | undefined;
}) => JSX.Element;
+// Warning: (ae-missing-release-tag) "EntityListContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const EntityListContainer: ({
+ children,
+}: PropsWithChildren<{}>) => JSX.Element;
+
// Warning: (ae-missing-release-tag) "EntityOrphanWarning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
@@ -343,6 +344,20 @@ export const EntitySwitch: {
// @public (undocumented)
export const EntitySystemDiagramCard: SystemDiagramCard;
+// Warning: (ae-missing-release-tag) "FilterContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const FilterContainer: ({
+ children,
+}: PropsWithChildren<{}>) => JSX.Element;
+
+// Warning: (ae-missing-release-tag) "FilteredEntityLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const FilteredEntityLayout: ({
+ children,
+}: PropsWithChildren<{}>) => JSX.Element;
+
// Warning: (ae-missing-release-tag) "isComponentType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
diff --git a/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx b/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx
deleted file mode 100644
index 2471ac9df2..0000000000
--- a/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2020 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 React from 'react';
-
-import { configApiRef, useApi } from '@backstage/core-plugin-api';
-import { Header, Page } from '@backstage/core-components';
-
-type Props = {
- children?: React.ReactNode;
-};
-
-export const CatalogLayout = ({ children }: Props) => {
- const orgName =
- useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage';
-
- return (
-
-
- {children}
-
- );
-};
-
-export default CatalogLayout;
diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx
index 584eea0850..e09aec55e7 100644
--- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx
+++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx
@@ -26,6 +26,7 @@ import {
MockStorageApi,
renderWithEffects,
wrapInTestApp,
+ mockBreakpoint,
} from '@backstage/test-utils';
import { fireEvent, screen } from '@testing-library/react';
import React from 'react';
@@ -244,4 +245,13 @@ describe('CatalogPage', () => {
screen.findByText(/Starred \(1\)/),
).resolves.toBeInTheDocument();
});
+
+ it('should wrap filter in drawer on smaller screens', async () => {
+ mockBreakpoint({ matches: true });
+ const { getByRole } = await renderWrapped();
+ const button = getByRole('button', { name: 'Filters' });
+ expect(getByRole('presentation', { hidden: true })).toBeInTheDocument();
+ fireEvent.click(button);
+ expect(getByRole('presentation')).toBeVisible();
+ });
});
diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx
index 089e61f0fb..963e04e5e2 100644
--- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx
+++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-import React from 'react';
-import { Grid } from '@material-ui/core';
+import {
+ Content,
+ ContentHeader,
+ PageWithHeader,
+ SupportButton,
+ TableColumn,
+ TableProps,
+} from '@backstage/core-components';
+import { configApiRef, useApi } from '@backstage/core-plugin-api';
import {
EntityKindPicker,
EntityLifecyclePicker,
@@ -26,18 +33,15 @@ import {
UserListFilterKind,
UserListPicker,
} from '@backstage/plugin-catalog-react';
+import React from 'react';
import { CatalogTable } from '../CatalogTable';
-
import { EntityRow } from '../CatalogTable/types';
-import CatalogLayout from './CatalogLayout';
import { CreateComponentButton } from '../CreateComponentButton';
import {
- Content,
- ContentHeader,
- SupportButton,
- TableColumn,
- TableProps,
-} from '@backstage/core-components';
+ FilteredEntityLayout,
+ EntityListContainer,
+ FilterContainer,
+} from '../FilteredEntityLayout';
export type CatalogPageProps = {
initiallySelectedFilter?: UserListFilterKind;
@@ -46,39 +50,36 @@ export type CatalogPageProps = {
};
export const CatalogPage = ({
- initiallySelectedFilter = 'owned',
columns,
actions,
-}: CatalogPageProps) => (
-
-
-
-
- All your software catalog entities
-
-
+ initiallySelectedFilter = 'owned',
+}: CatalogPageProps) => {
+ const orgName =
+ useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage';
+
+ return (
+
+
+
+
+ All your software catalog entities
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-);
+
+
+ );
+};
diff --git a/plugins/catalog/src/components/CatalogPage/index.ts b/plugins/catalog/src/components/CatalogPage/index.ts
index 66c3c2f4b3..dc7ed68229 100644
--- a/plugins/catalog/src/components/CatalogPage/index.ts
+++ b/plugins/catalog/src/components/CatalogPage/index.ts
@@ -13,5 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-export { CatalogLayout } from './CatalogLayout';
export { CatalogPage } from './CatalogPage';
diff --git a/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx b/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx
index 482a0c0a3c..ae2fc4b718 100644
--- a/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx
+++ b/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx
@@ -23,9 +23,7 @@ import { useRouteRef } from '@backstage/core-plugin-api';
export const CreateComponentButton = () => {
const createComponentLink = useRouteRef(createComponentRouteRef);
- if (!createComponentLink) return null;
-
- return (
+ return createComponentLink ? (
- );
+ ) : null;
};
diff --git a/plugins/catalog/src/components/CreateComponentButton/index.ts b/plugins/catalog/src/components/CreateComponentButton/index.ts
index e525bef782..edf59d18a6 100644
--- a/plugins/catalog/src/components/CreateComponentButton/index.ts
+++ b/plugins/catalog/src/components/CreateComponentButton/index.ts
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
export { CreateComponentButton } from './CreateComponentButton';
diff --git a/plugins/catalog/src/components/FilteredEntityLayout/EntityListContainer.tsx b/plugins/catalog/src/components/FilteredEntityLayout/EntityListContainer.tsx
new file mode 100644
index 0000000000..a39ef76b87
--- /dev/null
+++ b/plugins/catalog/src/components/FilteredEntityLayout/EntityListContainer.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 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 { Grid } from '@material-ui/core';
+import React, { PropsWithChildren } from 'react';
+
+export const EntityListContainer = ({ children }: PropsWithChildren<{}>) => (
+
+ {children}
+
+);
diff --git a/plugins/catalog/src/components/FilteredEntityLayout/FilterContainer.tsx b/plugins/catalog/src/components/FilteredEntityLayout/FilterContainer.tsx
new file mode 100644
index 0000000000..683dbf8b40
--- /dev/null
+++ b/plugins/catalog/src/components/FilteredEntityLayout/FilterContainer.tsx
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2021 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 { BackstageTheme } from '@backstage/theme';
+import {
+ Box,
+ Button,
+ Drawer,
+ Grid,
+ Typography,
+ useMediaQuery,
+ useTheme,
+} from '@material-ui/core';
+import FilterListIcon from '@material-ui/icons/FilterList';
+import React, { useState, PropsWithChildren } from 'react';
+
+export const FilterContainer = ({ children }: PropsWithChildren<{}>) => {
+ const isMidSizeScreen = useMediaQuery(theme =>
+ theme.breakpoints.down('md'),
+ );
+ const theme = useTheme();
+ const [filterDrawerOpen, setFilterDrawerOpen] = useState(false);
+
+ return isMidSizeScreen ? (
+ <>
+
+ setFilterDrawerOpen(false)}
+ anchor="left"
+ disableAutoFocus
+ keepMounted
+ variant="temporary"
+ >
+
+
+ Filters
+
+ {children}
+
+
+ >
+ ) : (
+
+ {children}
+
+ );
+};
diff --git a/plugins/catalog/src/components/FilteredEntityLayout/FilteredEntityLayout.tsx b/plugins/catalog/src/components/FilteredEntityLayout/FilteredEntityLayout.tsx
new file mode 100644
index 0000000000..628ad9fee6
--- /dev/null
+++ b/plugins/catalog/src/components/FilteredEntityLayout/FilteredEntityLayout.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 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 { Grid } from '@material-ui/core';
+import React, { PropsWithChildren } from 'react';
+
+export const FilteredEntityLayout = ({ children }: PropsWithChildren<{}>) => (
+
+ {children}
+
+);
diff --git a/plugins/catalog/src/components/FilteredEntityLayout/index.ts b/plugins/catalog/src/components/FilteredEntityLayout/index.ts
new file mode 100644
index 0000000000..ded61a4edc
--- /dev/null
+++ b/plugins/catalog/src/components/FilteredEntityLayout/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 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.
+ */
+
+export { FilteredEntityLayout } from './FilteredEntityLayout';
+export { FilterContainer } from './FilterContainer';
+export { EntityListContainer } from './EntityListContainer';
diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts
index 4bef0950a2..215a18f661 100644
--- a/plugins/catalog/src/index.ts
+++ b/plugins/catalog/src/index.ts
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-export * from './components/AboutCard';
export { CatalogClientWrapper } from './CatalogClientWrapper';
-export { CatalogLayout } from './components/CatalogPage';
-export { CatalogResultListItem } from './components/CatalogResultListItem';
+export * from './components/AboutCard';
+export * from './components/CatalogResultListItem';
export { CatalogTable } from './components/CatalogTable';
export type { EntityRow as CatalogTableRow } from './components/CatalogTable';
-export { CreateComponentButton } from './components/CreateComponentButton';
-export { EntityLayout } from './components/EntityLayout';
+export * from './components/CatalogTable/columns';
+export * from './components/CreateComponentButton';
+export * from './components/EntityLayout';
export * from './components/EntityOrphanWarning';
-export { EntityPageLayout } from './components/EntityPageLayout';
+export * from './components/EntityPageLayout';
export * from './components/EntitySwitch';
+export * from './components/FilteredEntityLayout';
export { Router } from './components/Router';
export {
CatalogEntityPage,
@@ -32,8 +33,8 @@ export {
catalogPlugin,
catalogPlugin as plugin,
EntityAboutCard,
- EntityDependsOnComponentsCard,
EntityDependencyOfComponentsCard,
+ EntityDependsOnComponentsCard,
EntityDependsOnResourcesCard,
EntityHasComponentsCard,
EntityHasResourcesCard,
@@ -42,4 +43,3 @@ export {
EntityLinksCard,
EntitySystemDiagramCard,
} from './plugin';
-export * from './components/CatalogTable/columns';