catalog: add EntitySwitch

Co-authored-by: blam <ben@blam.sh>
This commit is contained in:
Patrik Oldsberg
2020-12-18 17:53:03 +01:00
parent 9904a9d001
commit 9943238ffc
4 changed files with 239 additions and 0 deletions
@@ -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 = (
<EntitySwitch>
<EntitySwitch.Case if={isKind('component')} children="A" />
<EntitySwitch.Case if={isKind('template')} children="B" />
<EntitySwitch.Case children="C" />
</EntitySwitch>
);
const rendered = render(
<EntityContext.Provider
value={{
entity: { kind: 'component' } as Entity,
loading: false,
error: undefined,
}}
>
{content}
</EntityContext.Provider>,
);
expect(rendered.queryByText('A')).toBeInTheDocument();
expect(rendered.queryByText('B')).not.toBeInTheDocument();
expect(rendered.queryByText('C')).not.toBeInTheDocument();
rendered.rerender(
<EntityContext.Provider
value={{
entity: { kind: 'template' } as Entity,
loading: false,
error: undefined,
}}
>
{content}
</EntityContext.Provider>,
);
expect(rendered.queryByText('A')).not.toBeInTheDocument();
expect(rendered.queryByText('B')).toBeInTheDocument();
expect(rendered.queryByText('C')).not.toBeInTheDocument();
rendered.rerender(
<EntityContext.Provider
value={{
entity: { kind: 'derp' } as Entity,
loading: false,
error: undefined,
}}
>
{content}
</EntityContext.Provider>,
);
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(
<EntityContext.Provider value={entityContextValue}>
<EntitySwitch>
<EntitySwitch.Case if={isKind('component')} children="A" />
<EntitySwitch.Case children="B" />
</EntitySwitch>
</EntityContext.Provider>,
);
expect(rendered.queryByText('A')).toBeInTheDocument();
expect(rendered.queryByText('B')).not.toBeInTheDocument();
rendered.rerender(
<EntityContext.Provider value={entityContextValue}>
<EntitySwitch>
<EntitySwitch.Case if={isKind('template')} children="A" />
<EntitySwitch.Case children="B" />
</EntitySwitch>
</EntityContext.Provider>,
);
expect(rendered.queryByText('A')).not.toBeInTheDocument();
expect(rendered.queryByText('B')).toBeInTheDocument();
});
});
@@ -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;
@@ -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);
}
@@ -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';