From 2909e5154d768b59402f8009fa923cf201150b2c Mon Sep 17 00:00:00 2001 From: Kevin Silva Date: Tue, 18 Jul 2023 15:57:39 +0200 Subject: [PATCH] SONAR-19922 - Onboarding Tutorial - Clean up --- .../js/app/components/ComponentContainer.tsx | 28 +++++--- .../js/app/components/GlobalContainer.tsx | 5 ++ .../__tests__/ComponentContainer-test.tsx | 19 ++++++ .../src/main/js/app/styles/init/type.css | 4 ++ .../main/js/apps/overview/components/App.tsx | 1 - .../overview/components/EmptyOverview.tsx | 65 ++++++++++--------- .../tutorials/components/TutorialsApp.tsx | 17 +++-- .../__tests__/TutorialSelection-it.tsx | 6 +- server/sonar-web/src/main/js/helpers/urls.ts | 1 + 9 files changed, 94 insertions(+), 52 deletions(-) diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index 3d98cb0a5bb..b0d4039c39e 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -29,7 +29,7 @@ import { getComponentNavigation } from '../../api/navigation'; import { Location, Router, withRouter } from '../../components/hoc/withRouter'; import { translateWithParameters } from '../../helpers/l10n'; import { HttpStatus } from '../../helpers/request'; -import { getPortfolioUrl } from '../../helpers/urls'; +import { getPortfolioUrl, getProjectUrl } from '../../helpers/urls'; import { ProjectAlmBindingConfigurationErrors, ProjectAlmBindingResponse, @@ -88,7 +88,7 @@ export class ComponentContainer extends React.PureComponent { window.clearTimeout(this.watchStatusTimer); } - fetchComponent = async () => { + fetchComponent = async (shouldRedirectToDashboard = false) => { const { branch, id: key, pullRequest } = this.props.location.query; this.setState({ loading: true }); @@ -131,11 +131,18 @@ export class ComponentContainer extends React.PureComponent { } if (this.mounted) { - this.setState({ - component: componentWithQualifier, - projectBinding, - loading: false, - }); + this.setState( + { + component: componentWithQualifier, + projectBinding, + loading: false, + }, + () => { + if (shouldRedirectToDashboard && this.props.location.pathname.match('tutorials')) { + this.props.router.replace(getProjectUrl(key)); + } + } + ); this.fetchStatus(componentWithQualifier.key); this.fetchProjectBindingErrors(componentWithQualifier); @@ -147,7 +154,7 @@ export class ComponentContainer extends React.PureComponent { ({ current, queue }) => { if (this.mounted) { let shouldFetchComponent = false; - + let shouldRedirectToDashboard = false; this.setState( ({ component, currentTask, tasksInProgress }) => { const newCurrentTask = this.getCurrentTask(current); @@ -162,6 +169,9 @@ export class ComponentContainer extends React.PureComponent { component ); + shouldRedirectToDashboard = + component !== undefined && Boolean(!component.analysisDate); + if (this.needsAnotherCheck(shouldFetchComponent, component, newTasksInProgress)) { // Refresh the status as long as there is tasks in progress or no analysis window.clearTimeout(this.watchStatusTimer); @@ -182,7 +192,7 @@ export class ComponentContainer extends React.PureComponent { }, () => { if (shouldFetchComponent) { - this.fetchComponent(); + this.fetchComponent(shouldRedirectToDashboard); } } ); diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx index 05c5c8148ca..2d99e1311e4 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx @@ -49,6 +49,8 @@ const TEMP_PAGELIST_WITH_NEW_BACKGROUND = [ '/project/information', ]; +const TEMP_PAGELIST_WITH_NEW_BACKGROUND_WHITE = ['/tutorials']; + export default function GlobalContainer() { // it is important to pass `location` down to `GlobalNav` to trigger render on url change const location = useLocation(); @@ -63,6 +65,9 @@ export default function GlobalContainer() {
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index 0ed48b25ac1..48bf7483773 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -298,6 +298,25 @@ it('only fully loads a non-empty component once', async () => { expect(getTasksForComponent).toHaveBeenCalledTimes(1); }); +it('should redirect if in tutorials and ends the first analyses', async () => { + (getComponentData as jest.Mock).mockResolvedValueOnce({ + component: { key: 'bar', analysisDate: undefined }, + }); + (getTasksForComponent as jest.Mock).mockResolvedValueOnce({ + queue: [], + current: { id: 'foo', status: TaskStatuses.Success, type: TaskTypes.Report }, + }); + + const replace = jest.fn(); + const wrapper = shallowRender({ + location: mockLocation({ pathname: '/tutorials' }), + router: mockRouter({ replace }), + }); + + await waitAndUpdate(wrapper); + expect(replace).toHaveBeenCalledTimes(1); +}); + it('only fully reloads a non-empty component if there was previously some task in progress', async () => { jest.mocked(getComponentData).mockResolvedValueOnce({ component: { key: 'bar', analysisDate: '2019-01-01' }, diff --git a/server/sonar-web/src/main/js/app/styles/init/type.css b/server/sonar-web/src/main/js/app/styles/init/type.css index fdf6e89d8de..260102cbdfe 100644 --- a/server/sonar-web/src/main/js/app/styles/init/type.css +++ b/server/sonar-web/src/main/js/app/styles/init/type.css @@ -298,3 +298,7 @@ small, .new-background { background-color: #fcfcfd; } + +.white-background { + background-color: #ffffff; +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx index 96a4bcf287b..d6231334fcd 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx @@ -71,7 +71,6 @@ export function App(props: AppProps) { branchLikes={branchLikes} component={component} hasAnalyses={isPending ?? isInProgress} - projectBinding={projectBinding} /> )} diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx index 6da4abfc6ae..5ef4b9708c0 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx @@ -17,13 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { FlagMessage, LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; import * as React from 'react'; +import { Navigate } from 'react-router-dom'; import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext'; -import TutorialSelection from '../../../components/tutorials/TutorialSelection'; -import { Alert } from '../../../components/ui/Alert'; import { getBranchLikeDisplayName, isBranch, isMainBranch } from '../../../helpers/branch-like'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import { getProjectTutorialLocation } from '../../../helpers/urls'; import { BranchLike } from '../../../types/branch-like'; import { ComponentQualifier } from '../../../types/component'; import { Component } from '../../../types/types'; @@ -35,19 +35,22 @@ export interface EmptyOverviewProps { component: Component; currentUser: CurrentUser; hasAnalyses?: boolean; - projectBinding?: ProjectAlmBindingResponse; } export function EmptyOverview(props: EmptyOverviewProps) { - const { branchLike, branchLikes, component, currentUser, hasAnalyses, projectBinding } = props; + const { branchLike, branchLikes, component, currentUser, hasAnalyses } = props; if (component.qualifier === ComponentQualifier.Application) { return ( -
- + + {translate('provisioning.no_analysis.application')} - -
+ + ); } else if (!isBranch(branchLike)) { return null; @@ -60,6 +63,10 @@ export function EmptyOverview(props: EmptyOverviewProps) { const showTutorial = isMainBranch(branchLike) && !hasBranches && !hasAnalyses; + if (showTutorial && isLoggedIn(currentUser)) { + return ; + } + let warning; if (isLoggedIn(currentUser) && isMainBranch(branchLike) && hasBranches && hasBadBranchConfig) { warning = translateWithParameters( @@ -75,29 +82,23 @@ export function EmptyOverview(props: EmptyOverviewProps) { } return ( -
- {isLoggedIn(currentUser) ? ( - <> - {hasBranches && ( - - {warning} - - )} - {showTutorial && ( - - )} - - ) : ( - - {warning} - - )} -
+ + + {isLoggedIn(currentUser) ? ( + <> + {hasBranches && ( + + {warning} + + )} + + ) : ( + + {warning} + + )} + + ); } diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx index 890451abf2d..bff4b922f50 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx +++ b/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; import * as React from 'react'; import withComponentContext from '../../../app/components/componentContext/withComponentContext'; import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext'; @@ -41,13 +42,15 @@ export function TutorialsApp(props: TutorialsAppProps) { } return ( -
- -
+ + + + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx index 8824df09ed1..511fadce2f0 100644 --- a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx @@ -128,7 +128,7 @@ it('should correctly fetch the corresponding ALM setting', async () => { { projectBinding: mockProjectAlmBindingResponse({ alm: AlmKeys.GitHub, key: 'binding' }), }, - `dashboard?selectedTutorial=${TutorialModes.Jenkins}&id=bar` + `tutorials?selectedTutorial=${TutorialModes.Jenkins}&id=bar` ); await waitOnDataLoaded(); @@ -190,10 +190,10 @@ async function startLocalTutorial(user: UserEvent) { function renderTutorialSelection( props: Partial> = {}, - navigateTo: string = 'dashboard?id=bar' + navigateTo: string = 'tutorials?id=bar' ) { return renderApp( - '/dashboard', + '/tutorials', { return { + pathname: '/tutorials', search: queryToSearch({ id: project, selectedTutorial }), }; } -- 2.39.5