mirror of
https://github.com/SomboChea/ui
synced 2026-01-18 09:06:14 +07:00
Refactor(#209): Converted App component from class to func
This commit is contained in:
committed by
GitHub
parent
0a48906fc8
commit
0d9232a92c
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
|
||||
import { mount } from '../utils/test-enzyme';
|
||||
import { render, waitForElement, fireEvent } from '../utils/test-react-testing-library';
|
||||
import storage from '../utils/storage';
|
||||
// eslint-disable-next-line jest/no-mocks-import
|
||||
import { generateTokenWithTimeRange } from '../../jest/unit/components/__mocks__/token';
|
||||
|
||||
import App from './App';
|
||||
import { AppProps } from './AppContext';
|
||||
|
||||
jest.mock('../utils/storage', () => {
|
||||
class LocalStorageMock {
|
||||
@@ -36,63 +34,71 @@ jest.mock('../utils/api', () => ({
|
||||
request: require('../../jest/unit/components/__mocks__/api').default.request,
|
||||
}));
|
||||
|
||||
describe('App', () => {
|
||||
let wrapper: ReactWrapper<{}, AppProps, App>;
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
describe('<App />', () => {
|
||||
test('should display the Loading component at the beginning ', () => {
|
||||
const { container, queryByTestId } = render(<App />);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(<App />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(queryByTestId('loading')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('toggleLoginModal: should toggle the value in state', () => {
|
||||
const { handleToggleLoginModal } = wrapper.instance();
|
||||
expect(wrapper.state().showLoginModal).toBeFalsy();
|
||||
handleToggleLoginModal();
|
||||
expect(wrapper.state('showLoginModal')).toBeTruthy();
|
||||
expect(wrapper.state('error')).toEqual(undefined);
|
||||
test('should display the Header component ', async () => {
|
||||
const { container, queryByTestId } = render(<App />);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(queryByTestId('loading')).toBeTruthy();
|
||||
|
||||
// wait for the Header component appearance and return the element
|
||||
const headerElement = await waitForElement(() => queryByTestId('header'));
|
||||
expect(headerElement).toBeTruthy();
|
||||
});
|
||||
|
||||
test('handleLogout - logouts the user and clear localstorage', async () => {
|
||||
storage.setItem('username', 'verdaccio');
|
||||
storage.setItem('token', generateTokenWithTimeRange(24));
|
||||
|
||||
const { queryByTestId } = render(<App />);
|
||||
|
||||
// wait for the Account's circle element component appearance and return the element
|
||||
const accountCircleElement = await waitForElement(() => queryByTestId('header--menu-accountcircle'));
|
||||
expect(accountCircleElement).toBeTruthy();
|
||||
|
||||
if (accountCircleElement) {
|
||||
fireEvent.click(accountCircleElement);
|
||||
|
||||
// wait for the Button's logout element component appearance and return the element
|
||||
const buttonLogoutElement = await waitForElement(() => queryByTestId('header--button-logout'));
|
||||
expect(buttonLogoutElement).toBeTruthy();
|
||||
|
||||
if (buttonLogoutElement) {
|
||||
fireEvent.click(buttonLogoutElement);
|
||||
|
||||
expect(queryByTestId('greetings-label')).toBeFalsy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
|
||||
storage.setItem('username', 'verdaccio');
|
||||
storage.setItem('token', generateTokenWithTimeRange(24));
|
||||
const { isUserAlreadyLoggedIn } = wrapper.instance();
|
||||
|
||||
isUserAlreadyLoggedIn();
|
||||
const { queryByTestId, queryAllByText } = render(<App />);
|
||||
|
||||
expect(wrapper.state('user').username).toEqual('verdaccio');
|
||||
});
|
||||
// wait for the Account's circle element component appearance and return the element
|
||||
const accountCircleElement = await waitForElement(() => queryByTestId('header--menu-accountcircle'));
|
||||
expect(accountCircleElement).toBeTruthy();
|
||||
|
||||
test('handleLogout - logouts the user and clear localstorage', async () => {
|
||||
const { handleLogout } = wrapper.instance();
|
||||
storage.setItem('username', 'verdaccio');
|
||||
storage.setItem('token', 'xxxx.TOKEN.xxxx');
|
||||
if (accountCircleElement) {
|
||||
fireEvent.click(accountCircleElement);
|
||||
|
||||
await handleLogout();
|
||||
expect(wrapper.state('user')).toEqual({});
|
||||
expect(wrapper.state('isUserLoggedIn')).toBeFalsy();
|
||||
});
|
||||
// wait for the Greeting's label element component appearance and return the element
|
||||
const greetingsLabelElement = await waitForElement(() => queryByTestId('greetings-label'));
|
||||
expect(greetingsLabelElement).toBeTruthy();
|
||||
|
||||
test('handleDoLogin - login the user successfully', async () => {
|
||||
const { handleDoLogin } = wrapper.instance();
|
||||
await handleDoLogin('sam', '1234');
|
||||
const result = {
|
||||
username: 'sam',
|
||||
};
|
||||
expect(wrapper.state('isUserLoggedIn')).toBeTruthy();
|
||||
expect(wrapper.state('showLoginModal')).toBeFalsy();
|
||||
expect(storage.getItem('username')).toEqual('sam');
|
||||
expect(storage.getItem('token')).toEqual('TEST_TOKEN');
|
||||
expect(wrapper.state('user')).toEqual(result);
|
||||
});
|
||||
|
||||
test('handleDoLogin - authentication failure', async () => {
|
||||
const { handleDoLogin } = wrapper.instance();
|
||||
await handleDoLogin('sam', '12345');
|
||||
const result = {
|
||||
description: 'bad username/password, access denied',
|
||||
title: 'Unable to login',
|
||||
type: 'error',
|
||||
};
|
||||
expect(wrapper.state('user')).toEqual({});
|
||||
expect(wrapper.state('error')).toEqual(result);
|
||||
if (greetingsLabelElement) {
|
||||
expect(queryAllByText('verdaccio')).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
234
src/App/App.tsx
234
src/App/App.tsx
@@ -1,184 +1,106 @@
|
||||
import React, { Component, ReactElement } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import isNil from 'lodash/isNil';
|
||||
import { Router } from 'react-router-dom';
|
||||
|
||||
import storage from '../utils/storage';
|
||||
import { makeLogin, isTokenExpire } from '../utils/login';
|
||||
import Loading from '../components/Loading';
|
||||
import LoginModal from '../components/Login';
|
||||
import Header from '../components/Header';
|
||||
import { Container, Content } from '../components/Layout';
|
||||
import { isTokenExpire } from '../utils/login';
|
||||
import API from '../utils/api';
|
||||
import Header from '../components/Header';
|
||||
import Footer from '../components/Footer';
|
||||
import Box from '../muiComponents/Box';
|
||||
import Loading from '../components/Loading';
|
||||
import StyleBaseline from '../design-tokens/StyleBaseline';
|
||||
import { breakpoints } from '../utils/styles/media';
|
||||
|
||||
import AppRoute from './AppRoute';
|
||||
import { AppProps, AppContextProvider } from './AppContext';
|
||||
import AppContextProvider from './AppContextProvider';
|
||||
import AppRoute, { history } from './AppRoute';
|
||||
|
||||
export default class App extends Component<{}, AppProps> {
|
||||
public state: AppProps = {
|
||||
logoUrl: window.VERDACCIO_LOGO,
|
||||
user: {},
|
||||
scope: window.VERDACCIO_SCOPE || '',
|
||||
showLoginModal: false,
|
||||
isUserLoggedIn: false,
|
||||
packages: [],
|
||||
isLoading: true,
|
||||
const StyledBoxContent = styled(Box)({
|
||||
padding: 15,
|
||||
[`@media screen and (min-width: ${breakpoints.container}px)`]: {
|
||||
maxWidth: breakpoints.container,
|
||||
width: '100%',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
/* eslint-disable react/jsx-max-depth */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
const App: React.FC = () => {
|
||||
const [user, setUser] = useState();
|
||||
const [packages, setPackages] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
/**
|
||||
* Logouts user
|
||||
* Required by: <Header />
|
||||
*/
|
||||
const logout = () => {
|
||||
storage.removeItem('username');
|
||||
storage.removeItem('token');
|
||||
setUser(undefined);
|
||||
};
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.isUserAlreadyLoggedIn();
|
||||
this.loadOnHandler();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
public componentDidUpdate(_: AppProps, prevState: AppProps): void {
|
||||
const { isUserLoggedIn } = this.state;
|
||||
if (prevState.isUserLoggedIn !== isUserLoggedIn) {
|
||||
this.loadOnHandler();
|
||||
}
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<HTMLDivElement> {
|
||||
const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state;
|
||||
|
||||
const context = { isUserLoggedIn, packages, logoUrl, user, scope };
|
||||
|
||||
return (
|
||||
<Container isLoading={isLoading}>
|
||||
{isLoading ? <Loading /> : <AppContextProvider value={context}>{this.renderContent()}</AppContextProvider>}
|
||||
{this.renderLoginModal()}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
public isUserAlreadyLoggedIn = () => {
|
||||
const checkUserAlreadyLoggedIn = () => {
|
||||
// checks for token validity
|
||||
const token = storage.getItem('token');
|
||||
const username: string = storage.getItem('username') as string;
|
||||
const username = storage.getItem('username');
|
||||
|
||||
if (isTokenExpire(token) || isNil(username)) {
|
||||
this.handleLogout();
|
||||
} else {
|
||||
this.setState({
|
||||
user: { username },
|
||||
isUserLoggedIn: true,
|
||||
});
|
||||
logout();
|
||||
return;
|
||||
}
|
||||
|
||||
setUser({ username });
|
||||
};
|
||||
|
||||
public loadOnHandler = async () => {
|
||||
const loadOnHandler = async () => {
|
||||
try {
|
||||
const packages = await API.request<any[]>('packages', 'GET');
|
||||
// @ts-ignore: FIX THIS TYPE: Type 'any[]' is not assignable to type '[]'
|
||||
this.setState({
|
||||
packages,
|
||||
isLoading: false,
|
||||
});
|
||||
const packages = await API.request('packages', 'GET');
|
||||
// FIXME add correct type for package
|
||||
setPackages(packages as never[]);
|
||||
} catch (error) {
|
||||
// FIXME: add dialog
|
||||
console.error({
|
||||
title: 'Warning',
|
||||
message: `Unable to load package list: ${error.message}`,
|
||||
});
|
||||
this.setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
public setLoading = (isLoading: boolean) =>
|
||||
this.setState({
|
||||
isLoading,
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggles the login modal
|
||||
* Required by: <LoginModal /> <Header />
|
||||
*/
|
||||
public handleToggleLoginModal = () => {
|
||||
this.setState(prevState => ({
|
||||
showLoginModal: !prevState.showLoginModal,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* handles login
|
||||
* Required by: <Header />
|
||||
*/
|
||||
public handleDoLogin = async (usernameValue: string, passwordValue: string) => {
|
||||
const { username, token, error } = await makeLogin(usernameValue, passwordValue);
|
||||
|
||||
if (username && token) {
|
||||
storage.setItem('username', username);
|
||||
storage.setItem('token', token);
|
||||
this.setLoggedUser(username);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
this.setState({
|
||||
user: {},
|
||||
error,
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
public setLoggedUser = (username: string) => {
|
||||
this.setState({
|
||||
user: {
|
||||
username,
|
||||
},
|
||||
isUserLoggedIn: true, // close login modal after successful login
|
||||
showLoginModal: false, // set isUserLoggedIn to true
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
checkUserAlreadyLoggedIn();
|
||||
loadOnHandler();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Logouts user
|
||||
* Required by: <Header />
|
||||
*/
|
||||
public handleLogout = () => {
|
||||
storage.removeItem('username');
|
||||
storage.removeItem('token');
|
||||
this.setState({
|
||||
user: {},
|
||||
isUserLoggedIn: false,
|
||||
});
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<StyleBaseline />
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<>
|
||||
<Router history={history}>
|
||||
<AppContextProvider packages={packages} user={user}>
|
||||
<Header />
|
||||
<StyledBoxContent flexGrow={1}>
|
||||
<AppRoute />
|
||||
</StyledBoxContent>
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
<Footer />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
public renderLoginModal = (): ReactElement<HTMLElement> => {
|
||||
const { error, showLoginModal } = this.state;
|
||||
return (
|
||||
<LoginModal
|
||||
error={error}
|
||||
onCancel={this.handleToggleLoginModal}
|
||||
onSubmit={this.handleDoLogin}
|
||||
visibility={showLoginModal}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
public renderContent = (): ReactElement<HTMLElement> => {
|
||||
return (
|
||||
<>
|
||||
<Content>
|
||||
<AppRoute>{this.renderHeader()}</AppRoute>
|
||||
</Content>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
public renderHeader = (): ReactElement<HTMLElement> => {
|
||||
const {
|
||||
logoUrl,
|
||||
user: { username },
|
||||
scope,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Header
|
||||
logo={logoUrl}
|
||||
onLogout={this.handleLogout}
|
||||
onToggleLoginModal={this.handleToggleLoginModal}
|
||||
scope={scope}
|
||||
username={username}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
export default App;
|
||||
|
||||
23
src/App/AppContext.ts
Normal file
23
src/App/AppContext.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { FormError } from '../components/Login/Login';
|
||||
|
||||
export interface AppProps {
|
||||
error?: FormError;
|
||||
user?: User;
|
||||
scope: string;
|
||||
packages: any[];
|
||||
}
|
||||
|
||||
export interface User {
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface AppContextProps extends AppProps {
|
||||
setUser: (user?: User) => void;
|
||||
setError: (error?: FormError) => void;
|
||||
}
|
||||
|
||||
const AppContext = createContext<undefined | AppContextProps>(undefined);
|
||||
|
||||
export default AppContext;
|
||||
@@ -1,20 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { FormError } from '../components/Login/Login';
|
||||
|
||||
export interface AppProps {
|
||||
error?: FormError;
|
||||
logoUrl: string;
|
||||
user: {
|
||||
username?: string;
|
||||
};
|
||||
scope: string;
|
||||
showLoginModal: boolean;
|
||||
isUserLoggedIn: boolean;
|
||||
packages: [];
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export const AppContext = createContext<Partial<AppProps>>({});
|
||||
export const AppContextProvider = AppContext.Provider;
|
||||
export const AppContextConsumer = AppContext.Consumer;
|
||||
61
src/App/AppContextProvider.tsx
Normal file
61
src/App/AppContextProvider.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { FormError } from '../components/Login/Login';
|
||||
|
||||
import AppContext, { AppProps, User } from './AppContext';
|
||||
|
||||
interface Props {
|
||||
packages: any[];
|
||||
user?: User;
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
const AppContextProvider: React.FC<Props> = ({ children, packages, user }) => {
|
||||
const [state, setState] = useState<AppProps>({
|
||||
scope: window.VERDACCIO_SCOPE || '',
|
||||
packages,
|
||||
user,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setState({
|
||||
...state,
|
||||
user,
|
||||
});
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
setState({
|
||||
...state,
|
||||
packages,
|
||||
});
|
||||
}, [packages]);
|
||||
|
||||
const setUser = (user?: User) => {
|
||||
setState({
|
||||
...state,
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
const setError = (error?: FormError) => {
|
||||
setState({
|
||||
...state,
|
||||
error,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AppContext.Provider
|
||||
value={{
|
||||
...state,
|
||||
setUser,
|
||||
setError,
|
||||
}}>
|
||||
{children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppContextProvider;
|
||||
@@ -4,7 +4,7 @@ import { createBrowserHistory } from 'history';
|
||||
|
||||
import Loading from '../components/Loading';
|
||||
|
||||
import { AppContext } from './AppContext';
|
||||
import AppContext from './AppContext';
|
||||
|
||||
const NotFound = lazy(() => import('../components/NotFound'));
|
||||
const VersionContextProvider = lazy(() => import('../pages/Version/VersionContextProvider'));
|
||||
@@ -19,19 +19,25 @@ enum Route {
|
||||
PACKAGE_VERSION = '/-/web/detail/:package/v/:version',
|
||||
}
|
||||
|
||||
const history = createBrowserHistory({
|
||||
export const history = createBrowserHistory({
|
||||
basename: window.__VERDACCIO_BASENAME_UI_OPTIONS && window.__VERDACCIO_BASENAME_UI_OPTIONS.url_prefix,
|
||||
});
|
||||
|
||||
/* eslint react/jsx-max-depth: 0 */
|
||||
const AppRoute: React.FC = ({ children }) => {
|
||||
const AppRoute: React.FC = () => {
|
||||
const appContext = useContext(AppContext);
|
||||
const { isUserLoggedIn, packages } = appContext;
|
||||
|
||||
if (!appContext) {
|
||||
throw Error('The app Context was not correct used');
|
||||
}
|
||||
|
||||
const { user, packages } = appContext;
|
||||
|
||||
const isUserLoggedIn = user && user.username;
|
||||
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Suspense fallback={<Loading />}>
|
||||
{children}
|
||||
<Switch>
|
||||
<ReactRouterDomRoute exact={true} path={Route.ROOT}>
|
||||
<HomePage isUserLoggedIn={!!isUserLoggedIn} packages={packages || []} />
|
||||
|
||||
179
src/App/__snapshots__/App.test.tsx.snap
Normal file
179
src/App/__snapshots__/App.test.tsx.snap
Normal file
@@ -0,0 +1,179 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<App /> should display the Header component 1`] = `
|
||||
.emotion-8 {
|
||||
-webkit-transform: translate(-50%,-50%);
|
||||
-ms-transform: translate(-50%,-50%);
|
||||
transform: translate(-50%,-50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.emotion-2 {
|
||||
margin: 0 0 30px 0;
|
||||
border-radius: 25px;
|
||||
box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2);
|
||||
background: #f7f8f6;
|
||||
}
|
||||
|
||||
.emotion-0 {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-image: url([object Object]);
|
||||
background-repeat: no-repeat;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.emotion-6 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.emotion-4 {
|
||||
color: #4b5e40;
|
||||
}
|
||||
|
||||
<div
|
||||
class="MuiBox-root MuiBox-root-215"
|
||||
>
|
||||
<div
|
||||
class="emotion-8 emotion-9"
|
||||
data-testid="loading"
|
||||
>
|
||||
<div
|
||||
class="emotion-2 emotion-3"
|
||||
>
|
||||
<div
|
||||
class="emotion-0 emotion-1"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="emotion-6 emotion-7"
|
||||
>
|
||||
<div
|
||||
class="MuiCircularProgress-root emotion-4 emotion-5 MuiCircularProgress-colorPrimary MuiCircularProgress-indeterminate"
|
||||
role="progressbar"
|
||||
style="width: 50px; height: 50px;"
|
||||
>
|
||||
<svg
|
||||
class="MuiCircularProgress-svg"
|
||||
viewBox="22 22 44 44"
|
||||
>
|
||||
<circle
|
||||
class="MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate"
|
||||
cx="44"
|
||||
cy="44"
|
||||
fill="none"
|
||||
r="20.2"
|
||||
stroke-width="3.6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<App /> should display the Loading component at the beginning 1`] = `
|
||||
.emotion-8 {
|
||||
-webkit-transform: translate(-50%,-50%);
|
||||
-ms-transform: translate(-50%,-50%);
|
||||
transform: translate(-50%,-50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.emotion-2 {
|
||||
margin: 0 0 30px 0;
|
||||
border-radius: 25px;
|
||||
box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2);
|
||||
background: #f7f8f6;
|
||||
}
|
||||
|
||||
.emotion-0 {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-image: url([object Object]);
|
||||
background-repeat: no-repeat;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.emotion-6 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.emotion-4 {
|
||||
color: #4b5e40;
|
||||
}
|
||||
|
||||
<div
|
||||
class="MuiBox-root MuiBox-root-2"
|
||||
>
|
||||
<div
|
||||
class="emotion-8 emotion-9"
|
||||
data-testid="loading"
|
||||
>
|
||||
<div
|
||||
class="emotion-2 emotion-3"
|
||||
>
|
||||
<div
|
||||
class="emotion-0 emotion-1"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="emotion-6 emotion-7"
|
||||
>
|
||||
<div
|
||||
class="MuiCircularProgress-root emotion-4 emotion-5 MuiCircularProgress-colorPrimary MuiCircularProgress-indeterminate"
|
||||
role="progressbar"
|
||||
style="width: 50px; height: 50px;"
|
||||
>
|
||||
<svg
|
||||
class="MuiCircularProgress-svg"
|
||||
viewBox="22 22 44 44"
|
||||
>
|
||||
<circle
|
||||
class="MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate"
|
||||
cx="44"
|
||||
cy="44"
|
||||
fill="none"
|
||||
r="20.2"
|
||||
stroke-width="3.6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
export { default } from './App';
|
||||
export { default as AppContextProvider } from './AppContextProvider';
|
||||
|
||||
Reference in New Issue
Block a user