diff options
13 files changed, 335 insertions, 90 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx index 11f6fe3fbc5..7a805ec17ba 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx @@ -48,7 +48,7 @@ interface Props { handleOrgDetailsFinish: (organization: T.Organization) => Promise<void>; handleOrgDetailsStepOpen: () => void; onDone: () => void; - onOrgCreated: (organization: string, justCreated?: boolean) => void; + onOrgCreated: (organization: string) => void; onUpgradeFail: () => void; organization?: T.Organization; step: Step; @@ -72,7 +72,7 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S return bindAlmOrganization({ organization, installationId: this.props.almInstallId - }).then(() => this.props.onOrgCreated(organization, false)); + }).then(() => this.props.onOrgCreated(organization)); }; handleCreateOrganization = () => { diff --git a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx index 7ccc09db294..e368675861c 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx @@ -205,7 +205,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr this.updateUrlQuery({ almInstallId: undefined, almKey: undefined }); }; - handleOrgCreated = (organization: string, justCreated = true) => { + handleOrgCreated = (organization: string) => { this.props.skipOnboarding(); if (this.isStoredTimestampValid(ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP)) { this.props.router.push({ @@ -213,10 +213,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr state: { organization, tab: this.state.almOrganization ? 'auto' : 'manual' } }); } else { - this.props.router.push({ - pathname: getOrganizationUrl(organization), - state: { justCreated } - }); + this.props.router.push({ pathname: getOrganizationUrl(organization) }); } }; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx index c7999e6fa19..4026d982fab 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx @@ -87,7 +87,7 @@ it('should bind existing organization', async () => { organization: 'foo' }); await waitAndUpdate(wrapper); - expect(onOrgCreated).toHaveBeenCalledWith('foo', false); + expect(onOrgCreated).toHaveBeenCalledWith('foo'); }); function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) { diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx index 4da19584a2e..ddd5ebcd85f 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx @@ -226,16 +226,7 @@ it('should redirect to organization page after creation', async () => { wrapper.setState({ organization: boundOrganization }); wrapper.instance().handleOrgCreated('foo'); - expect(push).toHaveBeenCalledWith({ - pathname: '/organizations/foo', - state: { justCreated: true } - }); - - wrapper.instance().handleOrgCreated('foo', false); - expect(push).toHaveBeenCalledWith({ - pathname: '/organizations/foo', - state: { justCreated: false } - }); + expect(push).toHaveBeenCalledWith({ pathname: '/organizations/foo' }); }); it('should redirect to projects creation page after creation', async () => { diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.css b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css index 2779ccbce5c..55f794f1fc5 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.css +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css @@ -17,7 +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. */ -.organization-just-created { - margin: 120px auto 0; +.organization-empty { + margin: 100px auto 0; width: 800px; } diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx index 89f1d217de5..d47f5675f1c 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx @@ -25,7 +25,7 @@ import { translate } from '../../../helpers/l10n'; import { OnboardingContextShape } from '../../../app/components/OnboardingContext'; import { withRouter, Router } from '../../../components/hoc/withRouter'; import '../../tutorials/styles.css'; -import './OrganizationJustCreated.css'; +import './OrganizationEmpty.css'; interface Props { openProjectOnboarding: OnboardingContextShape; @@ -33,7 +33,7 @@ interface Props { router: Pick<Router, 'push'>; } -export class OrganizationJustCreated extends React.PureComponent<Props> { +export class OrganizationEmpty extends React.PureComponent<Props> { handleNewProjectClick = () => { this.props.openProjectOnboarding(this.props.organization); }; @@ -45,7 +45,7 @@ export class OrganizationJustCreated extends React.PureComponent<Props> { render() { return ( - <div className="organization-just-created"> + <div className="organization-empty"> <h3 className="text-center">{translate('onboarding.create_organization.ready')}</h3> <div className="onboarding-choices"> <Button className="onboarding-choice" onClick={this.handleNewProjectClick}> @@ -66,4 +66,4 @@ export class OrganizationJustCreated extends React.PureComponent<Props> { } } -export default withRouter(OrganizationJustCreated); +export default withRouter(OrganizationEmpty); diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx index d8770331308..e74b3a8b651 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import Helmet from 'react-helmet'; import { connect } from 'react-redux'; import { Location } from 'history'; -import OrganizationJustCreated from './OrganizationJustCreated'; import OrganizationNavigation from '../navigation/OrganizationNavigation'; import { fetchOrganization } from '../actions'; import NotFound from '../../../app/components/NotFound'; @@ -32,7 +31,6 @@ import { getMyOrganizations, Store } from '../../../store/rootReducer'; -import { OnboardingContext } from '../../../app/components/OnboardingContext'; interface OwnProps { children?: React.ReactNode; @@ -86,23 +84,6 @@ export class OrganizationPage extends React.PureComponent<Props, State> { this.props.fetchOrganization(organizationKey).then(this.stopLoading, this.stopLoading); }; - renderChildren(organization: T.Organization) { - const { location } = this.props; - const justCreated = Boolean(location.state && location.state.justCreated); - return justCreated ? ( - <OnboardingContext.Consumer> - {openProjectOnboarding => ( - <OrganizationJustCreated - openProjectOnboarding={openProjectOnboarding} - organization={organization} - /> - )} - </OnboardingContext.Consumer> - ) : ( - this.props.children - ); - } - render() { const { organization } = this.props; @@ -124,7 +105,7 @@ export class OrganizationPage extends React.PureComponent<Props, State> { organization={organization} userOrganizations={this.props.userOrganizations} /> - {this.renderChildren(organization)} + {this.props.children} </div> ); } diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationEmpty-test.tsx index 0bcc3631ed5..257fa9e9e27 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationEmpty-test.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { shallow } from 'enzyme'; -import { OrganizationJustCreated } from '../OrganizationJustCreated'; +import { OrganizationEmpty } from '../OrganizationEmpty'; import { click } from '../../../../helpers/testUtils'; const organization: T.Organization = { key: 'foo', name: 'Foo' }; @@ -27,7 +27,7 @@ const organization: T.Organization = { key: 'foo', name: 'Foo' }; it('should render', () => { expect( shallow( - <OrganizationJustCreated + <OrganizationEmpty openProjectOnboarding={jest.fn()} organization={organization} router={{ push: jest.fn() }} @@ -39,7 +39,7 @@ it('should render', () => { it('should create new project', () => { const openProjectOnboarding = jest.fn(); const wrapper = shallow( - <OrganizationJustCreated + <OrganizationEmpty openProjectOnboarding={openProjectOnboarding} organization={organization} router={{ push: jest.fn() }} @@ -52,7 +52,7 @@ it('should create new project', () => { it('should add members', () => { const router = { push: jest.fn() }; const wrapper = shallow( - <OrganizationJustCreated + <OrganizationEmpty openProjectOnboarding={jest.fn()} organization={organization} router={router} diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationJustCreated-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap index 0ce7ab7b2fa..32086688e74 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationJustCreated-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should render 1`] = ` <div - className="organization-just-created" + className="organization-empty" > <h3 className="text-center" diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.ts b/server/sonar-web/src/main/js/apps/organizations/routes.ts index e3279292d9c..d78634091ec 100644 --- a/server/sonar-web/src/main/js/apps/organizations/routes.ts +++ b/server/sonar-web/src/main/js/apps/organizations/routes.ts @@ -34,11 +34,8 @@ const routes = [ { indexRoute: { onEnter(nextState: RouterState, replace: RedirectFunction) { - const { location, params } = nextState; - const justCreated = Boolean(location.state && location.state.justCreated); - if (!justCreated) { - replace(`/organizations/${params.organizationKey}/projects`); - } + const { params } = nextState; + replace(`/organizations/${params.organizationKey}/projects`); } } }, diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx index 94f52fe4461..1c141b7e6f5 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx @@ -26,6 +26,7 @@ import PageSidebar from './PageSidebar'; import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; import ListFooter from '../../../components/controls/ListFooter'; +import OrganizationEmpty from '../../organizations/components/OrganizationEmpty'; import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; import Visualizations from '../visualizations/Visualizations'; @@ -33,11 +34,12 @@ import { Project, Facets } from '../types'; import { fetchProjects, parseSorting, SORTING_SWITCH } from '../utils'; import { parseUrlQuery, Query, hasFilterParams, hasVisualizationParams } from '../query'; import { translate } from '../../../helpers/l10n'; -import { addSideBarFooterClass, removeSideBarFooterClass } from '../../../helpers/pages'; +import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages'; import { RawQuery } from '../../../helpers/query'; import { get, save } from '../../../helpers/storage'; import { isSonarCloud } from '../../../helpers/system'; import { isLoggedIn } from '../../../helpers/users'; +import { OnboardingContext } from '../../../app/components/OnboardingContext'; import { withRouter, Location, Router } from '../../../components/hoc/withRouter'; import '../../../components/search-navigator.css'; import '../styles.css'; @@ -47,13 +49,13 @@ interface Props { isFavorite: boolean; location: Pick<Location, 'pathname' | 'query'>; organization: T.Organization | undefined; - organizationsEnabled?: boolean; router: Pick<Router, 'push' | 'replace'>; storageOptionsSuffix?: string; } interface State { facets?: Facets; + initialLoading: boolean; loading: boolean; pageIndex?: number; projects?: Project[]; @@ -70,7 +72,7 @@ export class AllProjects extends React.PureComponent<Props, State> { constructor(props: Props) { super(props); - this.state = { loading: true, query: {} }; + this.state = { initialLoading: true, loading: true, query: {} }; } componentDidMount() { @@ -81,38 +83,37 @@ export class AllProjects extends React.PureComponent<Props, State> { return; } this.handleQueryChange(true); - addSideBarFooterClass(); + this.updateFooterClass(); } componentDidUpdate(prevProps: Props) { if (prevProps.location.query !== this.props.location.query) { this.handleQueryChange(false); } + + if ( + prevProps.organization && + this.props.organization && + prevProps.organization.key !== this.props.organization.key + ) { + this.setState({ initialLoading: true }); + } + + this.updateFooterClass(); } componentWillUnmount() { this.mounted = false; - removeSideBarFooterClass(); + removeSideBarClass(); } - getView = () => this.state.query.view || 'overall'; - - getVisualization = () => this.state.query.visualization || 'risk'; - - getSort = () => this.state.query.sort || 'name'; - - stopLoading = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - }; - fetchProjects = (query: any) => { this.setState({ loading: true, query }); fetchProjects(query, this.props.isFavorite, this.props.organization).then(response => { if (this.mounted) { this.setState({ facets: response.facets, + initialLoading: false, loading: false, pageIndex: 1, projects: response.projects, @@ -141,6 +142,8 @@ export class AllProjects extends React.PureComponent<Props, State> { } }; + getSort = () => this.state.query.sort || 'name'; + getStorageOptions = () => { const { storageOptionsSuffix } = this.props; const options: { @@ -160,6 +163,14 @@ export class AllProjects extends React.PureComponent<Props, State> { return options; }; + getView = () => this.state.query.view || 'overall'; + + getVisualization = () => this.state.query.visualization || 'risk'; + + handleClearAll = () => { + this.props.router.push({ pathname: this.props.location.pathname }); + }; + handlePerspectiveChange = ({ view, visualization }: { view: string; visualization?: string }) => { const { storageOptionsSuffix } = this.props; const query: { @@ -188,12 +199,6 @@ export class AllProjects extends React.PureComponent<Props, State> { save(PROJECTS_VISUALIZATION, visualization, storageOptionsSuffix); }; - handleSortChange = (sort: string, desc: boolean) => { - const asString = (desc ? '-' : '') + sort; - this.updateLocationQuery({ sort: asString }); - save(PROJECTS_SORT, asString, this.props.storageOptionsSuffix); - }; - handleQueryChange(initialMount: boolean) { const query = parseUrlQuery(this.props.location.query); const savedOptions = this.getStorageOptions(); @@ -207,13 +212,34 @@ export class AllProjects extends React.PureComponent<Props, State> { } } + handleSortChange = (sort: string, desc: boolean) => { + const asString = (desc ? '-' : '') + sort; + this.updateLocationQuery({ sort: asString }); + save(PROJECTS_SORT, asString, this.props.storageOptionsSuffix); + }; + + stopLoading = () => { + if (this.mounted) { + this.setState({ initialLoading: false, loading: false }); + } + }; + updateLocationQuery = (newQuery: RawQuery) => { const query = omitBy({ ...this.props.location.query, ...newQuery }, x => !x); this.props.router.push({ pathname: this.props.location.pathname, query }); }; - handleClearAll = () => { - this.props.router.push({ pathname: this.props.location.pathname }); + updateFooterClass = () => { + const { organization } = this.props; + const { initialLoading, projects } = this.state; + const isOrganizationContext = isSonarCloud() && organization; + const isEmpty = projects && projects.length === 0; + + if (isOrganizationContext && (initialLoading || isEmpty)) { + removeSideBarClass(); + } else { + addSideBarClass(); + } }; renderSide = () => ( @@ -270,7 +296,7 @@ export class AllProjects extends React.PureComponent<Props, State> { <div className="layout-page-main-inner"> {this.state.projects && ( <Visualizations - displayOrganizations={!this.props.organization && this.props.organizationsEnabled} + displayOrganizations={!this.props.organization && isSonarCloud()} projects={this.state.projects} sort={this.state.query.sort} total={this.state.total} @@ -304,17 +330,44 @@ export class AllProjects extends React.PureComponent<Props, State> { }; render() { + const { organization } = this.props; + const { projects } = this.state; + const isOrganizationContext = isSonarCloud() && organization; + const initialLoading = isOrganizationContext && this.state.initialLoading; + const organizationEmpty = isOrganizationContext && projects && projects.length === 0; + return ( <div className="layout-page projects-page" id="projects-page"> <Suggestions suggestions="projects" /> <Helmet title={translate('projects.page')} /> - {this.renderSide()} + {initialLoading ? ( + <div className="display-flex-space-around width-100 huge-spacer-top"> + <DeferredSpinner /> + </div> + ) : ( + <> + {!organizationEmpty && this.renderSide()} - <div className="layout-page-main"> - {this.renderHeader()} - {this.renderMain()} - </div> + <div className="layout-page-main"> + {organizationEmpty && organization ? ( + <OnboardingContext.Consumer> + {openProjectOnboarding => ( + <OrganizationEmpty + openProjectOnboarding={openProjectOnboarding} + organization={organization} + /> + )} + </OnboardingContext.Consumer> + ) : ( + <> + {this.renderHeader()} + {this.renderMain()} + </> + )} + </div> + </> + )} </div> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx index 12e7e5d81e5..d1ac2e535f0 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx @@ -22,6 +22,8 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import { AllProjects } from '../AllProjects'; import { get, save } from '../../../../helpers/storage'; +import { isSonarCloud } from '../../../../helpers/system'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; jest.mock('../ProjectsList', () => ({ // eslint-disable-next-line @@ -55,11 +57,14 @@ jest.mock('../../../../helpers/storage', () => ({ save: jest.fn() })); -const fetchProjects = require('../../utils').fetchProjects as jest.Mock<any>; +jest.mock('../../../../helpers/system', () => ({ isSonarCloud: jest.fn() })); + +const fetchProjects = require('../../utils').fetchProjects as jest.Mock; beforeEach(() => { - (get as jest.Mock<any>).mockImplementation(() => null); - (save as jest.Mock<any>).mockClear(); + (get as jest.Mock).mockImplementation(() => null); + (save as jest.Mock).mockClear(); + (isSonarCloud as jest.Mock).mockReturnValue(false); fetchProjects.mockClear(); }); @@ -100,7 +105,7 @@ it('fetches projects', () => { }); it('redirects to the saved search', () => { - (get as jest.Mock<any>).mockImplementation( + (get as jest.Mock).mockImplementation( (key: string) => (key === 'sonarqube.projects.view' ? 'leak' : null) ); const replace = jest.fn(); @@ -161,6 +166,28 @@ it('changes perspective to risk visualization', () => { expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', 'risk', undefined); }); +it('renders correctly empty organization', async () => { + (isSonarCloud as jest.Mock).mockReturnValue(true); + const wrapper = shallow( + <AllProjects + currentUser={{ isLoggedIn: true }} + isFavorite={false} + location={{ pathname: '/projects', query: {} }} + organization={{ key: 'foo', name: 'Foo' }} + router={{ push: jest.fn(), replace: jest.fn() }} + /> + ); + expect(wrapper).toMatchSnapshot(); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + wrapper.setState({ + loading: false, + projects: [{ key: 'foo', measures: {}, name: 'Foo' }], + total: 0 + }); + expect(wrapper).toMatchSnapshot(); +}); + function shallowRender( props: Partial<AllProjects['props']> = {}, push = jest.fn(), @@ -172,7 +199,6 @@ function shallowRender( isFavorite={false} location={{ pathname: '/projects', query: {} }} organization={undefined} - organizationsEnabled={false} router={{ push, replace }} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap index e0873b4d900..1c09c537190 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap @@ -231,3 +231,203 @@ exports[`renders 2`] = ` </div> </div> `; + +exports[`renders correctly empty organization 1`] = ` +<div + className="layout-page projects-page" + id="projects-page" +> + <Suggestions + suggestions="projects" + /> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="projects.page" + /> + <div + className="display-flex-space-around width-100 huge-spacer-top" + > + <DeferredSpinner + timeout={100} + /> + </div> +</div> +`; + +exports[`renders correctly empty organization 2`] = ` +<div + className="layout-page projects-page" + id="projects-page" +> + <Suggestions + suggestions="projects" + /> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="projects.page" + /> + <div + className="layout-page-main" + > + <ContextConsumer> + <Component /> + </ContextConsumer> + </div> +</div> +`; + +exports[`renders correctly empty organization 3`] = ` +<div + className="layout-page projects-page" + id="projects-page" +> + <Suggestions + suggestions="projects" + /> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="projects.page" + /> + <ScreenPositionHelper + className="layout-page-side-outer" + > + <Component /> + </ScreenPositionHelper> + <div + className="layout-page-main" + > + <div + className="layout-page-header-panel layout-page-main-header" + > + <div + className="layout-page-header-panel-inner layout-page-main-header-inner" + > + <div + className="layout-page-main-inner" + > + <PageHeader + currentUser={ + Object { + "isLoggedIn": true, + } + } + isFavorite={false} + loading={false} + onPerspectiveChange={[Function]} + onQueryChange={[Function]} + onSortChange={[Function]} + organization={ + Object { + "key": "foo", + "name": "Foo", + } + } + projects={ + Array [ + Object { + "key": "foo", + "measures": Object {}, + "name": "Foo", + }, + ] + } + query={ + Object { + "coverage": undefined, + "duplications": undefined, + "gate": undefined, + "languages": undefined, + "maintainability": undefined, + "new_coverage": undefined, + "new_duplications": undefined, + "new_lines": undefined, + "new_maintainability": undefined, + "new_reliability": undefined, + "new_security": undefined, + "reliability": undefined, + "search": undefined, + "security": undefined, + "size": undefined, + "sort": undefined, + "tags": undefined, + "view": undefined, + "visualization": undefined, + } + } + selectedSort="name" + total={0} + view="overall" + visualization="risk" + /> + </div> + </div> + </div> + <DeferredSpinner + loading={false} + timeout={100} + > + <div + className="layout-page-main-inner" + > + <ProjectsList + cardType="overall" + currentUser={ + Object { + "isLoggedIn": true, + } + } + isFavorite={false} + isFiltered={false} + organization={ + Object { + "key": "foo", + "name": "Foo", + } + } + projects={ + Array [ + Object { + "key": "foo", + "measures": Object {}, + "name": "Foo", + }, + ] + } + query={ + Object { + "coverage": undefined, + "duplications": undefined, + "gate": undefined, + "languages": undefined, + "maintainability": undefined, + "new_coverage": undefined, + "new_duplications": undefined, + "new_lines": undefined, + "new_maintainability": undefined, + "new_reliability": undefined, + "new_security": undefined, + "reliability": undefined, + "search": undefined, + "security": undefined, + "size": undefined, + "sort": undefined, + "tags": undefined, + "view": undefined, + "visualization": undefined, + } + } + /> + <ListFooter + count={1} + loadMore={[Function]} + ready={true} + total={0} + /> + </div> + </DeferredSpinner> + </div> +</div> +`; |