diff --git a/packages/app/src/react-app-env.d.ts b/packages/app/src/react-app-env.d.ts index f4145f20db..f3b69cc361 100644 --- a/packages/app/src/react-app-env.d.ts +++ b/packages/app/src/react-app-env.d.ts @@ -13,5 +13,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js index 06345558b4..160ecb3b0c 100644 --- a/packages/core/.storybook/main.js +++ b/packages/core/.storybook/main.js @@ -11,8 +11,8 @@ module.exports = { { loader: require.resolve('ts-loader'), options: { - transpileOnly: true - } + transpileOnly: true, + }, }, ], }); diff --git a/packages/core/src/api/apis/ApiTestRegistry.test.ts b/packages/core/src/api/apis/ApiTestRegistry.test.ts index 434de430f7..6974daa3a7 100644 --- a/packages/core/src/api/apis/ApiTestRegistry.test.ts +++ b/packages/core/src/api/apis/ApiTestRegistry.test.ts @@ -59,13 +59,24 @@ describe('ApiTestRegistry', () => { it('should register factories with dependencies', () => { // 100% coverage + happy typescript = hasOwnProperty + this atrocity - const cDeps = Object.create({ c: cRef }, { a: { enumerable: true, value: aRef } }); + const cDeps = Object.create( + { c: cRef }, + { a: { enumerable: true, value: aRef } }, + ); cDeps.b = bRef; const registry = new ApiTestRegistry(); registry.register({ implements: aRef, deps: {}, factory: () => 3 }); - registry.register({ implements: bRef, deps: { dep: aRef }, factory: ({ dep }) => `hello ${dep}` }); - registry.register({ implements: cRef, deps: cDeps, factory: ({ a, b }) => b.repeat(a) }); + registry.register({ + implements: bRef, + deps: { dep: aRef }, + factory: ({ dep }) => `hello ${dep}`, + }); + registry.register({ + implements: cRef, + deps: cDeps, + factory: ({ a, b }) => b.repeat(a), + }); expect(registry.get(aRef)).toBe(3); expect(registry.get(bRef)).toBe('hello 3'); expect(registry.get(cRef)).toBe('hello 3hello 3hello 3'); @@ -73,17 +84,39 @@ describe('ApiTestRegistry', () => { it('should not allow cyclic dependencies', () => { const registry = new ApiTestRegistry(); - registry.register({ implements: aRef, deps: { b: bRef }, factory: () => 1 }); - registry.register({ implements: bRef, deps: { c: cRef }, factory: () => 'b' }); - registry.register({ implements: cRef, deps: { a: aRef }, factory: () => 'c' }); - expect(() => registry.get(aRef)).toThrow('Circular dependency of api factory for apiRef{a}'); - expect(() => registry.get(bRef)).toThrow('Circular dependency of api factory for apiRef{b}'); - expect(() => registry.get(cRef)).toThrow('Circular dependency of api factory for apiRef{c}'); + registry.register({ + implements: aRef, + deps: { b: bRef }, + factory: () => 1, + }); + registry.register({ + implements: bRef, + deps: { c: cRef }, + factory: () => 'b', + }); + registry.register({ + implements: cRef, + deps: { a: aRef }, + factory: () => 'c', + }); + expect(() => registry.get(aRef)).toThrow( + 'Circular dependency of api factory for apiRef{a}', + ); + expect(() => registry.get(bRef)).toThrow( + 'Circular dependency of api factory for apiRef{b}', + ); + expect(() => registry.get(cRef)).toThrow( + 'Circular dependency of api factory for apiRef{c}', + ); }); it('should throw error if dependency is not available', () => { const registry = new ApiTestRegistry(); - registry.register({ implements: aRef, deps: { b: bRef }, factory: () => 1 }); + registry.register({ + implements: aRef, + deps: { b: bRef }, + factory: () => 1, + }); expect(() => registry.get(aRef)).toThrow( 'No API factory available for dependency apiRef{b} of dependent apiRef{a}', ); diff --git a/packages/core/src/api/apis/ApiTestRegistry.ts b/packages/core/src/api/apis/ApiTestRegistry.ts index c359b5878a..2af2058fbc 100644 --- a/packages/core/src/api/apis/ApiTestRegistry.ts +++ b/packages/core/src/api/apis/ApiTestRegistry.ts @@ -19,8 +19,14 @@ import { TypesToApiRefs, AnyApiRef, ApiHolder, ApiFactory } from './types'; export default class ApiTestRegistry implements ApiHolder { private readonly apis = new Map(); - private factories = new Map>(); - private savedFactories = new Map>(); + private factories = new Map< + AnyApiRef, + ApiFactory + >(); + private savedFactories = new Map< + AnyApiRef, + ApiFactory + >(); get(ref: ApiRef): T | undefined { return this.load(ref); @@ -28,7 +34,10 @@ export default class ApiTestRegistry implements ApiHolder { register(ref: ApiRef, factoryFunc: () => T): ApiTestRegistry; register(factory: ApiFactory): ApiTestRegistry; - register(factory: ApiRef | ApiFactory, factoryFunc?: () => T): ApiTestRegistry { + register( + factory: ApiRef | ApiFactory, + factoryFunc?: () => T, + ): ApiTestRegistry { if (factory instanceof ApiRef) { this.factories.set(factory, { implements: factory, @@ -63,16 +72,25 @@ export default class ApiTestRegistry implements ApiHolder { } if (loading.includes(factory.implements)) { - throw new Error(`Circular dependency of api factory for ${factory.implements}`); + throw new Error( + `Circular dependency of api factory for ${factory.implements}`, + ); } - const deps = this.loadDeps(ref, factory.deps, [...loading, factory.implements]); + const deps = this.loadDeps(ref, factory.deps, [ + ...loading, + factory.implements, + ]); const api = factory.factory(deps); this.apis.set(ref, api); return api as T; } - private loadDeps(dependent: ApiRef, apis: TypesToApiRefs, loading: AnyApiRef[]): T { + private loadDeps( + dependent: ApiRef, + apis: TypesToApiRefs, + loading: AnyApiRef[], + ): T { const impls = {} as T; for (const key in apis) { @@ -81,7 +99,9 @@ export default class ApiTestRegistry implements ApiHolder { const api = this.load(ref, loading); if (!api) { - throw new Error(`No API factory available for dependency ${ref} of dependent ${dependent}`); + throw new Error( + `No API factory available for dependency ${ref} of dependent ${dependent}`, + ); } impls[key] = api; } diff --git a/packages/core/src/components/Status/index.ts b/packages/core/src/components/Status/index.ts index 2f68d7d5bf..35d2ca1c48 100644 --- a/packages/core/src/components/Status/index.ts +++ b/packages/core/src/components/Status/index.ts @@ -14,4 +14,12 @@ * limitations under the License. */ -export { StatusError, StatusFailed, StatusNA, StatusOK, StatusPending, StatusRunning, StatusWarning } from './Status'; +export { + StatusError, + StatusFailed, + StatusNA, + StatusOK, + StatusPending, + StatusRunning, + StatusWarning, +} from './Status'; diff --git a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js index 3ad10cfc30..409338caeb 100644 --- a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js +++ b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js @@ -15,10 +15,24 @@ */ import React, { Fragment } from 'react'; -import { IconButton, List, ListItem, ListItemIcon, ListItemText, Popover } from '@material-ui/core'; +import { + IconButton, + List, + ListItem, + ListItemIcon, + ListItemText, + Popover, +} from '@material-ui/core'; import { default as KebabMenuIcon } from './MenuVertical'; -const ActionItem = ({ label, secondaryLabel, icon, disabled = false, onClick, WrapperComponent = React.Fragment }) => { +const ActionItem = ({ + label, + secondaryLabel, + icon, + disabled = false, + onClick, + WrapperComponent = React.Fragment, +}) => { return ( { onClick={() => setOpen(true)} data-testid="header-action-menu" ref={anchorElRef} - style={{ color: 'white', height: 56, width: 56, marginRight: -4, padding: 0 }} + style={{ + color: 'white', + height: 56, + width: 56, + marginRight: -4, + padding: 0, + }} > diff --git a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js index 97ea36c2ab..1b90cad6f8 100644 --- a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js +++ b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js @@ -27,13 +27,20 @@ describe('', () => { it('can open the menu and click menu items', () => { const onClickFunction = jest.fn(); const rendered = render( - wrapInThemedTestApp(), + wrapInThemedTestApp( + , + ), ); expect(rendered.queryByText('Some label')).not.toBeInTheDocument(); expect(onClickFunction).not.toHaveBeenCalled(); fireEvent.click(rendered.getByTestId('header-action-menu')); expect(onClickFunction).not.toHaveBeenCalled(); - expect(rendered.getByTestId('header-action-item')).not.toHaveAttribute('aria-disabled', 'true'); + expect(rendered.getByTestId('header-action-item')).not.toHaveAttribute( + 'aria-disabled', + 'true', + ); fireEvent.click(rendered.queryByText('Some label')); expect(onClickFunction).toHaveBeenCalled(); // We do not expect the dropdown to disappear after click @@ -42,11 +49,18 @@ describe('', () => { it('Disabled', async () => { const rendered = render( - wrapInThemedTestApp(), + wrapInThemedTestApp( + , + ), ); fireEvent.click(rendered.getByTestId('header-action-menu')); - expect(rendered.getByTestId('header-action-item')).toHaveAttribute('aria-disabled', 'true'); + expect(rendered.getByTestId('header-action-item')).toHaveAttribute( + 'aria-disabled', + 'true', + ); }); it('Test wrapper, and secondary label', () => { @@ -58,7 +72,9 @@ describe('', () => { { label: 'Some label', secondaryLabel: 'Secondary label', - WrapperComponent: ({ children }) => , + WrapperComponent: ({ children }) => ( + + ), }, ]} />, @@ -75,7 +91,11 @@ describe('', () => { }); it('should close when hitting escape', async () => { - const rendered = render(wrapInThemedTestApp()); + const rendered = render( + wrapInThemedTestApp( + , + ), + ); expect(rendered.container.getAttribute('aria-hidden')).toBeNull(); fireEvent.click(rendered.getByTestId('header-action-menu')); diff --git a/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js b/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js index 1c47d50794..2d9e97bb51 100644 --- a/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js +++ b/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js @@ -31,12 +31,18 @@ describe('', () => { }); it('should have value', () => { - const rendered = render(wrapInThemedTestApp()); + const rendered = render( + wrapInThemedTestApp(), + ); expect(rendered.getByText('Value')).toBeInTheDocument(); }); it('should have a link', () => { - const rendered = render(wrapInThemedTestApp()); + const rendered = render( + wrapInThemedTestApp( + , + ), + ); const anchor = rendered.container.querySelector('a'); expect(rendered.getByText('Value')).toBeInTheDocument(); expect(anchor.href).toBe('http://localhost/test'); diff --git a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js new file mode 100644 index 0000000000..d13f0bd07a --- /dev/null +++ b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js @@ -0,0 +1,81 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Tooltip, Link, withStyles } from '@material-ui/core'; + +import { StatusError } from '../../components/Status'; +import HeaderLabel from './HeaderLabel'; + +const style = theme => ({ + notVerified: { + color: theme.palette.status.error, + borderRadius: 4, + padding: '3px 6px', + fontSize: '8pt', + opacity: 0.8, + fontWeight: 'bold', + position: 'relative', + top: -4, + backgroundColor: 'pink', + float: 'right', + marginLeft: 14, + }, + label: { float: 'left' }, +}); + +class OwnerHeaderLabel extends Component { + static propTypes = { + owner: PropTypes.object.isRequired, + }; + + render() { + const { owner, classes } = this.props; + const isBadSquad = owner.type !== 'squad'; + + const notVerified = isBadSquad && ( + + + Squad not + verified! + + + ); + const label = ( + + + {owner.name} + + + ); + return ( + <> + + {notVerified} + + ); + } +} + +export default withStyles(style)(OwnerHeaderLabel); diff --git a/packages/core/src/testUtils/Keyboard.test.js b/packages/core/src/testUtils/Keyboard.test.js index 7615214744..31e0885dc1 100644 --- a/packages/core/src/testUtils/Keyboard.test.js +++ b/packages/core/src/testUtils/Keyboard.test.js @@ -33,8 +33,11 @@ describe('testUtils.Keyboard', () => { const rendered = render(
typed1.push(value)} /> - {/* eslint-disable-next-line jsx-a11y/no-autofocus */} - typed2.push(value)} autoFocus /> + typed2.push(value)} + /* eslint-disable-next-line jsx-a11y/no-autofocus */ + autoFocus + /> typed3.push(value)} />
, ); @@ -64,9 +67,18 @@ describe('testUtils.Keyboard', () => { const rendered = render(
- typed1.push(value)} /> - typed2.push(value)} /> - typed3.push(value)} /> + typed1.push(value)} + /> + typed2.push(value)} + /> + typed3.push(value)} + />
, ); diff --git a/packages/core/src/testUtils/mockBreakpoint.ts b/packages/core/src/testUtils/mockBreakpoint.ts index d92862cbc0..598ef654e8 100644 --- a/packages/core/src/testUtils/mockBreakpoint.ts +++ b/packages/core/src/testUtils/mockBreakpoint.ts @@ -29,7 +29,9 @@ const queryToBreakpoint = { function toBreakpoint(query: string) { const breakpoint = queryToBreakpoint[query]; if (!breakpoint) { - throw new Error(`received unknown media query in breakpoint mock: '${query}'`); + throw new Error( + `received unknown media query in breakpoint mock: '${query}'`, + ); } return breakpoint; }