From 9943238ffc17f1070b6e3eec80f0086351641461 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 18 Dec 2020 17:53:03 +0100 Subject: [PATCH] catalog: add EntitySwitch Co-authored-by: blam --- .../EntitySwitch/EntitySwitch.test.tsx | 114 ++++++++++++++++++ .../components/EntitySwitch/EntitySwitch.tsx | 69 +++++++++++ .../src/components/EntitySwitch/conditions.ts | 39 ++++++ .../src/components/EntitySwitch/index.ts | 17 +++ 4 files changed, 239 insertions(+) create mode 100644 plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx create mode 100644 plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx create mode 100644 plugins/catalog/src/components/EntitySwitch/conditions.ts create mode 100644 plugins/catalog/src/components/EntitySwitch/index.ts diff --git a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx new file mode 100644 index 0000000000..292cd37ede --- /dev/null +++ b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx @@ -0,0 +1,114 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { render } from '@testing-library/react'; +import { EntityContext } from '../../hooks/useEntity'; +import { Entity } from '@backstage/catalog-model'; +import { EntitySwitch } from './EntitySwitch'; +import { isKind } from './conditions'; + +describe('EntitySwitch', () => { + it('should switch child when entity switches', () => { + const content = ( + + + + + + ); + + const rendered = render( + + {content} + , + ); + + expect(rendered.queryByText('A')).toBeInTheDocument(); + expect(rendered.queryByText('B')).not.toBeInTheDocument(); + expect(rendered.queryByText('C')).not.toBeInTheDocument(); + + rendered.rerender( + + {content} + , + ); + + expect(rendered.queryByText('A')).not.toBeInTheDocument(); + expect(rendered.queryByText('B')).toBeInTheDocument(); + expect(rendered.queryByText('C')).not.toBeInTheDocument(); + + rendered.rerender( + + {content} + , + ); + + expect(rendered.queryByText('A')).not.toBeInTheDocument(); + expect(rendered.queryByText('B')).not.toBeInTheDocument(); + expect(rendered.queryByText('C')).toBeInTheDocument(); + }); + + it('should switch child when filters switch', () => { + const entityContextValue = { + entity: { kind: 'component' } as Entity, + loading: false, + error: undefined, + }; + + const rendered = render( + + + + + + , + ); + + expect(rendered.queryByText('A')).toBeInTheDocument(); + expect(rendered.queryByText('B')).not.toBeInTheDocument(); + + rendered.rerender( + + + + + + , + ); + + expect(rendered.queryByText('A')).not.toBeInTheDocument(); + expect(rendered.queryByText('B')).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx new file mode 100644 index 0000000000..f36bc29cee --- /dev/null +++ b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { + ReactNode, + PropsWithChildren, + Children, + Fragment, + useMemo, + isValidElement, +} from 'react'; +import { useEntity } from '../../hooks/useEntity'; +import { Entity } from '@backstage/catalog-model'; + +const EntitySwitchCase = (_: { + if?: (entity: Entity) => boolean; + children: ReactNode; +}) => null; + +type SwitchCase = { + if?: (entity: Entity) => boolean; + children: JSX.Element; +}; + +function createSwitchCasesFromChildren(children: ReactNode): SwitchCase[] { + return Children.toArray(children).flatMap(child => { + if (!isValidElement(child)) { + return []; + } + + if (child.type === Fragment) { + return createSwitchCasesFromChildren(child.props.children); + } + + if (child.type !== EntitySwitchCase) { + throw new Error(`Child of EntitySwitch is not an EntitySwitch.Case`); + } + + const { if: condition, children } = child.props; + return [{ if: condition, children }]; + }); +} + +export const EntitySwitch = ({ children }: PropsWithChildren<{}>) => { + const { entity } = useEntity(); + const switchCases = useMemo(() => createSwitchCasesFromChildren(children), [ + children, + ]); + + const matchingCase = switchCases.find(switchCase => + switchCase.if ? switchCase.if(entity) : true, + ); + return matchingCase?.children ?? null; +}; + +EntitySwitch.Case = EntitySwitchCase; diff --git a/plugins/catalog/src/components/EntitySwitch/conditions.ts b/plugins/catalog/src/components/EntitySwitch/conditions.ts new file mode 100644 index 0000000000..8e4da19656 --- /dev/null +++ b/plugins/catalog/src/components/EntitySwitch/conditions.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Entity, ComponentEntity } from '@backstage/catalog-model'; + +function strCmp(a: string | undefined, b: string | undefined): boolean { + return Boolean(a && a?.toLowerCase() === b?.toLowerCase()); +} + +export function isKind(kind: string) { + return (entity: Entity) => strCmp(entity?.kind, kind); +} + +export function isComponentType(type: string) { + return (entity: Entity) => { + if (!strCmp(entity?.kind, 'component')) { + return false; + } + const componentEntity = entity as ComponentEntity; + return strCmp(componentEntity.spec.type, type); + }; +} + +export function isNamespace(namespace: string) { + return (entity: Entity) => strCmp(entity?.metadata?.namespace, namespace); +} diff --git a/plugins/catalog/src/components/EntitySwitch/index.ts b/plugins/catalog/src/components/EntitySwitch/index.ts new file mode 100644 index 0000000000..6549941187 --- /dev/null +++ b/plugins/catalog/src/components/EntitySwitch/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { EntitySwitch } from './EntitySwitch';