import { Link } from 'react-router';
import { translate } from '../../helpers/l10n';
-export default class ComponentContainerNotFound extends React.PureComponent {
- componentDidMount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
- }
-
- componentWillUnmount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
- }
-
- render() {
- return (
- <div id="bd" className="page-wrapper-simple">
- <div id="nonav" className="page-simple">
- <h2 className="big-spacer-bottom">{translate('dashboard.project_not_found')}</h2>
- <p className="spacer-bottom">{translate('dashboard.project_not_found.2')}</p>
- <p>
- <Link to="/">Go back to the homepage</Link>
- </p>
- </div>
+export default function ComponentContainerNotFound() {
+ return (
+ <div id="bd" className="page-wrapper-simple">
+ <div id="nonav" className="page-simple">
+ <h2 className="big-spacer-bottom">{translate('dashboard.project_not_found')}</h2>
+ <p className="spacer-bottom">{translate('dashboard.project_not_found.2')}</p>
+ <p>
+ <Link to="/">Go back to the homepage</Link>
+ </p>
</div>
- );
- }
+ </div>
+ );
}
hideLoggedInInfo?: boolean;
}
-export default class SimpleContainer extends React.PureComponent<Props> {
- componentDidMount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
- }
+export default function SimpleContainer(props: Props) {
+ return (
+ <div className="global-container">
+ <div className="page-wrapper" id="container">
+ <NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
- componentWillUnmount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
- }
-
- render() {
- return (
- <div className="global-container">
- <div className="page-wrapper" id="container">
- <NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
-
- <div id="bd" className="page-wrapper-simple">
- <div id="nonav" className="page-simple">
- {this.props.children}
- </div>
+ <div id="bd" className="page-wrapper-simple">
+ <div id="nonav" className="page-simple">
+ {props.children}
</div>
</div>
- <GlobalFooterContainer hideLoggedInInfo={this.props.hideLoggedInInfo} />
</div>
- );
- }
+ <GlobalFooterContainer hideLoggedInInfo={props.hideLoggedInInfo} />
+ </div>
+ );
}
import * as React from 'react';
import { Link } from 'react-router';
-export default class ExtensionNotFound extends React.PureComponent {
- componentDidMount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
- }
-
- componentWillUnmount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
- }
-
- render() {
- return (
- <div id="bd" className="page-wrapper-simple">
- <div id="nonav" className="page-simple">
- <h2 className="big-spacer-bottom">The page you were looking for does not exist.</h2>
- <p className="spacer-bottom">
- You may have mistyped the address or the page may have moved.
- </p>
- <p>
- <Link to="/">Go back to the homepage</Link>
- </p>
- </div>
+export default function ExtensionNotFound() {
+ return (
+ <div id="bd" className="page-wrapper-simple">
+ <div id="nonav" className="page-simple">
+ <h2 className="big-spacer-bottom">The page you were looking for does not exist.</h2>
+ <p className="spacer-bottom">
+ You may have mistyped the address or the page may have moved.
+ </p>
+ <p>
+ <Link to="/">Go back to the homepage</Link>
+ </p>
</div>
- );
- }
+ </div>
+ );
}
.navbar-context-branches {
- display: inline-block;
- vertical-align: top;
- padding: var(--gridSize) 0;
+ display: inline-flex;
+ justify-content: center;
+ line-height: calc(2 * var(--gridSize));
margin-left: calc(2 * var(--gridSize));
- line-height: 16px;
+ font-size: var(--baseFontSize);
}
.navbar-context-meta-branch-menu-item {
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import ComponentNavBranch from './ComponentNavBranch';
-import ComponentNavBreadcrumbs from './ComponentNavBreadcrumbs';
+import ComponentNavHeader from './ComponentNavHeader';
import ComponentNavMeta from './ComponentNavMeta';
import ComponentNavMenu from './ComponentNavMenu';
import ComponentNavBgTaskNotif from './ComponentNavBgTaskNotif';
id="context-navigation"
height={notifComponent ? theme.contextNavHeightRaw + 20 : theme.contextNavHeightRaw}
notif={notifComponent}>
- <ComponentNavBreadcrumbs component={this.props.component} />
- {this.props.currentBranch && (
- <ComponentNavBranch
- branches={this.props.branches}
- component={this.props.component}
- currentBranch={this.props.currentBranch}
- // to close dropdown on any location change
- location={this.props.location}
- />
- )}
+ <ComponentNavHeader
+ branches={this.props.branches}
+ component={this.props.component}
+ currentBranch={this.props.currentBranch}
+ // to close dropdown on any location change
+ location={this.props.location}
+ />
<ComponentNavMeta branch={this.props.currentBranch} component={this.props.component} />
<ComponentNavMenu
branch={this.props.currentBranch}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { connect } from 'react-redux';
-import { Link } from 'react-router';
-import { Component, Organization } from '../../../types';
-import QualifierIcon from '../../../../components/shared/QualifierIcon';
-import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
-import OrganizationAvatar from '../../../../components/common/OrganizationAvatar';
-import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
-import OrganizationLink from '../../../../components/ui/OrganizationLink';
-import PrivateBadge from '../../../../components/common/PrivateBadge';
-import { collapsePath, limitComponentName } from '../../../../helpers/path';
-import { getProjectUrl } from '../../../../helpers/urls';
-
-interface StateProps {
- organization?: Organization;
- shouldOrganizationBeDisplayed: boolean;
-}
-
-interface OwnProps {
- component: Component;
-}
-
-interface Props extends StateProps, OwnProps {}
-
-export function ComponentNavBreadcrumbs(props: Props) {
- const { component, organization, shouldOrganizationBeDisplayed } = props;
- const { breadcrumbs } = component;
-
- const lastItem = breadcrumbs[breadcrumbs.length - 1];
-
- const items: JSX.Element[] = [];
- breadcrumbs.forEach((item, index) => {
- const isPath = item.qualifier === 'DIR';
- const itemName = isPath ? collapsePath(item.name, 15) : limitComponentName(item.name);
-
- if (index === 0) {
- items.push(
- <QualifierIcon
- className="spacer-right"
- key={`qualifier-${item.key}`}
- qualifier={lastItem.qualifier}
- />
- );
- }
-
- items.push(
- <Link
- className="link-base-color link-no-underline"
- key={`name-${item.key}`}
- title={item.name}
- to={getProjectUrl(item.key)}>
- {itemName}
- </Link>
- );
-
- if (index < breadcrumbs.length - 1) {
- items.push(<span className="slash-separator" key={`separator-${item.key}`} />);
- }
- });
-
- return (
- <header className="navbar-context-header">
- <OrganizationHelmet
- title={component.name}
- organization={organization && shouldOrganizationBeDisplayed ? organization : undefined}
- />
- {organization &&
- shouldOrganizationBeDisplayed && <OrganizationAvatar organization={organization} />}
- {organization &&
- shouldOrganizationBeDisplayed && (
- <OrganizationLink
- organization={organization}
- className="link-base-color link-no-underline spacer-left">
- {organization.name}
- </OrganizationLink>
- )}
- {organization && shouldOrganizationBeDisplayed && <span className="slash-separator" />}
- {items}
- {component.visibility === 'private' && (
- <PrivateBadge className="spacer-left" qualifier={component.qualifier} />
- )}
- </header>
- );
-}
-
-const mapStateToProps = (state: any, ownProps: OwnProps): StateProps => ({
- organization:
- ownProps.component.organization && getOrganizationByKey(state, ownProps.component.organization),
- shouldOrganizationBeDisplayed: areThereCustomOrganizations(state)
-});
-
-export default connect(mapStateToProps)(ComponentNavBreadcrumbs);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Link } from 'react-router';
+import ComponentNavBranch from './ComponentNavBranch';
+import { Component, Organization, Branch, Breadcrumb } from '../../../types';
+import QualifierIcon from '../../../../components/shared/QualifierIcon';
+import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
+import OrganizationAvatar from '../../../../components/common/OrganizationAvatar';
+import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
+import OrganizationLink from '../../../../components/ui/OrganizationLink';
+import PrivateBadge from '../../../../components/common/PrivateBadge';
+import { collapsePath, limitComponentName } from '../../../../helpers/path';
+import { getProjectUrl } from '../../../../helpers/urls';
+
+interface StateProps {
+ organization?: Organization;
+ shouldOrganizationBeDisplayed: boolean;
+}
+
+interface OwnProps {
+ branches: Branch[];
+ component: Component;
+ currentBranch?: Branch;
+ location?: any;
+}
+
+interface Props extends StateProps, OwnProps {}
+
+export function ComponentNavHeader(props: Props) {
+ const { component, organization, shouldOrganizationBeDisplayed } = props;
+
+ return (
+ <header className="navbar-context-header">
+ <OrganizationHelmet
+ title={component.name}
+ organization={organization && shouldOrganizationBeDisplayed ? organization : undefined}
+ />
+ {organization &&
+ shouldOrganizationBeDisplayed && (
+ <>
+ <OrganizationAvatar organization={organization} />
+ <OrganizationLink
+ organization={organization}
+ className="link-base-color link-no-underline spacer-left">
+ {organization.name}
+ </OrganizationLink>
+ <span className="slash-separator" />
+ </>
+ )}
+ {renderBreadcrumbs(component.breadcrumbs)}
+ {component.visibility === 'private' && (
+ <PrivateBadge className="spacer-left" qualifier={component.qualifier} />
+ )}
+ {props.currentBranch && (
+ <ComponentNavBranch
+ branches={props.branches}
+ component={component}
+ currentBranch={props.currentBranch}
+ // to close dropdown on any location change
+ location={props.location}
+ />
+ )}
+ </header>
+ );
+}
+
+function renderBreadcrumbs(breadcrumbs: Breadcrumb[]) {
+ const lastItem = breadcrumbs[breadcrumbs.length - 1];
+ return breadcrumbs.map((item, index) => {
+ const isPath = item.qualifier === 'DIR';
+ const itemName = isPath ? collapsePath(item.name, 15) : limitComponentName(item.name);
+
+ return (
+ <React.Fragment key={item.key}>
+ {index === 0 && <QualifierIcon className="spacer-right" qualifier={lastItem.qualifier} />}
+ <Link
+ className="link-base-color link-no-underline"
+ title={item.name}
+ to={getProjectUrl(item.key)}>
+ {itemName}
+ </Link>
+ {index < breadcrumbs.length - 1 && <span className="slash-separator" />}
+ </React.Fragment>
+ );
+ });
+}
+
+const mapStateToProps = (state: any, ownProps: OwnProps): StateProps => ({
+ organization:
+ ownProps.component.organization && getOrganizationByKey(state, ownProps.component.organization),
+ shouldOrganizationBeDisplayed: areThereCustomOrganizations(state)
+});
+
+export default connect(mapStateToProps)(ComponentNavHeader);
*/
import * as React from 'react';
import { connect } from 'react-redux';
-import { Branch, Component, CurrentUser, isLoggedIn } from '../../../types';
+import { Branch, Component, CurrentUser, isLoggedIn, HomePageType } from '../../../types';
import BranchStatus from '../../../../components/common/BranchStatus';
import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
import Favorite from '../../../../components/controls/Favorite';
{isLoggedIn(currentUser) &&
mainBranch && (
<div className="navbar-context-meta-secondary">
- <Favorite component={component.key} favorite={Boolean(component.isFavorite)} />
+ <Favorite
+ component={component.key}
+ favorite={Boolean(component.isFavorite)}
+ qualifier={component.qualifier}
+ />
<HomePageSelect
className="spacer-left"
- currentPage={{ type: 'project', key: component.key }}
+ currentPage={{ type: HomePageType.Project, parameter: component.key }}
/>
</div>
)}
- {shortBranch && <BranchStatus branch={branch!} />}
+ {shortBranch && (
+ <div className="navbar-context-meta-secondary">
+ <BranchStatus branch={branch!} />
+ </div>
+ )}
</div>
);
}
}
}));
-jest.mock('../ComponentNavBreadcrumbs', () => ({
+jest.mock('../ComponentNavHeader', () => ({
// eslint-disable-next-line
- default: function ComponentNavBreadcrumbs() {
+ default: function ComponentNavHeader() {
return null;
}
}));
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import { ComponentNavBreadcrumbs } from '../ComponentNavBreadcrumbs';
-import { Visibility } from '../../../../types';
-
-it('should not render breadcrumbs with one element', () => {
- const component = {
- breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
- key: 'my-project',
- name: 'My Project',
- organization: 'org',
- qualifier: 'TRK',
- visibility: 'public'
- };
- const result = shallow(
- <ComponentNavBreadcrumbs component={component} shouldOrganizationBeDisplayed={false} />
- );
- expect(result).toMatchSnapshot();
-});
-
-it('should render organization', () => {
- const component = {
- breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
- key: 'my-project',
- name: 'My Project',
- organization: 'foo',
- qualifier: 'TRK',
- visibility: 'public'
- };
- const organization = {
- key: 'foo',
- name: 'The Foo Organization',
- projectVisibility: Visibility.Public
- };
- const result = shallow(
- <ComponentNavBreadcrumbs
- component={component}
- organization={organization}
- shouldOrganizationBeDisplayed={true}
- />
- );
- expect(result).toMatchSnapshot();
-});
-
-it('renders private badge', () => {
- const component = {
- breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
- key: 'my-project',
- name: 'My Project',
- organization: 'org',
- qualifier: 'TRK',
- visibility: 'private'
- };
- const result = shallow(
- <ComponentNavBreadcrumbs component={component} shouldOrganizationBeDisplayed={false} />
- );
- expect(result.find('PrivateBadge')).toHaveLength(1);
-});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import { ComponentNavHeader } from '../ComponentNavHeader';
+import { Visibility } from '../../../../types';
+
+it('should not render breadcrumbs with one element', () => {
+ const component = {
+ breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
+ key: 'my-project',
+ name: 'My Project',
+ organization: 'org',
+ qualifier: 'TRK',
+ visibility: 'public'
+ };
+ const result = shallow(
+ <ComponentNavHeader branches={[]} component={component} shouldOrganizationBeDisplayed={false} />
+ );
+ expect(result).toMatchSnapshot();
+});
+
+it('should render organization', () => {
+ const component = {
+ breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
+ key: 'my-project',
+ name: 'My Project',
+ organization: 'foo',
+ qualifier: 'TRK',
+ visibility: 'public'
+ };
+ const organization = {
+ key: 'foo',
+ name: 'The Foo Organization',
+ projectVisibility: Visibility.Public
+ };
+ const result = shallow(
+ <ComponentNavHeader
+ branches={[]}
+ component={component}
+ organization={organization}
+ shouldOrganizationBeDisplayed={true}
+ />
+ );
+ expect(result).toMatchSnapshot();
+});
+
+it('renders private badge', () => {
+ const component = {
+ breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
+ key: 'my-project',
+ name: 'My Project',
+ organization: 'org',
+ qualifier: 'TRK',
+ visibility: 'private'
+ };
+ const result = shallow(
+ <ComponentNavHeader branches={[]} component={component} shouldOrganizationBeDisplayed={false} />
+ );
+ expect(result.find('PrivateBadge')).toHaveLength(1);
+});
/>
}
>
- <ComponentNavBreadcrumbs
+ <ComponentNavHeader
+ branches={Array []}
component={
Object {
"breadcrumbs": Array [
"qualifier": "TRK",
}
}
+ location={Object {}}
/>
<ComponentNavMeta
component={
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should not render breadcrumbs with one element 1`] = `
-<header
- className="navbar-context-header"
->
- <OrganizationHelmet
- title="My Project"
- />
- <QualifierIcon
- className="spacer-right"
- key="qualifier-my-project"
- qualifier="TRK"
- />
- <Link
- className="link-base-color link-no-underline"
- key="name-my-project"
- onlyActiveOnIndex={false}
- style={Object {}}
- title="My Project"
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "my-project",
- },
- }
- }
- >
- My Project
- </Link>
-</header>
-`;
-
-exports[`should render organization 1`] = `
-<header
- className="navbar-context-header"
->
- <OrganizationHelmet
- organization={
- Object {
- "key": "foo",
- "name": "The Foo Organization",
- "projectVisibility": "public",
- }
- }
- title="My Project"
- />
- <OrganizationAvatar
- organization={
- Object {
- "key": "foo",
- "name": "The Foo Organization",
- "projectVisibility": "public",
- }
- }
- />
- <OrganizationLink
- className="link-base-color link-no-underline spacer-left"
- organization={
- Object {
- "key": "foo",
- "name": "The Foo Organization",
- "projectVisibility": "public",
- }
- }
- >
- The Foo Organization
- </OrganizationLink>
- <span
- className="slash-separator"
- />
- <QualifierIcon
- className="spacer-right"
- key="qualifier-my-project"
- qualifier="TRK"
- />
- <Link
- className="link-base-color link-no-underline"
- key="name-my-project"
- onlyActiveOnIndex={false}
- style={Object {}}
- title="My Project"
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "my-project",
- },
- }
- }
- >
- My Project
- </Link>
-</header>
-`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should not render breadcrumbs with one element 1`] = `
+<header
+ className="navbar-context-header"
+>
+ <OrganizationHelmet
+ title="My Project"
+ />
+ <React.Fragment
+ key="my-project"
+ >
+ <QualifierIcon
+ className="spacer-right"
+ qualifier="TRK"
+ />
+ <Link
+ className="link-base-color link-no-underline"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ title="My Project"
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "my-project",
+ },
+ }
+ }
+ >
+ My Project
+ </Link>
+ </React.Fragment>
+</header>
+`;
+
+exports[`should render organization 1`] = `
+<header
+ className="navbar-context-header"
+>
+ <OrganizationHelmet
+ organization={
+ Object {
+ "key": "foo",
+ "name": "The Foo Organization",
+ "projectVisibility": "public",
+ }
+ }
+ title="My Project"
+ />
+ <React.Fragment>
+ <OrganizationAvatar
+ organization={
+ Object {
+ "key": "foo",
+ "name": "The Foo Organization",
+ "projectVisibility": "public",
+ }
+ }
+ />
+ <OrganizationLink
+ className="link-base-color link-no-underline spacer-left"
+ organization={
+ Object {
+ "key": "foo",
+ "name": "The Foo Organization",
+ "projectVisibility": "public",
+ }
+ }
+ >
+ The Foo Organization
+ </OrganizationLink>
+ <span
+ className="slash-separator"
+ />
+ </React.Fragment>
+ <React.Fragment
+ key="my-project"
+ >
+ <QualifierIcon
+ className="spacer-right"
+ qualifier="TRK"
+ />
+ <Link
+ className="link-base-color link-no-underline"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ title="My Project"
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "my-project",
+ },
+ }
+ }
+ >
+ My Project
+ </Link>
+ </React.Fragment>
+</header>
+`;
date="2017-01-02T00:00:00.000Z"
/>
</div>
- <BranchStatus
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "master",
- "name": "feature",
- "status": Object {
- "bugs": 0,
- "codeSmells": 2,
- "vulnerabilities": 3,
- },
- "type": "SHORT",
+ <div
+ className="navbar-context-meta-secondary"
+ >
+ <BranchStatus
+ branch={
+ Object {
+ "isMain": false,
+ "mergeBranch": "master",
+ "name": "feature",
+ "status": Object {
+ "bugs": 0,
+ "codeSmells": 2,
+ "vulnerabilities": 3,
+ },
+ "type": "SHORT",
+ }
}
- }
- />
+ />
+ </div>
</div>
`;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+.white-page {
+ background-color: #fff !important;
+}
+
.global-container {
display: flex;
flex-direction: column;
padding-left: calc(50vw - 370px + 10px) !important;
}
-.page-footer-with-sidebar div {
+.page-footer-with-sidebar div,
+.page-footer-with-sidebar .page-footer-menu {
max-width: 980px;
}
html,
body {
- background-color: #fff;
+ background-color: var(--barBackgroundColor);
}
body {
stroke-width: 1.41421356;
stroke-opacity: 1;
fill-opacity: 0;
+ vector-effect: non-scaling-stroke;
transition: all 0.2s ease;
}
display: none !important;
}
- .dashboard-page,
- .dashboard-page body {
- background-color: #fff;
+ html,
+ body {
+ background-color: #fff !important;
}
-
.widget thead,
.widget tfoot {
display: table-row-group;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.dashboard-page,
-.dashboard-page body {
- background-color: var(--barBackgroundColor);
-}
-
.tabs {
height: 20px;
border-bottom: 1px solid #ddd;
name: string;
}
+export interface Breadcrumb {
+ key: string;
+ name: string;
+ qualifier: string;
+}
+
export interface Component {
analysisDate?: string;
- breadcrumbs: Array<{
- key: string;
- name: string;
- qualifier: string;
- }>;
+ breadcrumbs: Breadcrumb[];
configuration?: ComponentConfiguration;
description?: string;
extensions?: Extension[];
showOnboardingTutorial?: boolean;
}
+export enum HomePageType {
+ Project = 'PROJECT',
+ Organization = 'ORGANIZATION',
+ MyProjects = 'MY_PROJECTS',
+ MyIssues = 'MY_ISSUES'
+}
+
export interface HomePage {
- key?: string;
- type: string;
+ parameter?: string;
+ type: HomePageType;
}
export function isSameHomePage(a: HomePage, b: HomePage) {
- return a.type === b.type && a.key === b.key;
+ return a.type === b.type && a.parameter === b.parameter;
}
export interface LoggedInUser extends CurrentUser {
window.location = 'https://about.sonarcloud.io';
} else {
this.loadData();
+ // $FlowFixMe
+ document.body.classList.add('white-page');
}
}
componentWillUnmount() {
this.mounted = false;
+ // $FlowFixMe
+ document.body.classList.remove('white-page');
}
loadProjects() {
padding-top: 20px;
padding-bottom: 20px;
border-bottom: 1px solid var(--barBorderColor);
- background-color: var(--barBackgroundColor);
+ background-color: #fff;
}
.account-nav {
const { success, errors } = this.state;
return (
- <section>
+ <section className="boxed-group">
<h2 className="spacer-bottom">{translate('my_profile.password.title')}</h2>
- <form onSubmit={this.handleChangePassword}>
+ <form className="boxed-group-inner" onSubmit={this.handleChangePassword}>
{success && (
<div className="alert alert-success">{translate('my_profile.password.changed')}</div>
)}
return (
<div className="account-body account-container">
<Helmet title={translate('my_account.security')} />
-
<Tokens user={user} />
-
- {user.local && <hr className="account-separator" />}
-
{user.local && <Password user={user} />}
</div>
);
function GlobalNotifications(props /*: Props */) {
return (
- <section>
- <h2 className="spacer-bottom">{translate('my_profile.overall_notifications.title')}</h2>
+ <section className="boxed-group">
+ <h2>{translate('my_profile.overall_notifications.title')}</h2>
- <table className="form">
- <thead>
- <tr>
- <th />
- {props.channels.map(channel => (
- <th key={channel} className="text-center">
- <h4>{translate('notification.channel', channel)}</h4>
- </th>
- ))}
- </tr>
- </thead>
+ <div className="boxed-group-inner">
+ <table className="form">
+ <thead>
+ <tr>
+ <th />
+ {props.channels.map(channel => (
+ <th key={channel} className="text-center">
+ <h4>{translate('notification.channel', channel)}</h4>
+ </th>
+ ))}
+ </tr>
+ </thead>
- <NotificationsList
- notifications={props.notifications}
- channels={props.channels}
- types={props.types}
- checkboxId={(d, c) => `global-notification-${d}-${c}`}
- onAdd={props.addNotification}
- onRemove={props.removeNotification}
- />
- </table>
+ <NotificationsList
+ notifications={props.notifications}
+ channels={props.channels}
+ types={props.types}
+ checkboxId={(d, c) => `global-notification-${d}-${c}`}
+ onAdd={props.addNotification}
+ onRemove={props.removeNotification}
+ />
+ </table>
+ </div>
</section>
);
}
return (
<div className="account-body account-container">
<Helmet title={translate('my_account.notifications')} />
-
- <p className="big-spacer-bottom">{translate('notification.dispatcher.information')}</p>
-
+ <p className="alert alert-info">{translate('notification.dispatcher.information')}</p>
<GlobalNotifications />
-
- <hr className="account-separator" />
-
<Projects />
</div>
);
const allProjects = [...this.props.projects, ...this.state.addedProjects];
return (
- <section>
- <h2 className="spacer-bottom">{translate('my_profile.per_project_notifications.title')}</h2>
-
- {allProjects.length === 0 && (
- <div className="note">{translate('my_account.no_project_notifications')}</div>
- )}
-
- {allProjects.map(project => <ProjectNotifications key={project.key} project={project} />)}
-
- <div className="spacer-top panel bg-muted">
- <span className="text-middle spacer-right">
- {translate('my_account.set_notifications_for')}:
- </span>
- <AsyncSelect
- autoload={false}
- cache={false}
- name="new_project"
- style={{ width: '300px' }}
- loadOptions={this.loadOptions}
- minimumInput={2}
- optionRenderer={this.renderOption}
- onChange={this.handleAddProject}
- placeholder={translate('my_account.search_project')}
- />
+ <section className="boxed-group">
+ <h2>{translate('my_profile.per_project_notifications.title')}</h2>
+
+ <div className="boxed-group-inner">
+ {allProjects.length === 0 && (
+ <div className="note">{translate('my_account.no_project_notifications')}</div>
+ )}
+
+ {allProjects.map(project => <ProjectNotifications key={project.key} project={project} />)}
+
+ <div className="spacer-top panel bg-muted">
+ <span className="text-middle spacer-right">
+ {translate('my_account.set_notifications_for')}:
+ </span>
+ <AsyncSelect
+ autoload={false}
+ cache={false}
+ name="new_project"
+ style={{ width: '300px' }}
+ loadOptions={this.loadOptions}
+ minimumInput={2}
+ optionRenderer={this.renderOption}
+ onChange={this.handleAddProject}
+ placeholder={translate('my_account.search_project')}
+ />
+ </div>
</div>
</section>
);
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should match snapshot 1`] = `
-<section>
- <h2
- className="spacer-bottom"
- >
+<section
+ className="boxed-group"
+>
+ <h2>
my_profile.overall_notifications.title
</h2>
- <table
- className="form"
+ <div
+ className="boxed-group-inner"
>
- <thead>
- <tr>
- <th />
- <th
- className="text-center"
- key="channel1"
- >
- <h4>
- notification.channel.channel1
- </h4>
- </th>
- <th
- className="text-center"
- key="channel2"
- >
- <h4>
- notification.channel.channel2
- </h4>
- </th>
- </tr>
- </thead>
- <NotificationsList
- channels={
- Array [
- "channel1",
- "channel2",
- ]
- }
- checkboxId={[Function]}
- notifications={
- Array [
- Object {
- "channel": "channel1",
- "type": "type1",
- },
- Object {
- "channel": "channel1",
- "type": "type2",
- },
- Object {
- "channel": "channel2",
- "type": "type2",
- },
- ]
- }
- onAdd={[Function]}
- onRemove={[Function]}
- types={
- Array [
- "type1",
- "type2",
- ]
- }
- />
- </table>
+ <table
+ className="form"
+ >
+ <thead>
+ <tr>
+ <th />
+ <th
+ className="text-center"
+ key="channel1"
+ >
+ <h4>
+ notification.channel.channel1
+ </h4>
+ </th>
+ <th
+ className="text-center"
+ key="channel2"
+ >
+ <h4>
+ notification.channel.channel2
+ </h4>
+ </th>
+ </tr>
+ </thead>
+ <NotificationsList
+ channels={
+ Array [
+ "channel1",
+ "channel2",
+ ]
+ }
+ checkboxId={[Function]}
+ notifications={
+ Array [
+ Object {
+ "channel": "channel1",
+ "type": "type1",
+ },
+ Object {
+ "channel": "channel1",
+ "type": "type2",
+ },
+ Object {
+ "channel": "channel2",
+ "type": "type2",
+ },
+ ]
+ }
+ onAdd={[Function]}
+ onRemove={[Function]}
+ types={
+ Array [
+ "type1",
+ "type2",
+ ]
+ }
+ />
+ </table>
+ </div>
</section>
`;
title="my_account.notifications"
/>
<p
- className="big-spacer-bottom"
+ className="alert alert-info"
>
notification.dispatcher.information
</p>
<Connect(GlobalNotifications) />
- <hr
- className="account-separator"
- />
<Connect(Projects) />
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render projects 1`] = `
-<section>
- <h2
- className="spacer-bottom"
- >
+<section
+ className="boxed-group"
+>
+ <h2>
my_profile.per_project_notifications.title
</h2>
- <Connect(ProjectNotifications)
- key="foo"
- project={
- Object {
- "key": "foo",
- "name": "Foo",
- }
- }
- />
- <Connect(ProjectNotifications)
- key="bar"
- project={
- Object {
- "key": "bar",
- "name": "Bar",
- }
- }
- />
<div
- className="spacer-top panel bg-muted"
+ className="boxed-group-inner"
>
- <span
- className="text-middle spacer-right"
- >
- my_account.set_notifications_for
- :
- </span>
- <AsyncSelect
- autoload={false}
- cache={false}
- loadOptions={[Function]}
- minimumInput={2}
- name="new_project"
- onChange={[Function]}
- optionRenderer={[Function]}
- placeholder="my_account.search_project"
- style={
+ <Connect(ProjectNotifications)
+ key="foo"
+ project={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ />
+ <Connect(ProjectNotifications)
+ key="bar"
+ project={
Object {
- "width": "300px",
+ "key": "bar",
+ "name": "Bar",
}
}
/>
+ <div
+ className="spacer-top panel bg-muted"
+ >
+ <span
+ className="text-middle spacer-right"
+ >
+ my_account.set_notifications_for
+ :
+ </span>
+ <AsyncSelect
+ autoload={false}
+ cache={false}
+ loadOptions={[Function]}
+ minimumInput={2}
+ name="new_project"
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ placeholder="my_account.search_project"
+ style={
+ Object {
+ "width": "300px",
+ }
+ }
+ />
+ </div>
</div>
</section>
`;
exports[`should render projects 2`] = `
-<section>
- <h2
- className="spacer-bottom"
- >
+<section
+ className="boxed-group"
+>
+ <h2>
my_profile.per_project_notifications.title
</h2>
- <Connect(ProjectNotifications)
- key="foo"
- project={
- Object {
- "key": "foo",
- "name": "Foo",
- }
- }
- />
- <Connect(ProjectNotifications)
- key="bar"
- project={
- Object {
- "key": "bar",
- "name": "Bar",
- }
- }
- />
- <Connect(ProjectNotifications)
- key="qux"
- project={
- Object {
- "key": "qux",
- "name": "Qux",
- }
- }
- />
<div
- className="spacer-top panel bg-muted"
+ className="boxed-group-inner"
>
- <span
- className="text-middle spacer-right"
- >
- my_account.set_notifications_for
- :
- </span>
- <AsyncSelect
- autoload={false}
- cache={false}
- loadOptions={[Function]}
- minimumInput={2}
- name="new_project"
- onChange={[Function]}
- optionRenderer={[Function]}
- placeholder="my_account.search_project"
- style={
+ <Connect(ProjectNotifications)
+ key="foo"
+ project={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ />
+ <Connect(ProjectNotifications)
+ key="bar"
+ project={
+ Object {
+ "key": "bar",
+ "name": "Bar",
+ }
+ }
+ />
+ <Connect(ProjectNotifications)
+ key="qux"
+ project={
Object {
- "width": "300px",
+ "key": "qux",
+ "name": "Qux",
}
}
/>
+ <div
+ className="spacer-top panel bg-muted"
+ >
+ <span
+ className="text-middle spacer-right"
+ >
+ my_account.set_notifications_for
+ :
+ </span>
+ <AsyncSelect
+ autoload={false}
+ cache={false}
+ loadOptions={[Function]}
+ minimumInput={2}
+ name="new_project"
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ placeholder="my_account.search_project"
+ style={
+ Object {
+ "width": "300px",
+ }
+ }
+ />
+ </div>
</div>
</section>
`;
exports[`should render projects 3`] = `
-<section>
- <h2
- className="spacer-bottom"
- >
+<section
+ className="boxed-group"
+>
+ <h2>
my_profile.per_project_notifications.title
</h2>
- <Connect(ProjectNotifications)
- key="foo"
- project={
- Object {
- "key": "foo",
- "name": "Foo",
- }
- }
- />
- <Connect(ProjectNotifications)
- key="bar"
- project={
- Object {
- "key": "bar",
- "name": "Bar",
- }
- }
- />
- <Connect(ProjectNotifications)
- key="qux"
- project={
- Object {
- "key": "qux",
- "name": "Qux",
- }
- }
- />
<div
- className="spacer-top panel bg-muted"
+ className="boxed-group-inner"
>
- <span
- className="text-middle spacer-right"
- >
- my_account.set_notifications_for
- :
- </span>
- <AsyncSelect
- autoload={false}
- cache={false}
- loadOptions={[Function]}
- minimumInput={2}
- name="new_project"
- onChange={[Function]}
- optionRenderer={[Function]}
- placeholder="my_account.search_project"
- style={
+ <Connect(ProjectNotifications)
+ key="foo"
+ project={
Object {
- "width": "300px",
+ "key": "foo",
+ "name": "Foo",
}
}
/>
+ <Connect(ProjectNotifications)
+ key="bar"
+ project={
+ Object {
+ "key": "bar",
+ "name": "Bar",
+ }
+ }
+ />
+ <Connect(ProjectNotifications)
+ key="qux"
+ project={
+ Object {
+ "key": "qux",
+ "name": "Qux",
+ }
+ }
+ />
+ <div
+ className="spacer-top panel bg-muted"
+ >
+ <span
+ className="text-middle spacer-right"
+ >
+ my_account.set_notifications_for
+ :
+ </span>
+ <AsyncSelect
+ autoload={false}
+ cache={false}
+ loadOptions={[Function]}
+ minimumInput={2}
+ name="new_project"
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ placeholder="my_account.search_project"
+ style={
+ Object {
+ "width": "300px",
+ }
+ }
+ />
+ </div>
</div>
</section>
`;
import { sortBy } from 'lodash';
import OrganizationCard from './OrganizationCard';
import { Organization } from '../../../app/types';
+import { translate } from '../../../helpers/l10n';
interface Props {
organizations: Organization[];
}
export default function OrganizationsList({ organizations }: Props) {
+ if (organizations.length === 0) {
+ return <div>{translate('my_account.organizations.no_results')}</div>;
+ }
+
return (
<ul className="account-projects-list">
{sortBy(organizations, organization => organization.name.toLocaleLowerCase()).map(
<div className="account-body account-container">
<Helmet title={translate('my_account.organizations')} />
- <header className="page-header">
- <h2 className="page-title">{translate('my_account.organizations')}</h2>
+ <div className="boxed-group">
{canCreateOrganizations && (
- <div className="page-actions">
- <button onClick={this.handleCreateClick}>{translate('create')}</button>
+ <div className="clearfix">
+ <div className="boxed-group-actions">
+ <button onClick={this.handleCreateClick}>{translate('create')}</button>
+ </div>
</div>
)}
- {this.props.organizations.length > 0 ? (
- <div className="page-description">
- {translate('my_account.organizations.description')}
- </div>
- ) : (
- <div className="page-description">
- {translate('my_account.organizations.no_results')}
- </div>
- )}
- </header>
-
- {this.state.loading ? (
- <i className="spinner" />
- ) : (
- <OrganizationsList organizations={this.props.organizations} />
- )}
+ <div className="boxed-group-inner">
+ {this.state.loading ? (
+ <i className="spinner" />
+ ) : (
+ <OrganizationsList organizations={this.props.organizations} />
+ )}
+ </div>
+ </div>
{this.state.createOrganization && (
<CreateOrganizationForm
return (
<div className="account-body account-container">
- <div className="spacer-bottom">
- {translate('login')}: <strong id="login">{user.login}</strong>
- </div>
+ <div className="boxed-group boxed-group-inner">
+ <div className="spacer-bottom">
+ {translate('login')}: <strong id="login">{user.login}</strong>
+ </div>
+
+ {!user.local &&
+ user.externalProvider !== 'sonarqube' && (
+ <div id="identity-provider" className="spacer-bottom">
+ <UserExternalIdentity user={user} />
+ </div>
+ )}
- {!user.local &&
- user.externalProvider !== 'sonarqube' && (
- <div id="identity-provider" className="spacer-bottom">
- <UserExternalIdentity user={user} />
+ {!!user.email && (
+ <div className="spacer-bottom">
+ {translate('my_profile.email')}: <strong id="email">{user.email}</strong>
</div>
)}
- {!!user.email && (
- <div className="spacer-bottom">
- {translate('my_profile.email')}: <strong id="email">{user.email}</strong>
- </div>
- )}
+ {!customOrganizations && <hr className="account-separator" />}
+ {!customOrganizations && <UserGroups groups={user.groups} />}
- {!customOrganizations && <hr className="account-separator" />}
- {!customOrganizations && <UserGroups groups={user.groups} />}
+ <hr />
- <hr className="account-separator" />
-
- <UserScmAccounts user={user} scmAccounts={user.scmAccounts} />
+ <UserScmAccounts user={user} scmAccounts={user.scmAccounts} />
+ </div>
</div>
);
}
-<h2 class="spacer-bottom">{{t 'users.tokens'}}</h2>
+<div class="boxed-group">
+ <h2>{{t 'users.tokens'}}</h2>
-<div class="big-spacer-bottom big-spacer-right markdown">
- <p>{{t 'my_account.tokens_description'}}</p>
-</div>
+<div class="boxed-group-inner">
+ <div class="big-spacer-bottom big-spacer-right markdown">
+ <p>{{t 'my_account.tokens_description'}}</p>
+ </div>
-{{#notNull tokens}}
- <table class="data">
- <thead>
- <tr>
- <th>{{t 'name'}}</th>
- <th class="text-right">{{t 'created'}}</th>
- <th> </th>
- </tr>
- </thead>
- <tbody>
- {{#each tokens}}
- <tr>
- <td>
- <div title="{{name}}">
- {{limitString name}}
- </div>
- </td>
- <td class="thin nowrap text-right">
- {{d createdAt}}
- </td>
- <td class="thin nowrap text-right">
- <div class="big-spacer-left">
- <form class="js-revoke-token-form" data-token="{{name}}">
- {{#if deleting}}
- <button class="button-red active input-small">{{t 'users.tokens.sure'}}</button>
- {{else}}
- <button class="button-red input-small">{{t 'users.tokens.revoke'}}</button>
- {{/if}}
- </form>
- </div>
- </td>
- </tr>
- {{else}}
+ {{#notNull tokens}}
+ <table class="data">
+ <thead>
<tr>
- <td colspan="3">
- <span class="note">{{t 'users.no_tokens'}}</span>
- </td>
+ <th>{{t 'name'}}</th>
+ <th class="text-right">{{t 'created'}}</th>
+ <th> </th>
</tr>
- {{/each}}
- </tbody>
- </table>
-{{/notNull}}
+ </thead>
+ <tbody>
+ {{#each tokens}}
+ <tr>
+ <td>
+ <div title="{{name}}">
+ {{limitString name}}
+ </div>
+ </td>
+ <td class="thin nowrap text-right">
+ {{d createdAt}}
+ </td>
+ <td class="thin nowrap text-right">
+ <div class="big-spacer-left">
+ <form class="js-revoke-token-form" data-token="{{name}}">
+ {{#if deleting}}
+ <button class="button-red active input-small">{{t 'users.tokens.sure'}}</button>
+ {{else}}
+ <button class="button-red input-small">{{t 'users.tokens.revoke'}}</button>
+ {{/if}}
+ </form>
+ </div>
+ </td>
+ </tr>
+ {{else}}
+ <tr>
+ <td colspan="3">
+ <span class="note">{{t 'users.no_tokens'}}</span>
+ </td>
+ </tr>
+ {{/each}}
+ </tbody>
+ </table>
+ {{/notNull}}
-{{#each errors}}
- <div class="alert alert-danger">{{msg}}</div>
-{{/each}}
+ {{#each errors}}
+ <div class="alert alert-danger">{{msg}}</div>
+ {{/each}}
-<form class="js-generate-token-form spacer-top panel bg-muted">
- <label>{{t 'users.generate_new_token'}}:</label>
- <input type="text" required maxlength="100" placeholder="{{t 'users.enter_token_name'}}">
- <button>{{t 'users.generate'}}</button>
-</form>
+ <form class="js-generate-token-form spacer-top panel bg-muted">
+ <label>{{t 'users.generate_new_token'}}:</label>
+ <input type="text" required maxlength="100" placeholder="{{t 'users.enter_token_name'}}">
+ <button>{{t 'users.generate'}}</button>
+ </form>
-{{#if newToken}}
- <div class="panel panel-white big-spacer-top">
- <div class="alert alert-warning">
- {{tp 'users.tokens.new_token_created' newToken.name}}
- </div>
+ {{#if newToken}}
+ <div class="panel panel-white big-spacer-top">
+ <div class="alert alert-warning">
+ {{tp 'users.tokens.new_token_created' newToken.name}}
+ </div>
- <table class="data">
- <tr>
- <td class="thin">
- <button class="js-copy-to-clipboard" data-clipboard-text="{{newToken.token}}">{{t 'copy'}}</button>
- </td>
- <td class="nowrap">
- <div class="monospaced text-success">{{newToken.token}}</div>
- </td>
- </tr>
- </table>
- </div>
-{{/if}}
+ <table class="data">
+ <tr>
+ <td class="thin">
+ <button class="js-copy-to-clipboard" data-clipboard-text="{{newToken.token}}">{{t 'copy'}}</button>
+ </td>
+ <td class="nowrap">
+ <div class="monospaced text-success">{{newToken.token}}</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ {{/if}}
+</div>
+</div>
});
return (
- <table className={className}>
- <thead>
- <tr>
- <th>{translate('background_tasks.table.status')}</th>
- <th>{translate('background_tasks.table.task')}</th>
- <th>{translate('background_tasks.table.id')}</th>
- <th> </th>
- <th className="text-right">{translate('background_tasks.table.submitted')}</th>
- <th className="text-right">{translate('background_tasks.table.started')}</th>
- <th className="text-right">{translate('background_tasks.table.finished')}</th>
- <th className="text-right">{translate('background_tasks.table.duration')}</th>
- <th> </th>
- </tr>
- </thead>
- <tbody>
- {tasks.map((task, index, tasks) => (
- <Task
- key={task.id}
- task={task}
- tasks={tasks}
- component={component}
- onCancelTask={onCancelTask}
- onFilterTask={onFilterTask}
- previousTask={index > 0 ? tasks[index - 1] : undefined}
- />
- ))}
- </tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table className={className}>
+ <thead>
+ <tr>
+ <th>{translate('background_tasks.table.status')}</th>
+ <th>{translate('background_tasks.table.task')}</th>
+ <th>{translate('background_tasks.table.id')}</th>
+ <th> </th>
+ <th className="text-right">{translate('background_tasks.table.submitted')}</th>
+ <th className="text-right">{translate('background_tasks.table.started')}</th>
+ <th className="text-right">{translate('background_tasks.table.finished')}</th>
+ <th className="text-right">{translate('background_tasks.table.duration')}</th>
+ <th> </th>
+ </tr>
+ </thead>
+ <tbody>
+ {tasks.map((task, index, tasks) => (
+ <Task
+ key={task.id}
+ task={task}
+ tasks={tasks}
+ component={component}
+ onCancelTask={onCancelTask}
+ onFilterTask={onFilterTask}
+ previousTask={index > 0 ? tasks[index - 1] : undefined}
+ />
+ ))}
+ </tbody>
+ </table>
+ </div>
);
}
}
const shouldShowBreadcrumbs = breadcrumbs.length > 1;
- const componentsClassName = classNames('spacer-top', { 'new-loading': loading });
+ const componentsClassName = classNames('boxed-group', 'boxed-group-inner', 'spacer-top', {
+ 'new-loading': loading
+ });
return (
<div className="page page-limited">
{loading && <i className="spinner spacer-left" />}
{results != null && (
- <Components
- branch={this.props.branch}
- components={results}
- rootComponent={component}
- selected={selected}
- />
+ <div className="boxed-group boxed-group-inner spacer-top">
+ <Components
+ branch={this.props.branch}
+ components={results}
+ rootComponent={component}
+ selected={selected}
+ />
+ </div>
)}
</div>
);
*/
componentDidMount() {
+ // $FlowFixMe
+ document.body.classList.add('white-page');
+
if (this.props.appState.organizationsEnabled && !this.props.params.organizationKey) {
// redirect to organization-level rules page
this.props.router.replace(
}
componentWillUnmount() {
+ // $FlowFixMe
+ document.body.classList.remove('white-page');
+
if (this.stop) {
this.stop();
}
componentDidMount() {
this.mounted = true;
+ // $FlowFixMe
+ document.body.classList.add('white-page');
this.props.fetchMetrics();
this.fetchMeasures(this.props);
key.setScope('measures-files');
componentWillUnmount() {
this.mounted = false;
+ // $FlowFixMe
+ document.body.classList.remove('white-page');
key.deleteScope('measures-files');
const footer = document.getElementById('footer');
if (footer) {
-<table class="data zebra">
- <thead>
- <tr>
- <th>{{t 'custom_measures.metric'}}</th>
- <th>{{t 'value'}}</th>
- <th>{{t 'description'}}</th>
- <th>{{t 'date'}}</th>
- <th> </th>
- </tr>
- </thead>
- <tbody></tbody>
-</table>
+<div class="boxed-group boxed-group-inner">
+ <table class="data zebra">
+ <thead>
+ <tr>
+ <th>{{t 'custom_measures.metric'}}</th>
+ <th>{{t 'value'}}</th>
+ <th>{{t 'description'}}</th>
+ <th>{{t 'date'}}</th>
+ <th> </th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+</div>
-<div>
+<div class="boxed-group boxed-group-inner">
{{#isNull organization}}
<div class="panel panel-vertical js-anyone">
<div class="display-inline-block text-top width-20">
-<div class="panel panel-vertical bordered-bottom spacer-bottom">
+<div class="big-spacer-bottom">
<form id="groups-search-form" class="search-box">
<input id="groups-search-query" class="search-box-input" type="text" name="q" placeholder="{{t 'search.search_by_name'}}" maxlength="100">
<svg class="search-box-magnifier" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
return;
}
+ // $FlowFixMe
+ document.body.classList.add('white-page');
+
const footer = document.getElementById('footer');
if (footer) {
footer.classList.add('page-footer-with-sidebar');
componentWillUnmount() {
this.detachShortcuts();
+ // $FlowFixMe
+ document.body.classList.remove('white-page');
+
const footer = document.getElementById('footer');
if (footer) {
footer.classList.remove('page-footer-with-sidebar');
import IssuesCounter from './IssuesCounter';
import ReloadButton from './ReloadButton';
/*:: import type { Paging } from '../utils'; */
+import { HomePageType } from '../../../app/types';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import HomePageSelect from '../../../components/controls/HomePageSelect';
import { translate } from '../../../helpers/l10n';
</div>
{this.props.canSetHome && (
- <HomePageSelect className="huge-spacer-left" currentPage={{ type: 'my-issues' }} />
+ <HomePageSelect
+ className="huge-spacer-left"
+ currentPage={{ type: HomePageType.MyIssues }}
+ />
)}
</div>
);
render() {
return (
- <div id="marketplace-plugins">
+ <div className="boxed-group boxed-group-inner" id="marketplace-plugins">
<ul>
{this.props.plugins.map(plugin => (
<li key={plugin.key} className="panel panel-vertical">
}
];
return (
- <div id="marketplace-search" className="panel panel-vertical bordered-bottom spacer-bottom">
+ <div id="marketplace-search" className="big-spacer-bottom">
<div className="display-inline-block text-top nowrap abs-width-150 spacer-right">
<RadioToggle
name="marketplace-filter"
display: flex;
flex-direction: column;
justify-content: space-between;
- background-color: #f3f3f3;
margin-left: 8px;
margin-right: 8px;
}
<div class="page page-limited">
<div id="metrics-header"></div>
- <div id="metrics-list"></div>
+ <div id="metrics-list" class="boxed-group boxed-group-inner"></div>
<div id="metrics-list-footer"></div>
</div>
render() {
return (
- <table className="data zebra">
- <tbody>
- {this.props.members.map(member => (
- <MembersListItem
- key={member.login}
- member={member}
- organizationGroups={this.props.organizationGroups}
- organization={this.props.organization}
- removeMember={this.props.removeMember}
- updateMemberGroups={this.props.updateMemberGroups}
- />
- ))}
- </tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table className="data zebra">
+ <tbody>
+ {this.props.members.map(member => (
+ <MembersListItem
+ key={member.login}
+ member={member}
+ organizationGroups={this.props.organizationGroups}
+ organization={this.props.organization}
+ removeMember={this.props.removeMember}
+ updateMemberGroups={this.props.updateMemberGroups}
+ />
+ ))}
+ </tbody>
+ </table>
+ </div>
);
}
}
<h1 className="page-title">{title}</h1>
</header>
- <form onSubmit={this.handleSubmit}>
- <div className="modal-field">
- <label htmlFor="organization-name">
- {translate('organization.name')}
- <em className="mandatory">*</em>
- </label>
- <input
- id="organization-name"
- name="name"
- required={true}
- type="text"
- maxLength="64"
- value={this.state.name}
- disabled={this.state.loading}
- onChange={e => this.setState({ name: e.target.value })}
- />
- <div className="modal-field-description">
- {translate('organization.name.description')}
- </div>
- </div>
- <div className="modal-field">
- <label htmlFor="organization-avatar">{translate('organization.avatar')}</label>
- <input
- id="organization-avatar"
- name="avatar"
- type="text"
- maxLength="256"
- value={this.state.avatar}
- disabled={this.state.loading}
- onChange={this.handleAvatarInputChange}
- />
- <div className="modal-field-description">
- {translate('organization.avatar.description')}
+ <div className="boxed-group boxed-group-inner">
+ <form onSubmit={this.handleSubmit}>
+ <div className="modal-field">
+ <label htmlFor="organization-name">
+ {translate('organization.name')}
+ <em className="mandatory">*</em>
+ </label>
+ <input
+ id="organization-name"
+ name="name"
+ required={true}
+ type="text"
+ maxLength="64"
+ value={this.state.name}
+ disabled={this.state.loading}
+ onChange={e => this.setState({ name: e.target.value })}
+ />
+ <div className="modal-field-description">
+ {translate('organization.name.description')}
+ </div>
</div>
- {!!this.state.avatarImage && (
- <div className="spacer-top spacer-bottom">
- <div className="little-spacer-bottom">
- {translate('organization.avatar.preview')}
- {':'}
+ <div className="modal-field">
+ <label htmlFor="organization-avatar">{translate('organization.avatar')}</label>
+ <input
+ id="organization-avatar"
+ name="avatar"
+ type="text"
+ maxLength="256"
+ value={this.state.avatar}
+ disabled={this.state.loading}
+ onChange={this.handleAvatarInputChange}
+ />
+ <div className="modal-field-description">
+ {translate('organization.avatar.description')}
+ </div>
+ {!!this.state.avatarImage && (
+ <div className="spacer-top spacer-bottom">
+ <div className="little-spacer-bottom">
+ {translate('organization.avatar.preview')}
+ {':'}
+ </div>
+ <img src={this.state.avatarImage} alt="" height={30} />
</div>
- <img src={this.state.avatarImage} alt="" height={30} />
+ )}
+ </div>
+ <div className="modal-field">
+ <label htmlFor="organization-description">{translate('description')}</label>
+ <textarea
+ id="organization-description"
+ name="description"
+ rows="3"
+ maxLength="256"
+ value={this.state.description}
+ disabled={this.state.loading}
+ onChange={e => this.setState({ description: e.target.value })}
+ />
+ <div className="modal-field-description">
+ {translate('organization.description.description')}
+ </div>
+ </div>
+ <div className="modal-field">
+ <label htmlFor="organization-url">{translate('organization.url')}</label>
+ <input
+ id="organization-url"
+ name="url"
+ type="text"
+ maxLength="256"
+ value={this.state.url}
+ disabled={this.state.loading}
+ onChange={e => this.setState({ url: e.target.value })}
+ />
+ <div className="modal-field-description">
+ {translate('organization.url.description')}
</div>
- )}
- </div>
- <div className="modal-field">
- <label htmlFor="organization-description">{translate('description')}</label>
- <textarea
- id="organization-description"
- name="description"
- rows="3"
- maxLength="256"
- value={this.state.description}
- disabled={this.state.loading}
- onChange={e => this.setState({ description: e.target.value })}
- />
- <div className="modal-field-description">
- {translate('organization.description.description')}
</div>
- </div>
- <div className="modal-field">
- <label htmlFor="organization-url">{translate('organization.url')}</label>
- <input
- id="organization-url"
- name="url"
- type="text"
- maxLength="256"
- value={this.state.url}
- disabled={this.state.loading}
- onChange={e => this.setState({ url: e.target.value })}
- />
- <div className="modal-field-description">
- {translate('organization.url.description')}
+ <div className="modal-field">
+ <button type="submit" disabled={this.state.loading}>
+ {translate('save')}
+ </button>
+ {this.state.loading && <i className="spinner spacer-left" />}
</div>
- </div>
- <div className="modal-field">
- <button type="submit" disabled={this.state.loading}>
- {translate('save')}
- </button>
- {this.state.loading && <i className="spinner spacer-left" />}
- </div>
- </form>
+ </form>
+ </div>
</div>
);
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render a list of members of an organization 1`] = `
-<table
- className="data zebra"
+<div
+ className="boxed-group boxed-group-inner"
>
- <tbody>
- <MembersListItem
- key="admin"
- member={
- Object {
- "avatar": "",
- "groupCount": 3,
- "login": "admin",
- "name": "Admin Istrator",
+ <table
+ className="data zebra"
+ >
+ <tbody>
+ <MembersListItem
+ key="admin"
+ member={
+ Object {
+ "avatar": "",
+ "groupCount": 3,
+ "login": "admin",
+ "name": "Admin Istrator",
+ }
}
- }
- organization={
- Object {
- "key": "foo",
- "name": "Foo",
+ organization={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
}
- }
- />
- <MembersListItem
- key="john"
- member={
- Object {
- "avatar": "7daf6c79d4802916d83f6266e24850af",
- "groupCount": 1,
- "login": "john",
- "name": "John Doe",
+ />
+ <MembersListItem
+ key="john"
+ member={
+ Object {
+ "avatar": "7daf6c79d4802916d83f6266e24850af",
+ "groupCount": 1,
+ "login": "john",
+ "name": "John Doe",
+ }
}
- }
- organization={
- Object {
- "key": "foo",
- "name": "Foo",
+ organization={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
}
- }
- />
- </tbody>
-</table>
+ />
+ </tbody>
+ </table>
+</div>
`;
organization.edit
</h1>
</header>
- <form
- onSubmit={[Function]}
+ <div
+ className="boxed-group boxed-group-inner"
>
- <div
- className="modal-field"
+ <form
+ onSubmit={[Function]}
>
- <label
- htmlFor="organization-name"
+ <div
+ className="modal-field"
>
- organization.name
- <em
- className="mandatory"
+ <label
+ htmlFor="organization-name"
+ >
+ organization.name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ disabled={false}
+ id="organization-name"
+ maxLength="64"
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value="Foo"
+ />
+ <div
+ className="modal-field-description"
>
- *
- </em>
- </label>
- <input
- disabled={false}
- id="organization-name"
- maxLength="64"
- name="name"
- onChange={[Function]}
- required={true}
- type="text"
- value="Foo"
- />
+ organization.name.description
+ </div>
+ </div>
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.name.description
+ <label
+ htmlFor="organization-avatar"
+ >
+ organization.avatar
+ </label>
+ <input
+ disabled={false}
+ id="organization-avatar"
+ maxLength="256"
+ name="avatar"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.avatar.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-avatar"
- >
- organization.avatar
- </label>
- <input
- disabled={false}
- id="organization-avatar"
- maxLength="256"
- name="avatar"
- onChange={[Function]}
- type="text"
- value=""
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.avatar.description
+ <label
+ htmlFor="organization-description"
+ >
+ description
+ </label>
+ <textarea
+ disabled={false}
+ id="organization-description"
+ maxLength="256"
+ name="description"
+ onChange={[Function]}
+ rows="3"
+ value=""
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.description.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-description"
- >
- description
- </label>
- <textarea
- disabled={false}
- id="organization-description"
- maxLength="256"
- name="description"
- onChange={[Function]}
- rows="3"
- value=""
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.description.description
+ <label
+ htmlFor="organization-url"
+ >
+ organization.url
+ </label>
+ <input
+ disabled={false}
+ id="organization-url"
+ maxLength="256"
+ name="url"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.url.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-url"
- >
- organization.url
- </label>
- <input
- disabled={false}
- id="organization-url"
- maxLength="256"
- name="url"
- onChange={[Function]}
- type="text"
- value=""
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.url.description
+ <button
+ disabled={false}
+ type="submit"
+ >
+ save
+ </button>
</div>
- </div>
- <div
- className="modal-field"
- >
- <button
- disabled={false}
- type="submit"
- >
- save
- </button>
- </div>
- </form>
+ </form>
+ </div>
</div>
`;
organization.edit
</h1>
</header>
- <form
- onSubmit={[Function]}
+ <div
+ className="boxed-group boxed-group-inner"
>
- <div
- className="modal-field"
+ <form
+ onSubmit={[Function]}
>
- <label
- htmlFor="organization-name"
- >
- organization.name
- <em
- className="mandatory"
- >
- *
- </em>
- </label>
- <input
- disabled={false}
- id="organization-name"
- maxLength="64"
- name="name"
- onChange={[Function]}
- required={true}
- type="text"
- value="New Foo"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.name.description
+ <label
+ htmlFor="organization-name"
+ >
+ organization.name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ disabled={false}
+ id="organization-name"
+ maxLength="64"
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value="New Foo"
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.name.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-avatar"
- >
- organization.avatar
- </label>
- <input
- disabled={false}
- id="organization-avatar"
- maxLength="256"
- name="avatar"
- onChange={[Function]}
- type="text"
- value="foo-avatar"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.avatar.description
+ <label
+ htmlFor="organization-avatar"
+ >
+ organization.avatar
+ </label>
+ <input
+ disabled={false}
+ id="organization-avatar"
+ maxLength="256"
+ name="avatar"
+ onChange={[Function]}
+ type="text"
+ value="foo-avatar"
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.avatar.description
+ </div>
+ <div
+ className="spacer-top spacer-bottom"
+ >
+ <div
+ className="little-spacer-bottom"
+ >
+ organization.avatar.preview
+ :
+ </div>
+ <img
+ alt=""
+ height={30}
+ src="foo-avatar-image"
+ />
+ </div>
</div>
<div
- className="spacer-top spacer-bottom"
+ className="modal-field"
>
+ <label
+ htmlFor="organization-description"
+ >
+ description
+ </label>
+ <textarea
+ disabled={false}
+ id="organization-description"
+ maxLength="256"
+ name="description"
+ onChange={[Function]}
+ rows="3"
+ value="foo-description"
+ />
<div
- className="little-spacer-bottom"
+ className="modal-field-description"
>
- organization.avatar.preview
- :
+ organization.description.description
</div>
- <img
- alt=""
- height={30}
- src="foo-avatar-image"
- />
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-description"
- >
- description
- </label>
- <textarea
- disabled={false}
- id="organization-description"
- maxLength="256"
- name="description"
- onChange={[Function]}
- rows="3"
- value="foo-description"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.description.description
+ <label
+ htmlFor="organization-url"
+ >
+ organization.url
+ </label>
+ <input
+ disabled={false}
+ id="organization-url"
+ maxLength="256"
+ name="url"
+ onChange={[Function]}
+ type="text"
+ value="foo-url"
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.url.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-url"
- >
- organization.url
- </label>
- <input
- disabled={false}
- id="organization-url"
- maxLength="256"
- name="url"
- onChange={[Function]}
- type="text"
- value="foo-url"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.url.description
+ <button
+ disabled={false}
+ type="submit"
+ >
+ save
+ </button>
</div>
- </div>
- <div
- className="modal-field"
- >
- <button
- disabled={false}
- type="submit"
- >
- save
- </button>
- </div>
- </form>
+ </form>
+ </div>
</div>
`;
organization.edit
</h1>
</header>
- <form
- onSubmit={[Function]}
+ <div
+ className="boxed-group boxed-group-inner"
>
- <div
- className="modal-field"
+ <form
+ onSubmit={[Function]}
>
- <label
- htmlFor="organization-name"
- >
- organization.name
- <em
- className="mandatory"
- >
- *
- </em>
- </label>
- <input
- disabled={true}
- id="organization-name"
- maxLength="64"
- name="name"
- onChange={[Function]}
- required={true}
- type="text"
- value="New Foo"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.name.description
+ <label
+ htmlFor="organization-name"
+ >
+ organization.name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ disabled={true}
+ id="organization-name"
+ maxLength="64"
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value="New Foo"
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.name.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-avatar"
- >
- organization.avatar
- </label>
- <input
- disabled={true}
- id="organization-avatar"
- maxLength="256"
- name="avatar"
- onChange={[Function]}
- type="text"
- value="foo-avatar"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.avatar.description
+ <label
+ htmlFor="organization-avatar"
+ >
+ organization.avatar
+ </label>
+ <input
+ disabled={true}
+ id="organization-avatar"
+ maxLength="256"
+ name="avatar"
+ onChange={[Function]}
+ type="text"
+ value="foo-avatar"
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.avatar.description
+ </div>
+ <div
+ className="spacer-top spacer-bottom"
+ >
+ <div
+ className="little-spacer-bottom"
+ >
+ organization.avatar.preview
+ :
+ </div>
+ <img
+ alt=""
+ height={30}
+ src="foo-avatar-image"
+ />
+ </div>
</div>
<div
- className="spacer-top spacer-bottom"
+ className="modal-field"
>
+ <label
+ htmlFor="organization-description"
+ >
+ description
+ </label>
+ <textarea
+ disabled={true}
+ id="organization-description"
+ maxLength="256"
+ name="description"
+ onChange={[Function]}
+ rows="3"
+ value="foo-description"
+ />
<div
- className="little-spacer-bottom"
+ className="modal-field-description"
>
- organization.avatar.preview
- :
+ organization.description.description
</div>
- <img
- alt=""
- height={30}
- src="foo-avatar-image"
- />
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-description"
- >
- description
- </label>
- <textarea
- disabled={true}
- id="organization-description"
- maxLength="256"
- name="description"
- onChange={[Function]}
- rows="3"
- value="foo-description"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.description.description
+ <label
+ htmlFor="organization-url"
+ >
+ organization.url
+ </label>
+ <input
+ disabled={true}
+ id="organization-url"
+ maxLength="256"
+ name="url"
+ onChange={[Function]}
+ type="text"
+ value="foo-url"
+ />
+ <div
+ className="modal-field-description"
+ >
+ organization.url.description
+ </div>
</div>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="organization-url"
- >
- organization.url
- </label>
- <input
- disabled={true}
- id="organization-url"
- maxLength="256"
- name="url"
- onChange={[Function]}
- type="text"
- value="foo-url"
- />
<div
- className="modal-field-description"
+ className="modal-field"
>
- organization.url.description
+ <button
+ disabled={true}
+ type="submit"
+ >
+ save
+ </button>
+ <i
+ className="spinner spacer-left"
+ />
</div>
- </div>
- <div
- className="modal-field"
- >
- <button
- disabled={true}
- type="submit"
- >
- save
- </button>
- <i
- className="spinner spacer-left"
- />
- </div>
- </form>
+ </form>
+ </div>
</div>
`;
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Organization } from '../../../app/types';
+import { connect } from 'react-redux';
+import { Organization, HomePageType } from '../../../app/types';
import HomePageSelect from '../../../components/controls/HomePageSelect';
import { translate } from '../../../helpers/l10n';
+import { getGlobalSettingValue } from '../../../store/rootReducer';
-interface Props {
+interface StateProps {
+ onSonarCloud: boolean;
+}
+
+interface Props extends StateProps {
organization: Organization;
}
-export default function OrganizationNavigationMeta({ organization }: Props) {
+export function OrganizationNavigationMeta({ onSonarCloud, organization }: Props) {
return (
<div className="navbar-context-meta">
{organization.url != null && (
<div className="text-muted">
<strong>{translate('organization.key')}:</strong> {organization.key}
</div>
- <div className="navbar-context-meta-secondary">
- <HomePageSelect currentPage={{ type: 'organization', key: organization.key }} />
- </div>
+ {onSonarCloud && (
+ <div className="navbar-context-meta-secondary">
+ <HomePageSelect
+ currentPage={{ type: HomePageType.Organization, parameter: organization.key }}
+ />
+ </div>
+ )}
</div>
);
}
+
+const mapStateToProps = (state: any): StateProps => {
+ const sonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+
+ return {
+ onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
+ };
+};
+
+export default connect(mapStateToProps)(OrganizationNavigationMeta);
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import OrganizationNavigationMeta from '../OrganizationNavigationMeta';
+import { OrganizationNavigationMeta } from '../OrganizationNavigationMeta';
import { Visibility } from '../../../../app/types';
it('renders', () => {
expect(
shallow(
<OrganizationNavigationMeta
+ onSonarCloud={true}
organization={{
key: 'foo',
name: 'Foo',
}
}
/>
- <OrganizationNavigationMeta
+ <Connect(OrganizationNavigationMeta)
organization={
Object {
"key": "foo",
<Connect(HomePageSelect)
currentPage={
Object {
- "key": "foo",
- "type": "organization",
+ "parameter": "foo",
+ "type": "ORGANIZATION",
}
}
/>
componentDidMount() {
this.mounted = true;
- const domElement = document.querySelector('html');
- if (domElement) {
- domElement.classList.add('dashboard-page');
- }
this.loadMeasures().then(this.loadHistory);
}
componentWillUnmount() {
this.mounted = false;
- const domElement = document.querySelector('html');
- if (domElement) {
- domElement.classList.remove('dashboard-page');
- }
}
loadMeasures() {
));
return (
- <table id="permission-templates" className="data zebra permissions-table">
- <ListHeader organization={this.props.organization} permissions={this.props.permissions} />
- <tbody>{permissionTemplates}</tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table id="permission-templates" className="data zebra permissions-table">
+ <ListHeader organization={this.props.organization} permissions={this.props.permissions} />
+ <tbody>{permissionTemplates}</tbody>
+ </table>
+ </div>
);
}
}
));
return (
- <table className="data zebra permissions-table">
- {this.renderTableHeader()}
- <tbody>
- {users.length === 0 && groups.length === 0 && this.renderEmpty()}
- {users}
- {groups}
- </tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table className="data zebra permissions-table">
+ {this.renderTableHeader()}
+ <tbody>
+ {users.length === 0 && groups.length === 0 && this.renderEmpty()}
+ {users}
+ {groups}
+ </tbody>
+ </table>
+ </div>
);
}
}
componentDidMount() {
this.mounted = true;
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
this.fetchData();
}
componentWillUnmount() {
this.mounted = false;
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
}
fetchData() {
)}
{hasModules && (
- <div>
+ <div className="boxed-group boxed-group-inner">
<div className="big-spacer-bottom">
<ul className="tabs">
<li>
));
return (
- <table id="project-links" className="data zebra">
- {this.renderHeader()}
- <tbody>{linkRows}</tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table id="project-links" className="data zebra">
+ {this.renderHeader()}
+ <tbody>{linkRows}</tbody>
+ </table>
+ </div>
);
}
}
componentDidMount() {
this.mounted = true;
- const elem = document.querySelector('html');
- elem && elem.classList.add('dashboard-page');
if (this.shouldRedirect()) {
const newQuery = { ...this.state.query, graph: getGraph() };
if (isCustomGraph(newQuery.graph)) {
componentWillUnmount() {
this.mounted = false;
- const elem = document.querySelector('html');
- elem && elem.classList.remove('dashboard-page');
}
addCustomEvent = (analysis /*: string */, name /*: string */, category /*: ?string */) =>
{this.renderBranchLifeTime()}
</header>
- <table className="data zebra zebra-hover">
- <thead>
- <tr>
- <th>{translate('branch')}</th>
- <th className="thin nowrap text-right">{translate('status')}</th>
- <th className="thin nowrap text-right">{translate('branches.last_analysis_date')}</th>
- <th className="thin nowrap text-right">{translate('actions')}</th>
- </tr>
- </thead>
- <tbody>
- {sortBranchesAsTree(branches).map(branch => (
- <BranchRow
- branch={branch}
- component={component.key}
- key={branch.name}
- onChange={onBranchesChange}
- />
- ))}
- </tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table className="data zebra zebra-hover">
+ <thead>
+ <tr>
+ <th>{translate('branch')}</th>
+ <th className="thin nowrap text-right">{translate('status')}</th>
+ <th className="thin nowrap text-right">
+ {translate('branches.last_analysis_date')}
+ </th>
+ <th className="thin nowrap text-right">{translate('actions')}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {sortBranchesAsTree(branches).map(branch => (
+ <BranchRow
+ branch={branch}
+ component={component.key}
+ key={branch.name}
+ onChange={onBranchesChange}
+ />
+ ))}
+ </tbody>
+ </table>
+ </div>
</div>
);
}
/>
</p>
</header>
- <table
- className="data zebra zebra-hover"
+ <div
+ className="boxed-group boxed-group-inner"
>
- <thead>
- <tr>
- <th>
- branch
- </th>
- <th
- className="thin nowrap text-right"
- >
- status
- </th>
- <th
- className="thin nowrap text-right"
- >
- branches.last_analysis_date
- </th>
- <th
- className="thin nowrap text-right"
- >
- actions
- </th>
- </tr>
- </thead>
- <tbody>
- <BranchRow
- branch={
- Object {
- "isMain": true,
- "name": "master",
+ <table
+ className="data zebra zebra-hover"
+ >
+ <thead>
+ <tr>
+ <th>
+ branch
+ </th>
+ <th
+ className="thin nowrap text-right"
+ >
+ status
+ </th>
+ <th
+ className="thin nowrap text-right"
+ >
+ branches.last_analysis_date
+ </th>
+ <th
+ className="thin nowrap text-right"
+ >
+ actions
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <BranchRow
+ branch={
+ Object {
+ "isMain": true,
+ "name": "master",
+ }
}
- }
- component="foo"
- key="master"
- onChange={[Function]}
- />
- <BranchRow
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "master",
- "name": "branch-1.0",
- "type": "SHORT",
+ component="foo"
+ key="master"
+ onChange={[Function]}
+ />
+ <BranchRow
+ branch={
+ Object {
+ "isMain": false,
+ "mergeBranch": "master",
+ "name": "branch-1.0",
+ "type": "SHORT",
+ }
}
- }
- component="foo"
- key="branch-1.0"
- onChange={[Function]}
- />
- <BranchRow
- branch={
- Object {
- "isMain": false,
- "name": "branch-1.0",
- "type": "LONG",
+ component="foo"
+ key="branch-1.0"
+ onChange={[Function]}
+ />
+ <BranchRow
+ branch={
+ Object {
+ "isMain": false,
+ "name": "branch-1.0",
+ "type": "LONG",
+ }
}
- }
- component="foo"
- key="branch-1.0"
- onChange={[Function]}
- />
- </tbody>
- </table>
+ component="foo"
+ key="branch-1.0"
+ onChange={[Function]}
+ />
+ </tbody>
+ </table>
+ </div>
</div>
`;
));
return (
- <table className="data zebra">
- <thead>
- <tr>
- <th className="thin nowrap">{translate('language')}</th>
- <th className="thin nowrap">{translate('quality_profile')}</th>
- {/* keep one empty cell for the spinner */}
- <th> </th>
- </tr>
- </thead>
- <tbody>{profileRows}</tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table className="data zebra">
+ <thead>
+ <tr>
+ <th className="thin nowrap">{translate('language')}</th>
+ <th className="thin nowrap">{translate('quality_profile')}</th>
+ {/* keep one empty cell for the spinner */}
+ <th> </th>
+ </tr>
+ </thead>
+ <tbody>{profileRows}</tbody>
+ </table>
+ </div>
);
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders 1`] = `
-<table
- className="data zebra"
+<div
+ className="boxed-group boxed-group-inner"
>
- <thead>
- <tr>
- <th
- className="thin nowrap"
- >
- language
- </th>
- <th
- className="thin nowrap"
- >
- quality_profile
- </th>
- <th>
- Â
- </th>
- </tr>
- </thead>
- <tbody>
- <ProfileRow
- key="java"
- onChangeProfile={[Function]}
- possibleProfiles={
- Array [
+ <table
+ className="data zebra"
+ >
+ <thead>
+ <tr>
+ <th
+ className="thin nowrap"
+ >
+ language
+ </th>
+ <th
+ className="thin nowrap"
+ >
+ quality_profile
+ </th>
+ <th>
+ Â
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <ProfileRow
+ key="java"
+ onChangeProfile={[Function]}
+ possibleProfiles={
+ Array [
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 17,
+ "key": "foo-java",
+ "language": "java",
+ "languageName": "java",
+ "name": "foo-java",
+ "organization": "org",
+ },
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 17,
+ "key": "bar-java",
+ "language": "java",
+ "languageName": "java",
+ "name": "bar-java",
+ "organization": "org",
+ },
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 17,
+ "key": "baz-java",
+ "language": "java",
+ "languageName": "java",
+ "name": "baz-java",
+ "organization": "org",
+ },
+ ]
+ }
+ profile={
Object {
"activeDeprecatedRuleCount": 0,
"activeRuleCount": 17,
"languageName": "java",
"name": "foo-java",
"organization": "org",
- },
- Object {
- "activeDeprecatedRuleCount": 0,
- "activeRuleCount": 17,
- "key": "bar-java",
- "language": "java",
- "languageName": "java",
- "name": "bar-java",
- "organization": "org",
- },
- Object {
- "activeDeprecatedRuleCount": 0,
- "activeRuleCount": 17,
- "key": "baz-java",
- "language": "java",
- "languageName": "java",
- "name": "baz-java",
- "organization": "org",
- },
- ]
- }
- profile={
- Object {
- "activeDeprecatedRuleCount": 0,
- "activeRuleCount": 17,
- "key": "foo-java",
- "language": "java",
- "languageName": "java",
- "name": "foo-java",
- "organization": "org",
+ }
+ }
+ />
+ <ProfileRow
+ key="js"
+ onChangeProfile={[Function]}
+ possibleProfiles={
+ Array [
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 17,
+ "key": "foo-js",
+ "language": "js",
+ "languageName": "js",
+ "name": "foo-js",
+ "organization": "org",
+ },
+ ]
}
- }
- />
- <ProfileRow
- key="js"
- onChangeProfile={[Function]}
- possibleProfiles={
- Array [
+ profile={
Object {
"activeDeprecatedRuleCount": 0,
"activeRuleCount": 17,
"languageName": "js",
"name": "foo-js",
"organization": "org",
- },
- ]
- }
- profile={
- Object {
- "activeDeprecatedRuleCount": 0,
- "activeRuleCount": 17,
- "key": "foo-js",
- "language": "js",
- "languageName": "js",
- "name": "foo-js",
- "organization": "org",
+ }
}
- }
- />
- </tbody>
-</table>
+ />
+ </tbody>
+ </table>
+</div>
`;
componentDidMount() {
this.mounted = true;
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
-
if (this.props.isFavorite && !isLoggedIn(this.props.currentUser)) {
handleRequiredAuthentication();
return;
componentWillUnmount() {
this.mounted = false;
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
-
const footer = document.getElementById('footer');
if (footer) {
footer.classList.remove('page-footer-with-sidebar');
import Tooltip from '../../../components/controls/Tooltip';
import PerspectiveSelect from './PerspectiveSelect';
import ProjectsSortingSelect from './ProjectsSortingSelect';
-import { CurrentUser, isLoggedIn } from '../../../app/types';
+import { CurrentUser, isLoggedIn, HomePageType } from '../../../app/types';
import HomePageSelect from '../../../components/controls/HomePageSelect';
import { translate } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';
)}
</div>
- {props.onSonarCloud &&
- isLoggedIn(currentUser) &&
- props.isFavorite &&
- !props.organization && (
- <HomePageSelect className="huge-spacer-left" currentPage={{ type: 'my-projects' }} />
- )}
+ {props.isFavorite && (
+ <HomePageSelect
+ className="huge-spacer-left"
+ currentPage={{ type: HomePageType.MyProjects }}
+ />
+ )}
</header>
);
}
className="spacer-right"
component={project.key}
favorite={project.isFavorite}
+ qualifier="TRK"
/>
)}
<h2 className="project-card-name">
className="spacer-right"
component={project.key}
favorite={project.isFavorite}
+ qualifier="TRK"
/>
)}
<h2 className="project-card-name">
render() {
return (
- <table
- className={classNames('data', 'zebra', { 'new-loading': !this.props.ready })}
- id="projects-management-page-projects">
- <thead>
- <tr>
- <th />
- <th>{translate('name')}</th>
- <th />
- <th>{translate('key')}</th>
- <th className="thin nowrap text-right">{translate('last_analysis')}</th>
- <th />
- </tr>
- </thead>
- <tbody>
- {this.props.projects.map(project => (
- <ProjectRow
- currentUser={this.props.currentUser}
- key={project.key}
- onApplyTemplate={this.handleApplyTemplate}
- onProjectCheck={this.onProjectCheck}
- project={project}
- selected={this.props.selection.includes(project.key)}
- />
- ))}
- </tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table
+ className={classNames('data', 'zebra', { 'new-loading': !this.props.ready })}
+ id="projects-management-page-projects">
+ <thead>
+ <tr>
+ <th />
+ <th>{translate('name')}</th>
+ <th />
+ <th>{translate('key')}</th>
+ <th className="thin nowrap text-right">{translate('last_analysis')}</th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {this.props.projects.map(project => (
+ <ProjectRow
+ currentUser={this.props.currentUser}
+ key={project.key}
+ onApplyTemplate={this.handleApplyTemplate}
+ onProjectCheck={this.onProjectCheck}
+ project={project}
+ selected={this.props.selection.includes(project.key)}
+ />
+ ))}
+ </tbody>
+ </table>
+ </div>
);
}
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders list of projects 1`] = `
-<table
- className="data zebra new-loading"
- id="projects-management-page-projects"
+<div
+ className="boxed-group boxed-group-inner"
>
- <thead>
- <tr>
- <th />
- <th>
- name
- </th>
- <th />
- <th>
- key
- </th>
- <th
- className="thin nowrap text-right"
- >
- last_analysis
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- <ProjectRow
- currentUser={
- Object {
- "login": "foo",
+ <table
+ className="data zebra new-loading"
+ id="projects-management-page-projects"
+ >
+ <thead>
+ <tr>
+ <th />
+ <th>
+ name
+ </th>
+ <th />
+ <th>
+ key
+ </th>
+ <th
+ className="thin nowrap text-right"
+ >
+ last_analysis
+ </th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ <ProjectRow
+ currentUser={
+ Object {
+ "login": "foo",
+ }
}
- }
- key="a"
- onApplyTemplate={[Function]}
- onProjectCheck={[Function]}
- project={
- Object {
- "key": "a",
- "name": "A",
- "qualifier": "TRK",
- "visibility": "public",
+ key="a"
+ onApplyTemplate={[Function]}
+ onProjectCheck={[Function]}
+ project={
+ Object {
+ "key": "a",
+ "name": "A",
+ "qualifier": "TRK",
+ "visibility": "public",
+ }
}
- }
- selected={true}
- />
- <ProjectRow
- currentUser={
- Object {
- "login": "foo",
+ selected={true}
+ />
+ <ProjectRow
+ currentUser={
+ Object {
+ "login": "foo",
+ }
}
- }
- key="b"
- onApplyTemplate={[Function]}
- onProjectCheck={[Function]}
- project={
- Object {
- "key": "b",
- "name": "B",
- "qualifier": "TRK",
- "visibility": "public",
+ key="b"
+ onApplyTemplate={[Function]}
+ onProjectCheck={[Function]}
+ project={
+ Object {
+ "key": "b",
+ "name": "B",
+ "qualifier": "TRK",
+ "visibility": "public",
+ }
}
- }
- selected={false}
- />
- </tbody>
-</table>
+ selected={false}
+ />
+ </tbody>
+ </table>
+</div>
`;
componentDidMount() {
this.fetchQualityGates();
+ // $FlowFixMe
+ document.body.classList.add('white-page');
const footer = document.getElementById('footer');
if (footer) {
footer.classList.add('page-footer-with-sidebar');
}
componentWillUnmount() {
+ // $FlowFixMe
+ document.body.classList.remove('white-page');
const footer = document.getElementById('footer');
if (footer) {
footer.classList.remove('page-footer-with-sidebar');
mounted: boolean;
state: State = { loading: true };
- componentWillMount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
- }
-
componentDidMount() {
this.mounted = true;
this.loadData();
componentWillUnmount() {
this.mounted = false;
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
}
fetchProfiles() {
state /*: State */ = { loaded: false };
componentDidMount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
- }
const componentKey = this.props.component ? this.props.component.key : null;
this.props.fetchSettings(componentKey).then(() => this.setState({ loaded: true }));
}
}
}
- componentWillUnmount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
- }
- }
-
render() {
if (!this.state.loaded) {
return null;
users
}: Props) {
return (
- <table id="users-list" className="data zebra">
- <thead>
- <tr>
- <th />
- <th className="nowrap" />
- <th className="nowrap">{translate('my_profile.scm_accounts')}</th>
- {!organizationsEnabled && <th className="nowrap">{translate('my_profile.groups')}</th>}
- <th className="nowrap">{translate('users.tokens')}</th>
- <th className="nowrap"> </th>
- </tr>
- </thead>
- <tbody>
- {users.map(user => (
- <UserListItem
- identityProvider={identityProviders.find(
- provider => user.externalProvider === provider.key
- )}
- isCurrentUser={currentUser.isLoggedIn && currentUser.login === user.login}
- key={user.login}
- onUpdateUsers={onUpdateUsers}
- organizationsEnabled={organizationsEnabled}
- user={user}
- />
- ))}
- </tbody>
- </table>
+ <div className="boxed-group boxed-group-inner">
+ <table id="users-list" className="data zebra">
+ <thead>
+ <tr>
+ <th />
+ <th className="nowrap" />
+ <th className="nowrap">{translate('my_profile.scm_accounts')}</th>
+ {!organizationsEnabled && <th className="nowrap">{translate('my_profile.groups')}</th>}
+ <th className="nowrap">{translate('users.tokens')}</th>
+ <th className="nowrap"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ {users.map(user => (
+ <UserListItem
+ identityProvider={identityProviders.find(
+ provider => user.externalProvider === provider.key
+ )}
+ isCurrentUser={currentUser.isLoggedIn && currentUser.login === user.login}
+ key={user.login}
+ onUpdateUsers={onUpdateUsers}
+ organizationsEnabled={organizationsEnabled}
+ user={user}
+ />
+ ))}
+ </tbody>
+ </table>
+ </div>
);
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render correctly 1`] = `
-<table
- className="data zebra"
- id="users-list"
+<div
+ className="boxed-group boxed-group-inner"
>
- <thead>
- <tr>
- <th />
- <th
- className="nowrap"
- />
- <th
- className="nowrap"
- >
- my_profile.scm_accounts
- </th>
- <th
- className="nowrap"
- >
- users.tokens
- </th>
- <th
- className="nowrap"
- >
- Â
- </th>
- </tr>
- </thead>
- <tbody>
- <UserListItem
- isCurrentUser={true}
- key="luke"
- onUpdateUsers={[Function]}
- organizationsEnabled={true}
- user={
- Object {
- "active": true,
- "local": false,
- "login": "luke",
- "name": "Luke",
- "scmAccounts": Array [],
+ <table
+ className="data zebra"
+ id="users-list"
+ >
+ <thead>
+ <tr>
+ <th />
+ <th
+ className="nowrap"
+ />
+ <th
+ className="nowrap"
+ >
+ my_profile.scm_accounts
+ </th>
+ <th
+ className="nowrap"
+ >
+ users.tokens
+ </th>
+ <th
+ className="nowrap"
+ >
+ Â
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <UserListItem
+ isCurrentUser={true}
+ key="luke"
+ onUpdateUsers={[Function]}
+ organizationsEnabled={true}
+ user={
+ Object {
+ "active": true,
+ "local": false,
+ "login": "luke",
+ "name": "Luke",
+ "scmAccounts": Array [],
+ }
}
- }
- />
- <UserListItem
- isCurrentUser={false}
- key="obi"
- onUpdateUsers={[Function]}
- organizationsEnabled={true}
- user={
- Object {
- "active": true,
- "local": false,
- "login": "obi",
- "name": "One",
- "scmAccounts": Array [],
+ />
+ <UserListItem
+ isCurrentUser={false}
+ key="obi"
+ onUpdateUsers={[Function]}
+ organizationsEnabled={true}
+ user={
+ Object {
+ "active": true,
+ "local": false,
+ "login": "obi",
+ "name": "One",
+ "scmAccounts": Array [],
+ }
}
- }
- />
- </tbody>
-</table>
+ />
+ </tbody>
+ </table>
+</div>
`;
);
}
- return <hr />;
+ return null;
}
render() {
const actionKey = getActionKey(domain.path, action.key);
return (
- <div id={actionKey} className="web-api-action">
- <header className="web-api-action-header">
+ <div id={actionKey} className="boxed-group">
+ <header className="web-api-action-header boxed-group-header">
<Link
to={{ pathname: '/web_api/' + actionKey }}
className="spacer-right link-no-underline">
)}
</header>
- <div
- className="web-api-action-description markdown"
- dangerouslySetInnerHTML={{ __html: action.description }}
- />
+ <div className="boxed-group-inner">
+ <div
+ className="web-api-action-description markdown"
+ dangerouslySetInnerHTML={{ __html: action.description }}
+ />
- {this.renderTabs()}
+ {this.renderTabs()}
- {showParams &&
- action.params && (
- <Params
- params={action.params}
- showDeprecated={this.props.showDeprecated}
- showInternal={this.props.showInternal}
- />
- )}
+ {showParams &&
+ action.params && (
+ <Params
+ params={action.params}
+ showDeprecated={this.props.showDeprecated}
+ showInternal={this.props.showInternal}
+ />
+ )}
- {showResponse &&
- action.hasResponseExample && <ResponseExample domain={domain} action={action} />}
+ {showResponse &&
+ action.hasResponseExample && <ResponseExample domain={domain} action={action} />}
- {showChangelog && <ActionChangelog changelog={action.changelog} />}
+ {showChangelog && <ActionChangelog changelog={action.changelog} />}
+ </div>
</div>
);
}
exports[`should render correctly 1`] = `
<div
- className="web-api-action"
+ className="boxed-group"
id="foo/foo"
>
<header
- className="web-api-action-header"
+ className="web-api-action-header boxed-group-header"
>
<Link
className="spacer-right link-no-underline"
</h3>
</header>
<div
- className="web-api-action-description markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "Foo Desc",
- }
- }
- />
- <ul
- className="web-api-action-actions tabs"
+ className="boxed-group-inner"
>
- <li>
- <a
- className=""
- href="#"
- onClick={[Function]}
- >
- api_documentation.parameters
- </a>
- </li>
- <li>
- <a
- className=""
- href="#"
- onClick={[Function]}
- >
- api_documentation.response_example
- </a>
- </li>
- <li>
- <a
- className=""
- href="#"
- onClick={[Function]}
- >
- api_documentation.changelog
- </a>
- </li>
- </ul>
+ <div
+ className="web-api-action-description markdown"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "Foo Desc",
+ }
+ }
+ />
+ <ul
+ className="web-api-action-actions tabs"
+ >
+ <li>
+ <a
+ className=""
+ href="#"
+ onClick={[Function]}
+ >
+ api_documentation.parameters
+ </a>
+ </li>
+ <li>
+ <a
+ className=""
+ href="#"
+ onClick={[Function]}
+ >
+ api_documentation.response_example
+ </a>
+ </li>
+ <li>
+ <a
+ className=""
+ href="#"
+ onClick={[Function]}
+ >
+ api_documentation.changelog
+ </a>
+ </li>
+ </ul>
+ </div>
</div>
`;
}
.web-api-domain-actions {
-}
-
-.web-api-action {
- padding-top: 30px;
+ margin-top: calc(2 * var(--gridSize));
}
.web-api-action-title {
.source-viewer {
width: 100%;
min-height: 200px;
- border: 1px solid var(--barBorderColor);
+ border: 1px solid var(--gray80);
box-sizing: border-box;
background-color: #fff;
overflow-x: auto;
.branch-status {
+ display: flex;
+ align-items: center;
min-width: 64px;
+ line-height: calc(2 * var(--gridSize));
text-align: right;
}
const indicatorColor = totalIssues > 0 ? 'red' : 'green';
return concise ? (
- <ul className="list-inline branch-status">
+ <ul className="branch-status">
<li>{totalIssues}</li>
<li className="spacer-left">
<StatusIndicator color={indicatorColor} size="small" />
</li>
</ul>
) : (
- <ul className="list-inline branch-status">
+ <ul className="branch-status">
<li className="spacer-right">
<StatusIndicator color={indicatorColor} size="small" />
</li>
- <li>
+ <li className="spacer-left">
{branch.status.bugs}
<BugIcon />
</li>
- <li>
+ <li className="spacer-left">
{branch.status.vulnerabilities}
<VulnerabilityIcon />
</li>
- <li>
+ <li className="spacer-left">
{branch.status.codeSmells}
<CodeSmellIcon />
</li>
exports[`renders status of short-living branches 1`] = `
<ul
- className="list-inline branch-status"
+ className="branch-status"
>
<li
className="spacer-right"
size="small"
/>
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
0
<BugIcon />
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
0
<VulnerabilityIcon />
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
0
<CodeSmellIcon />
</li>
exports[`renders status of short-living branches 2`] = `
<ul
- className="list-inline branch-status"
+ className="branch-status"
>
<li
className="spacer-right"
size="small"
/>
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
0
<BugIcon />
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
0
<VulnerabilityIcon />
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
1
<CodeSmellIcon />
</li>
exports[`renders status of short-living branches 3`] = `
<ul
- className="list-inline branch-status"
+ className="branch-status"
>
<li
className="spacer-right"
size="small"
/>
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
7
<BugIcon />
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
6
<VulnerabilityIcon />
</li>
- <li>
+ <li
+ className="spacer-left"
+ >
3
<CodeSmellIcon />
</li>
className?: string;
component: string;
favorite: boolean;
+ qualifier: string;
}
-export default function Favorite({ favorite, component, ...other }: Props) {
+export default function Favorite({ component, ...other }: Props) {
return (
<FavoriteBase
{...other}
- favorite={favorite}
addFavorite={() => addFavorite(component)}
removeFavorite={() => removeFavorite(component)}
/>
import FavoriteIcon from '../icons-components/FavoriteIcon';
import { translate } from '../../helpers/l10n';
-interface Props {
+export interface Props {
addFavorite: () => Promise<void>;
className?: string;
favorite: boolean;
+ qualifier: string;
removeFavorite: () => Promise<void>;
}
render() {
const tooltip = this.state.favorite
- ? translate('favorite.current')
- : translate('favorite.check');
+ ? translate('favorite.current', this.props.qualifier)
+ : translate('favorite.check', this.props.qualifier);
return (
- <Tooltip overlay={tooltip}>
+ <Tooltip overlay={tooltip} placement="left">
<a
className={classNames('display-inline-block', 'link-no-underline', this.props.className)}
href="#"
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FavoriteBase from './FavoriteBase';
-import { toggleIssueFilter } from '../../api/issue-filters';
-
-export default class FavoriteIssueFilter extends React.PureComponent {
- static propTypes = {
- favorite: PropTypes.bool.isRequired,
- filter: PropTypes.shape({
- id: PropTypes.string.isRequired
- }).isRequired
- };
-
- render() {
- return (
- <FavoriteBase
- favorite={this.props.favorite}
- addFavorite={() => toggleIssueFilter(this.props.filter.id)}
- removeFavorite={() => toggleIssueFilter(this.props.filter.id)}
- />
- );
- }
-}
import HomeIcon from '../icons-components/HomeIcon';
import { CurrentUser, isLoggedIn, HomePage, isSameHomePage } from '../../app/types';
import { translate } from '../../helpers/l10n';
-import { getCurrentUser } from '../../store/rootReducer';
+import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
import { setHomePage } from '../../store/users/actions';
interface StateProps {
currentUser: CurrentUser;
+ onSonarCloud: boolean;
}
interface DispatchProps {
};
render() {
- const { currentPage, currentUser } = this.props;
+ const { currentPage, currentUser, onSonarCloud } = this.props;
- if (!isLoggedIn(currentUser)) {
+ if (!isLoggedIn(currentUser) || !onSonarCloud) {
return null;
}
const tooltip = checked ? translate('homepage.current') : translate('homepage.check');
return (
- <Tooltip overlay={tooltip}>
+ <Tooltip overlay={tooltip} placement="left">
{checked ? (
<span className={classNames('display-inline-block', this.props.className)}>
<HomeIcon filled={checked} />
}
}
-const mapStateToProps = (state: any): StateProps => ({
- currentUser: getCurrentUser(state)
-});
+const mapStateToProps = (state: any): StateProps => {
+ const sonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+
+ return {
+ currentUser: getCurrentUser(state),
+ onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
+ };
+};
const mapDispatchToProps: DispatchProps = { setHomePage };
import Favorite from '../Favorite';
it('renders', () => {
- expect(shallow(<Favorite component="foo" favorite={true} />)).toMatchSnapshot();
+ expect(shallow(<Favorite component="foo" favorite={true} qualifier="TRK" />)).toMatchSnapshot();
});
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import FavoriteBase from '../FavoriteBase';
+import FavoriteBase, { Props } from '../FavoriteBase';
import { click } from '../../../helpers/testUtils';
it('should render favorite', () => {
expect(removeFavorite).toBeCalled();
});
-function renderFavoriteBase(props?: any) {
+function renderFavoriteBase(props: Partial<Props> = {}) {
return shallow(
- <FavoriteBase favorite={true} addFavorite={jest.fn()} removeFavorite={jest.fn()} {...props} />
+ <FavoriteBase
+ favorite={true}
+ addFavorite={jest.fn()}
+ qualifier="TRK"
+ removeFavorite={jest.fn()}
+ {...props}
+ />
);
}
<FavoriteBase
addFavorite={[Function]}
favorite={true}
+ qualifier="TRK"
removeFavorite={[Function]}
/>
`;
exports[`should render favorite 1`] = `
<Tooltip
- overlay="favorite.current"
- placement="bottom"
+ overlay="favorite.current.TRK"
+ placement="left"
>
<a
className="display-inline-block link-no-underline"
exports[`should render not favorite 1`] = `
<Tooltip
- overlay="favorite.check"
- placement="bottom"
+ overlay="favorite.check.TRK"
+ placement="left"
>
<a
className="display-inline-block link-no-underline"
.navbar-context,
.navbar-context .navbar-inner {
- background-color: var(--barBackgroundColor);
+ background-color: #fff;
z-index: 420;
}
}
.navbar-context-header {
- display: inline-flex;
+ display: flex;
align-items: center;
height: calc(4 * var(--gridSize));
font-size: var(--bigFontSize);
top: 36px;
right: 0;
padding: 0 20px;
+ white-space: nowrap;
}
.navbar-context-description {
import { omitBy, isNil } from 'lodash';
import { isShortLivingBranch } from './branches';
import { getProfilePath } from '../apps/quality-profiles/utils';
-import { Branch, HomePage } from '../app/types';
+import { Branch, HomePage, HomePageType } from '../app/types';
interface Query {
[x: string]: string | undefined;
export function getHomePageUrl(homepage: HomePage) {
switch (homepage.type) {
- case 'project':
- return getProjectUrl(homepage.key!);
- case 'organization':
- return getOrganizationUrl(homepage.key!);
- case 'my-projects':
+ case HomePageType.Project:
+ return getProjectUrl(homepage.parameter!);
+ case HomePageType.Organization:
+ return getOrganizationUrl(homepage.parameter!);
+ case HomePageType.MyProjects:
return '/projects';
- case 'my-issues':
+ case HomePageType.MyIssues:
return { pathname: '/issues', query: { resolved: 'false' } };
}
#
#------------------------------------------------------------------------------
notification.channel.EmailNotificationChannel=Email
-notification.dispatcher.information=Receive notifications when specific types of events occur. A notification is never sent to the author of the event.
+notification.dispatcher.information=A notification is never sent to the author of the event.
notification.dispatcher.ChangesOnMyIssue=Changes in issues assigned to me
notification.dispatcher.NewIssues=New issues
notification.dispatcher.NewAlerts=New quality gate status
# FAVORITE
#
#------------------------------------------------------------------------------
-favorite.current=This is your favorite component. Click to unset.
-favorite.check=Click to mark this component as favorite.
+favorite.check.TRK=Click to mark this project as favorite.
+favorite.check.BRC=Click to mark this sub-project as favorite.
+favorite.check.DIR=Click to mark this directory as favorite.
+favorite.check.PAC=Click to mark this package as favorite.
+favorite.check.VW=Click to mark this portfolio as favorite.
+favorite.check.SVW=Click to mark this sub-ortfolio as favorite.
+favorite.check.APP=Click to mark this application as favorite.
+favorite.check.FIL=Click to mark this file as favorite.
+favorite.check.CLA=Click to mark this file as favorite.
+favorite.check.UTS=Click to mark this test file as favorite.
+
+favorite.current.TRK=This project is marked as favorite.
+favorite.current.BRC=This sub-project is marked as favorite.
+favorite.current.DIR=This directory is marked as favorite.
+favorite.current.PAC=This package is marked as favorite.
+favorite.current.VW=This portfolio is marked as favorite.
+favorite.current.SVW=This sub-ortfolio is marked as favorite.
+favorite.current.APP=This application is marked as favorite.
+favorite.current.FIL=This file is marked as favorite.
+favorite.current.CLA=This file is marked as favorite.
+favorite.current.UTS=This test file is marked as favorite.