From 222ffed0226f5aaa62f2d5b91bb08717b2aa24ef Mon Sep 17 00:00:00 2001 From: coolsp Date: Sun, 12 Jan 2020 22:21:29 +0100 Subject: [PATCH] fix: package list refresh based on logged-in user (#415) * fix: package list refresh based on logged-in user description: In `pages/home/Home.tsx` now monitoring any change in a user log-in/out which will trigger a new `API.request` to get the _packages_ from the Verdaccio-server. This is done by creating a `useEffect` on **isUserLoggedIn**. Code has been transplanted from `App/App.tsx` including the use of the Loading component during the XHR. The use of **packages** was removed from other components as no longer needed and tests updated. Resolves issue #414 * fix: package list refresh based on logged-in user description: In `pages/home/Home.tsx` now monitoring any change in a user log-in/out which will trigger a new `API.request` to get the _packages_ from the Verdaccio-server. This is done by creating a `useEffect` on **isUserLoggedIn**. Code has been transplanted from `App/App.tsx` including the use of the Loading component during the XHR. The use of **packages** was removed from other components as no longer needed and tests updated. Test snapshots updated Resolves issue #414 Co-authored-by: Juan Picado @jotadeveloper --- src/App/App.tsx | 50 +- src/App/AppContext.ts | 1 - src/App/AppContextProvider.tsx | 11 +- src/App/AppRoute.tsx | 4 +- src/App/__snapshots__/App.test.tsx.snap | 1332 ++++++++++++++++- src/components/Header/Header.test.tsx | 14 +- .../LoginDialog/LoginDialog.test.tsx | 1 - src/pages/home/Home.tsx | 34 +- 8 files changed, 1319 insertions(+), 128 deletions(-) diff --git a/src/App/App.tsx b/src/App/App.tsx index 7ee8c8e..c2ba219 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -5,11 +5,9 @@ import { Router } from 'react-router-dom'; import storage from '../utils/storage'; 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 { Theme } from '../design-tokens/theme'; @@ -33,11 +31,9 @@ const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({ /* 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 + * Logout user * Required by:
*/ const logout = () => { @@ -59,46 +55,26 @@ const App: React.FC = () => { setUser({ username }); }; - const loadOnHandler = async () => { - try { - 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}`, - }); - } - - setIsLoading(false); - }; - useEffect(() => { checkUserAlreadyLoggedIn(); - loadOnHandler(); }, []); return ( <> - {isLoading ? ( - - ) : ( - <> - - -
- - - - - -
- - )} + <> + + +
+ + {/* eslint-disable-next-line react/jsx-max-depth */} + + + + +
+ ); diff --git a/src/App/AppContext.ts b/src/App/AppContext.ts index 70da778..a8837ab 100644 --- a/src/App/AppContext.ts +++ b/src/App/AppContext.ts @@ -3,7 +3,6 @@ import { createContext } from 'react'; export interface AppProps { user?: User; scope: string; - packages: any[]; } export interface User { diff --git a/src/App/AppContextProvider.tsx b/src/App/AppContextProvider.tsx index 92805fe..6422891 100644 --- a/src/App/AppContextProvider.tsx +++ b/src/App/AppContextProvider.tsx @@ -3,15 +3,13 @@ import React, { useState, useEffect } from 'react'; import AppContext, { AppProps, User } from './AppContext'; interface Props { - packages: any[]; user?: User; } /* eslint-disable react-hooks/exhaustive-deps */ -const AppContextProvider: React.FC = ({ children, packages, user }) => { +const AppContextProvider: React.FC = ({ children, user }) => { const [state, setState] = useState({ scope: window.VERDACCIO_SCOPE || '', - packages, user, }); @@ -22,13 +20,6 @@ const AppContextProvider: React.FC = ({ children, packages, user }) => { }); }, [user]); - useEffect(() => { - setState({ - ...state, - packages, - }); - }, [packages]); - const setUser = (user?: User) => { setState({ ...state, diff --git a/src/App/AppRoute.tsx b/src/App/AppRoute.tsx index 4f64a13..0fce9f0 100644 --- a/src/App/AppRoute.tsx +++ b/src/App/AppRoute.tsx @@ -30,7 +30,7 @@ const AppRoute: React.FC = () => { throw Error('The app Context was not correct used'); } - const { user, packages } = appContext; + const { user } = appContext; const isUserLoggedIn = user && user.username; @@ -39,7 +39,7 @@ const AppRoute: React.FC = () => { }> - + diff --git a/src/App/__snapshots__/App.test.tsx.snap b/src/App/__snapshots__/App.test.tsx.snap index d54ae26..ae9c1dd 100644 --- a/src/App/__snapshots__/App.test.tsx.snap +++ b/src/App/__snapshots__/App.test.tsx.snap @@ -1,11 +1,158 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` should display the Header component 1`] = ` -.emotion-10 { +.emotion-78 { background-color: #fff; } +.emotion-24 { + background-color: #4b5e40; + min-height: 60px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; +} + +@media (min-width:768px) { + .emotion-24 .emotion-13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } + + .emotion-24 .emotion-17 { + display: none; + } + + .emotion-24 .e1jf5lit4 { + display: none; + } +} + +@media (min-width:1024px) { + .emotion-24 .emotion-23 { + padding: 0 20px; + } + +@media (min-width:1275px) { + .emotion-24 .emotion-23 { + max-width: 1240px; + width: 100%; + margin: 0 auto; + } +} +} + +.emotion-22 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0 15px; +} + +.emotion-14 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.emotion-2 { + margin-right: 1em; +} + +.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: 40px; + height: 40px; +} + +.emotion-12 { + display: none; + max-width: 393px; + width: 100%; +} + +.emotion-10 { + width: 100%; + height: 32px; + position: relative; + z-index: 1; +} + +.emotion-6 .MuiInputBase-root:before { + content: ''; + border: none; +} + +.emotion-6 .MuiInputBase-root:after { + border-color: #fff; +} + +.emotion-6 .MuiInputBase-root:hover:before { + content: none; +} + +.emotion-6 .MuiInputBase-input { + color: #fff; +} + +.emotion-4 { + color: #fff; +} + .emotion-8 { + max-height: 500px; + overflow-y: auto; +} + +.emotion-20 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; +} + +.emotion-16 { + display: block; +} + +.emotion-18 { + color: #fff; +} + +@media screen and (min-width:1240px) { + .emotion-36 { + max-width: 1240px; + width: 100%; + margin-left: auto; + margin-right: auto; + } +} + +.emotion-34 { -webkit-transform: translate(-50%,-50%); -ms-transform: translate(-50%,-50%); transform: translate(-50%,-50%); @@ -14,14 +161,14 @@ exports[` should display the Header component 1`] = ` position: absolute; } -.emotion-2 { +.emotion-28 { margin: 0 0 30px 0; border-radius: 25px; box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2); background: #f7f8f6; } -.emotion-0 { +.emotion-26 { display: inline-block; vertical-align: middle; box-sizing: border-box; @@ -33,7 +180,7 @@ exports[` should display the Header component 1`] = ` height: 90px; } -.emotion-6 { +.emotion-32 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -48,45 +195,504 @@ exports[` should display the Header component 1`] = ` justify-content: center; } -.emotion-4 { +.emotion-30 { color: #4b5e40; } +.emotion-76 { + background: #f9f9f9; + border-top: 1px solid #e3e3e3; + color: #999999; + font-size: 14px; + padding: 20px; +} + +.emotion-74 { + 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: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; + width: 100%; +} + +@media (min-width:768px) { + .emotion-74 { + min-width: 400px; + max-width: 800px; + margin: auto; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + } +} + +@media (min-width:1024px) { + .emotion-74 { + max-width: 1240px; + } +} + +.emotion-65 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: none; +} + +@media (min-width:768px) { + .emotion-65 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } +} + +.emotion-38 { + color: #e25555; + padding: 0 5px; +} + +.emotion-63 { + position: relative; + height: 18px; +} + +.emotion-63:hover .emotion-62 { + visibility: visible; +} + +.emotion-41 { + box-sizing: initial; + display: inline-block; + cursor: default; + width: 18px; + height: 18px; + padding: 0 10px; +} + +.emotion-61 { + position: absolute; + background: #d3dddd; + padding: 1px 4px; + border-radius: 3px; + height: 20px; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + visibility: hidden; + top: -2px; +} + +.emotion-61:before { + content: ''; + position: absolute; + top: 29%; + left: -4px; + margin-left: -5px; + border: 5px solid; + border-color: #d3dddd transparent transparent transparent; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +.emotion-44 { + box-sizing: initial; + display: inline-block; + cursor: default; + width: 18px; + height: 18px; + padding: 0 5px; +} + +.emotion-72 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: none; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +@media (min-width:768px) { + .emotion-72 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } +} + +.emotion-70 { + box-sizing: initial; + display: inline-block; + cursor: pointer; + width: 18px; + height: 18px; + padding: 0 5px; +} + +.emotion-67 { + width: 100%; + height: auto; +} +
-
-
-
-
- - - + +
+
+ +
+
+
+ + +
+ +
+
+ + +
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+ Made with + + ♥ + + on + + + + Earth + + + + + + + Spain + + + + + + Nicaragua + + + + + + India + + + + + + Brazil + + + + + + China + + + + + + Austria + + + + + +
+
+ Powered by + + Verdaccio + + / undefined
@@ -94,11 +700,158 @@ exports[` should display the Header component 1`] = ` `; exports[` should display the Loading component at the beginning 1`] = ` -.emotion-10 { +.emotion-78 { background-color: #fff; } +.emotion-24 { + background-color: #4b5e40; + min-height: 60px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; +} + +@media (min-width:768px) { + .emotion-24 .emotion-13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } + + .emotion-24 .emotion-17 { + display: none; + } + + .emotion-24 .e1jf5lit4 { + display: none; + } +} + +@media (min-width:1024px) { + .emotion-24 .emotion-23 { + padding: 0 20px; + } + +@media (min-width:1275px) { + .emotion-24 .emotion-23 { + max-width: 1240px; + width: 100%; + margin: 0 auto; + } +} +} + +.emotion-22 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0 15px; +} + +.emotion-14 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.emotion-2 { + margin-right: 1em; +} + +.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: 40px; + height: 40px; +} + +.emotion-12 { + display: none; + max-width: 393px; + width: 100%; +} + +.emotion-10 { + width: 100%; + height: 32px; + position: relative; + z-index: 1; +} + +.emotion-6 .MuiInputBase-root:before { + content: ''; + border: none; +} + +.emotion-6 .MuiInputBase-root:after { + border-color: #fff; +} + +.emotion-6 .MuiInputBase-root:hover:before { + content: none; +} + +.emotion-6 .MuiInputBase-input { + color: #fff; +} + +.emotion-4 { + color: #fff; +} + .emotion-8 { + max-height: 500px; + overflow-y: auto; +} + +.emotion-20 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; +} + +.emotion-16 { + display: block; +} + +.emotion-18 { + color: #fff; +} + +@media screen and (min-width:1240px) { + .emotion-36 { + max-width: 1240px; + width: 100%; + margin-left: auto; + margin-right: auto; + } +} + +.emotion-34 { -webkit-transform: translate(-50%,-50%); -ms-transform: translate(-50%,-50%); transform: translate(-50%,-50%); @@ -107,14 +860,14 @@ exports[` should display the Loading component at the beginning 1`] = ` position: absolute; } -.emotion-2 { +.emotion-28 { margin: 0 0 30px 0; border-radius: 25px; box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2); background: #f7f8f6; } -.emotion-0 { +.emotion-26 { display: inline-block; vertical-align: middle; box-sizing: border-box; @@ -126,7 +879,7 @@ exports[` should display the Loading component at the beginning 1`] = ` height: 90px; } -.emotion-6 { +.emotion-32 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -141,45 +894,500 @@ exports[` should display the Loading component at the beginning 1`] = ` justify-content: center; } -.emotion-4 { +.emotion-30 { color: #4b5e40; } +.emotion-76 { + background: #f9f9f9; + border-top: 1px solid #e3e3e3; + color: #999999; + font-size: 14px; + padding: 20px; +} + +.emotion-74 { + 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: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; + width: 100%; +} + +@media (min-width:768px) { + .emotion-74 { + min-width: 400px; + max-width: 800px; + margin: auto; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + } +} + +@media (min-width:1024px) { + .emotion-74 { + max-width: 1240px; + } +} + +.emotion-65 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: none; +} + +@media (min-width:768px) { + .emotion-65 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } +} + +.emotion-38 { + color: #e25555; + padding: 0 5px; +} + +.emotion-63 { + position: relative; + height: 18px; +} + +.emotion-63:hover .emotion-62 { + visibility: visible; +} + +.emotion-41 { + box-sizing: initial; + display: inline-block; + cursor: default; + width: 18px; + height: 18px; + padding: 0 10px; +} + +.emotion-61 { + position: absolute; + background: #d3dddd; + padding: 1px 4px; + border-radius: 3px; + height: 20px; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + visibility: hidden; + top: -2px; +} + +.emotion-61:before { + content: ''; + position: absolute; + top: 29%; + left: -4px; + margin-left: -5px; + border: 5px solid; + border-color: #d3dddd transparent transparent transparent; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +.emotion-44 { + box-sizing: initial; + display: inline-block; + cursor: default; + width: 18px; + height: 18px; + padding: 0 5px; +} + +.emotion-72 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: none; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +@media (min-width:768px) { + .emotion-72 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } +} + +.emotion-70 { + box-sizing: initial; + display: inline-block; + cursor: pointer; + width: 18px; + height: 18px; + padding: 0 5px; +} + +.emotion-67 { + width: 100%; + height: auto; +} +
-
-
-
-
- - - + +
+
+ +
+
+
+ + +
+ +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+ Made with + + ♥ + + on + + + + Earth + + + + + + + Spain + + + + + + Nicaragua + + + + + + India + + + + + + Brazil + + + + + + China + + + + + + Austria + + + + + +
+
+ Powered by + + Verdaccio + + / undefined
diff --git a/src/components/Header/Header.test.tsx b/src/components/Header/Header.test.tsx index fdfef85..aeed01f 100644 --- a/src/components/Header/Header.test.tsx +++ b/src/components/Header/Header.test.tsx @@ -18,7 +18,7 @@ describe('
component with logged in state', () => { test('should load the component in logged out state', () => { const { container, queryByTestId, getByText } = render( - +
@@ -32,7 +32,7 @@ describe('
component with logged in state', () => { test('should load the component in logged in state', () => { const { container, getByTestId, queryByText } = render( - +
@@ -46,7 +46,7 @@ describe('
component with logged in state', () => { test('should open login dialog', async () => { const { getByText } = render( - +
@@ -61,7 +61,7 @@ describe('
component with logged in state', () => { test('should logout the user', async () => { const { getByText, getByTestId } = render( - +
@@ -79,7 +79,7 @@ describe('
component with logged in state', () => { test("The question icon should open a new tab of verdaccio's website - installation doc", () => { const { getByTestId } = render( - +
@@ -92,7 +92,7 @@ describe('
component with logged in state', () => { test('should open the registrationInfo modal when clicking on the info icon', async () => { const { getByTestId } = render( - +
@@ -109,7 +109,7 @@ describe('
component with logged in state', () => { test('should close the registrationInfo modal when clicking on the button close', async () => { const { getByTestId, getByText, queryByTestId } = render( - +
diff --git a/src/components/LoginDialog/LoginDialog.test.tsx b/src/components/LoginDialog/LoginDialog.test.tsx index ab104ba..15c3830 100644 --- a/src/components/LoginDialog/LoginDialog.test.tsx +++ b/src/components/LoginDialog/LoginDialog.test.tsx @@ -8,7 +8,6 @@ import LoginDialog from './LoginDialog'; const appContextValue: AppContextProps = { scope: '', - packages: [], setUser: jest.fn(), }; diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx index 6f2c72b..6406e0e 100644 --- a/src/pages/home/Home.tsx +++ b/src/pages/home/Home.tsx @@ -1,17 +1,35 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { PackageList } from '../../components/PackageList'; -import { PackageInterface } from '../../components/Package/Package'; +import API from '../../utils/api'; +import Loading from '../../components/Loading'; interface Props { isUserLoggedIn: boolean; - packages: PackageInterface[]; } -const Home: React.FC = ({ packages }) => ( -
- -
-); +const Home: React.FC = ({ isUserLoggedIn }) => { + const [packages, setPackages] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const loadPackages = async () => { + try { + 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}`, + }); + } + setIsLoading(false); + }; + useEffect(() => { + loadPackages().then(); + }, [isUserLoggedIn]); + + return
{isLoading ? : }
; +}; export default Home;