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 -
- -
+ + +
- -
-
+ + + + + +
-
+ ); }; ``` @@ -137,19 +143,27 @@ export const EntitySecurityTierPicker = () => { Now we can add the component to `CustomCatalogPage`: ```diff -export const CustomCatalogPage = () => { +export const CustomCatalogPage = ({ + columns, + actions, + initiallySelectedFilter = 'owned', +}: CatalogPageProps) => { return ( ... - -
+ + +
- -
+ + + + + + ... }; ``` diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index e915e004ee..ccafa4cfa7 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -1202,6 +1202,16 @@ export const Page: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "PageWithHeaderProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PageWithHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const PageWithHeader: ({ + themeId, + children, + ...props +}: PropsWithChildren) => JSX.Element; + // Warning: (ae-missing-release-tag) "Progress" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/components/SupportButton/SupportButton.tsx b/packages/core-components/src/components/SupportButton/SupportButton.tsx index 2ffef89878..3e719acb95 100644 --- a/packages/core-components/src/components/SupportButton/SupportButton.tsx +++ b/packages/core-components/src/components/SupportButton/SupportButton.tsx @@ -28,7 +28,7 @@ import { makeStyles, Popover, } from '@material-ui/core'; -import React, { Fragment, MouseEventHandler, useState } from 'react'; +import React, { MouseEventHandler, useState } from 'react'; import { SupportItem, SupportItemLink, useSupportConfig } from '../../hooks'; import { Link } from '../Link'; @@ -94,17 +94,17 @@ export const SupportButton = ({ title, children }: SupportButtonProps) => { }; return ( - - + <> + + + { - + ); }; diff --git a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx index 1e2ad0b5d4..62cb80cffa 100644 --- a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx +++ b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx @@ -18,7 +18,7 @@ * TODO favoriteable capability */ -import React, { ComponentType, Fragment, PropsWithChildren } from 'react'; +import React, { ComponentType, PropsWithChildren } from 'react'; import { Typography, makeStyles } from '@material-ui/core'; import { Helmet } from 'react-helmet'; @@ -57,15 +57,15 @@ const useStyles = (props: ContentHeaderProps) => }, })); -type DefaultTitleProps = { +type ContentHeaderTitleProps = { title?: string; - className: string; + className?: string; }; -const DefaultTitle = ({ +const ContentHeaderTitle = ({ title = 'Unknown page', className, -}: DefaultTitleProps) => ( +}: ContentHeaderTitleProps) => ( ) : ( - + ); return ( - + <>
@@ -111,6 +111,6 @@ export const ContentHeader = ({
{children}
-
+ ); }; 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';