app,plugin/login: remove login page for now

This commit is contained in:
Patrik Oldsberg
2020-03-05 10:52:27 +01:00
parent cb4d1e82b7
commit 5377ca9d56
19 changed files with 5 additions and 358 deletions
-1
View File
@@ -8,7 +8,6 @@
"@material-ui/icons": "^4.9.1",
"@spotify-backstage/core": "1.0.0",
"@spotify-backstage/plugin-home-page": "0.0.0",
"@spotify-backstage/plugin-login": "0.0.0",
"@react-workspaces/react-scripts": "^3.3.0-alpha-08",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
+4 -20
View File
@@ -1,14 +1,10 @@
import { BackstageTheme, createApp, InfoCard } from '@spotify-backstage/core';
//import PageHeader from './components/PageHeader';
import { LoginComponent } from '@spotify-backstage/plugin-login';
import { BackstageTheme, createApp } from '@spotify-backstage/core';
import { CssBaseline, makeStyles, ThemeProvider } from '@material-ui/core';
import React, { FC } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import * as plugins from './plugins';
import SideBar from './components/SideBar';
import entities from './entities';
import { LoginBarrier } from './login/LoginBarrier';
import { MockCurrentUser } from './login/MockCurrentUser';
const useStyles = makeStyles(theme => ({
'@global': {
@@ -36,16 +32,6 @@ const useStyles = makeStyles(theme => ({
},
}));
const currentUser = new MockCurrentUser();
const Login: FC<{}> = () => {
return (
<InfoCard title="Login Page">
<LoginComponent onLogin={username => currentUser.login(username)} />
</InfoCard>
);
};
const AppShell: FC<{}> = ({ children }) => {
const classes = useStyles();
@@ -69,11 +55,9 @@ const App: FC<{}> = () => {
<CssBaseline>
<ThemeProvider theme={BackstageTheme}>
<Router>
<LoginBarrier fallback={Login} state$={currentUser.state}>
<AppShell>
<AppComponent />
</AppShell>
</LoginBarrier>
<AppShell>
<AppComponent />
</AppShell>
</Router>
</ThemeProvider>
</CssBaseline>
@@ -1,57 +0,0 @@
import { render, wait, act } from '@testing-library/react';
import React from 'react';
import Observable from 'zen-observable';
import { LoginBarrier } from './LoginBarrier';
import { LoginState } from './types';
describe('LoginBarrier', () => {
it('passes through when logged in', async () => {
const state$ = Observable.of<LoginState>({
type: 'LOGGED_IN',
user: 'apa',
});
const rendered = render(
<LoginBarrier
state$={state$}
fallback={({ state }) => <div>{state.type}</div>}
>
Logged in!
</LoginBarrier>,
);
await wait(() => rendered.getByText('Logged in!'));
});
it('goes to fallback when not logged in', async () => {
const state$ = Observable.of<LoginState>({ type: 'LOGGED_OUT' });
const rendered = render(
<LoginBarrier
state$={state$}
fallback={({ state }) => <div>{state.type}</div>}
>
Logged in!
</LoginBarrier>,
);
await wait(() => rendered.getByText('LOGGED_OUT'));
});
it('transitions between states', async () => {
let subscriber: ZenObservable.SubscriptionObserver<LoginState> | undefined;
const state$ = new Observable<LoginState>(s => {
subscriber = s;
});
const rendered = render(
<LoginBarrier
state$={state$}
fallback={({ state }) => <div>{state.type}</div>}
>
Logged in!
</LoginBarrier>,
);
await wait(() => rendered.getByText('LOGGED_OUT'));
// @ts-ignore: Object is possibly 'undefined'
act(() => subscriber.next({ type: 'LOGGED_IN', user: 'apa' }));
await wait(() => rendered.getByText('Logged in!'));
});
});
@@ -1,22 +0,0 @@
import React, { FC } from 'react';
import Observable from 'zen-observable';
import { useObservable } from 'react-use';
import { LoginState } from './types';
type LoginBarrierProps = {
fallback: React.ComponentType<{ state: LoginState }>;
state$: Observable<LoginState>;
};
export const LoginBarrier: FC<LoginBarrierProps> = ({
fallback: Fallback,
state$,
children,
}) => {
const state = useObservable(state$, { type: 'LOGGED_OUT' });
if (state.type === 'LOGGED_IN') {
return <>{children}</>;
} else {
return <Fallback state={state} />;
}
};
@@ -1,38 +0,0 @@
import { MockCurrentUser } from './MockCurrentUser';
import { wait } from '@testing-library/react';
describe('MockCurrentUser', () => {
it('notifies immediately on subscribe', async () => {
const next = jest.fn();
const current = new MockCurrentUser();
current.state.subscribe(next);
await wait();
expect(next).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ type: 'LOGGED_OUT' }),
);
});
it('logs in and out', async () => {
const next = jest.fn();
const current = new MockCurrentUser();
current.state.subscribe(next);
await wait();
expect(next).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ type: 'LOGGED_OUT' }),
);
current.login('apa');
await wait();
expect(next).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ type: 'LOGGED_IN', user: 'apa' }),
);
current.logout();
await wait();
expect(next).toHaveBeenNthCalledWith(
3,
expect.objectContaining({ type: 'LOGGED_OUT' }),
);
});
});
@@ -1,67 +0,0 @@
import Observable from 'zen-observable';
import { LoginState } from './types';
const LOCAL_STORAGE_KEY = 'mock_current_user';
export class MockCurrentUser {
private listeners: ZenObservable.SubscriptionObserver<LoginState>[];
private currentUser: string | undefined;
constructor() {
this.listeners = [];
this.currentUser = undefined;
this.loadPrevious();
}
login(user: string) {
this.currentUser = user;
this.saveCurrent();
this.notify();
}
logout() {
this.currentUser = undefined;
this.saveCurrent();
this.notify();
}
get state(): Observable<LoginState> {
return new Observable(subscriber => {
this.listeners.push(subscriber);
subscriber.next(this.getState());
return () => {
this.listeners = this.listeners.filter(x => x !== subscriber);
};
});
}
private loadPrevious() {
const user = localStorage.getItem(LOCAL_STORAGE_KEY);
if (user) {
try {
this.currentUser = JSON.parse(user);
} catch (e) {
localStorage.removeItem(LOCAL_STORAGE_KEY);
}
}
}
private saveCurrent() {
if (!this.currentUser) {
localStorage.removeItem(LOCAL_STORAGE_KEY);
} else {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.currentUser));
}
}
private notify() {
const state = this.getState();
this.listeners.forEach(listener => listener.next(state));
}
private getState(): LoginState {
return this.currentUser
? { type: 'LOGGED_IN', user: this.currentUser }
: { type: 'LOGGED_OUT' };
}
}
-11
View File
@@ -1,11 +0,0 @@
export type LoggedOutState = { type: 'LOGGED_OUT' };
export type LoggingInState = { type: 'LOGGING_IN' };
export type LoggedInState = { type: 'LOGGED_IN'; user: string };
export type LoggingOutState = { type: 'LOGGING_OUT' };
export type LoginFailedState = { type: 'LOGIN_FAILED'; error: Error };
export type LoginState =
| LoggedOutState
| LoggingInState
| LoggedInState
| LoggingOutState
| LoginFailedState;
@@ -1 +0,0 @@
Welcome to your login-page plugin!
@@ -1,4 +0,0 @@
module.exports = {
...require('@spotify/web-scripts/config/jest.config.js'),
setupFilesAfterEnv: ['../jest.setup.ts'],
};
@@ -1 +0,0 @@
import '@testing-library/jest-dom/extend-expect';
@@ -1,27 +0,0 @@
{
"name": "@spotify-backstage/plugin-login",
"version": "0.0.0",
"main": "dist/cjs",
"devDependencies": {
"@spotify-backstage/cli": "^1.2.0",
"@spotify-backstage/core": "1.0.0",
"@spotify/web-scripts": "^6.0.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^24.0.0",
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1"
},
"scripts": {
"build": "tsc --outDir dist/cjs --noEmit false --module CommonJS",
"lint": "web-scripts lint",
"test": "web-scripts test"
},
"license": "Apache-2.0"
}
@@ -1,11 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import LoginComponent from './LoginComponent';
describe('LoginComponent', () => {
it('should render', () => {
const onLogin = jest.fn();
const rendered = render(<LoginComponent onLogin={onLogin} />);
expect(rendered.getByText('Login')).toBeInTheDocument();
});
});
@@ -1,67 +0,0 @@
import React, { FC, useState } from 'react';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import { Typography } from '@material-ui/core';
type Props = {
onLogin: (username: string) => Promise<void> | void;
};
const LoginComponent: FC<Props> = ({ onLogin }) => {
const [error, setError] = useState('');
const [username, setUsername] = useState('');
const onUsernameChanged = async (e: React.ChangeEvent<HTMLInputElement>) => {
setUsername(e.target.value);
};
const onSubmit = async () => {
if (!username) {
setError('You have to supply a username');
}
setError('');
try {
await onLogin(username);
} catch (e) {
setError(`Login failed: ${e.message}`);
}
};
return (
<form onSubmit={onSubmit} autoComplete="off">
<Grid container spacing={8} alignItems="flex-end">
<Grid item md={true} sm={true} xs={true}>
<TextField
id="username"
label="Username"
type="email"
autoFocus
required
value={username}
onChange={onUsernameChanged}
/>
</Grid>
</Grid>
<Grid
container
justify="flex-start"
style={{ marginTop: '24px', marginBottom: '24px' }}
>
<Button
type="submit"
variant="contained"
color="primary"
onClick={onSubmit}
disabled={!username}
>
Login
</Button>
</Grid>
<Typography>{error || 'Use your github username'}</Typography>
</form>
);
};
export default LoginComponent;
@@ -1 +0,0 @@
export { default } from './LoginComponent';
@@ -1,2 +0,0 @@
export { default } from './plugin';
export { default as LoginComponent } from './components/LoginComponent';
@@ -1,7 +0,0 @@
import plugin from './plugin';
describe('login', () => {
it('should export plugin', () => {
expect(plugin).toBeDefined();
});
});
@@ -1,5 +0,0 @@
import { createPlugin } from '@spotify-backstage/core';
export default createPlugin({
id: 'login',
});
@@ -1,4 +0,0 @@
{
"extends": "../../../tsconfig.json",
"include": ["src"]
}
+1 -12
View File
@@ -2308,17 +2308,6 @@
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/lab@4.0.0-alpha.45":
version "4.0.0-alpha.45"
resolved "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.45.tgz#6e1abbdd6e44b9ef7b3eff8ef892a3da5dc52f10"
integrity sha512-zT6kUU87SHsPukiu3tlWg8V6o0tGS38c1b/xst/kPqX6eLbfqrROyxhHn1A8ZtHmqga1AKQdv/1llQoG80Afww==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/utils" "^4.7.1"
clsx "^1.0.4"
prop-types "^15.7.2"
react-is "^16.8.0"
"@material-ui/styles@^4.9.0":
version "4.9.0"
resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.0.tgz#10c31859f6868cfa9d3adf6b6c3e32c9d676bc76"
@@ -5068,7 +5057,7 @@ clone@^1.0.2:
resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
clsx@^1.0.2, clsx@^1.0.4:
clsx@^1.0.2:
version "1.1.0"
resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702"
integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA==