diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-12-14 12:20:38 +0100 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-01-02 10:38:10 +0100 |
commit | 7b757f90537d99f1863407dfd2667874561d53aa (patch) | |
tree | 8c19f29af2c49718805962a2ab824e8ffaef0b83 /server/sonar-web | |
parent | 027514e6f94607fcd7df8e69e668fe32aeb2873e (diff) | |
download | sonarqube-7b757f90537d99f1863407dfd2667874561d53aa.tar.gz sonarqube-7b757f90537d99f1863407dfd2667874561d53aa.zip |
SONAR-10188 Update project/organization header
Diffstat (limited to 'server/sonar-web')
94 files changed, 1767 insertions, 1720 deletions
diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx index e87bea11cce..c097f03da96 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx @@ -21,32 +21,16 @@ import * as React from 'react'; 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> + ); } diff --git a/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx b/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx index 4e5e35b7cce..8c2efc977af 100644 --- a/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx @@ -27,35 +27,19 @@ interface Props { 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> + ); } diff --git a/server/sonar-web/src/main/js/app/components/extensions/ExtensionNotFound.tsx b/server/sonar-web/src/main/js/app/components/extensions/ExtensionNotFound.tsx index 154b674f6b9..1cad19e3d20 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/ExtensionNotFound.tsx +++ b/server/sonar-web/src/main/js/app/components/extensions/ExtensionNotFound.tsx @@ -20,34 +20,18 @@ 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> + ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css index fd0f3a46df1..f9488b415e3 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css @@ -1,9 +1,9 @@ .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 { diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx index ff2205adbb6..7d4cd235925 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx @@ -18,8 +18,7 @@ * 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'; @@ -111,16 +110,13 @@ export default class ComponentNav extends React.PureComponent<Props, State> { 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} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavHeader.tsx index c8a75e8a105..e042b010d2e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavHeader.tsx @@ -20,7 +20,8 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router'; -import { Component, Organization } from '../../../types'; +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'; @@ -36,46 +37,16 @@ interface StateProps { } interface OwnProps { + branches: Branch[]; component: Component; + currentBranch?: Branch; + location?: any; } interface Props extends StateProps, OwnProps {} -export function ComponentNavBreadcrumbs(props: Props) { +export function ComponentNavHeader(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"> @@ -84,28 +55,59 @@ export function ComponentNavBreadcrumbs(props: Props) { 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> + <> + <OrganizationAvatar organization={organization} /> + <OrganizationLink + organization={organization} + className="link-base-color link-no-underline spacer-left"> + {organization.name} + </OrganizationLink> + <span className="slash-separator" /> + </> )} - {organization && shouldOrganizationBeDisplayed && <span className="slash-separator" />} - {items} + {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)(ComponentNavBreadcrumbs); +export default connect(mapStateToProps)(ComponentNavHeader); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx index 01be74777a6..3b080ac9d4b 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx @@ -19,7 +19,7 @@ */ 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'; @@ -60,14 +60,22 @@ export function ComponentNavMeta({ branch, component, currentUser }: Props) { {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> ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx index 26f876d34f0..4c30ef3b3a4 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx @@ -29,9 +29,9 @@ jest.mock('../ComponentNavMeta', () => ({ } })); -jest.mock('../ComponentNavBreadcrumbs', () => ({ +jest.mock('../ComponentNavHeader', () => ({ // eslint-disable-next-line - default: function ComponentNavBreadcrumbs() { + default: function ComponentNavHeader() { return null; } })); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavHeader-test.tsx index 78a2eed1234..89d10f4ac78 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavHeader-test.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { shallow } from 'enzyme'; -import { ComponentNavBreadcrumbs } from '../ComponentNavBreadcrumbs'; +import { ComponentNavHeader } from '../ComponentNavHeader'; import { Visibility } from '../../../../types'; it('should not render breadcrumbs with one element', () => { @@ -32,7 +32,7 @@ it('should not render breadcrumbs with one element', () => { visibility: 'public' }; const result = shallow( - <ComponentNavBreadcrumbs component={component} shouldOrganizationBeDisplayed={false} /> + <ComponentNavHeader branches={[]} component={component} shouldOrganizationBeDisplayed={false} /> ); expect(result).toMatchSnapshot(); }); @@ -52,7 +52,8 @@ it('should render organization', () => { projectVisibility: Visibility.Public }; const result = shallow( - <ComponentNavBreadcrumbs + <ComponentNavHeader + branches={[]} component={component} organization={organization} shouldOrganizationBeDisplayed={true} @@ -71,7 +72,7 @@ it('renders private badge', () => { visibility: 'private' }; const result = shallow( - <ComponentNavBreadcrumbs component={component} shouldOrganizationBeDisplayed={false} /> + <ComponentNavHeader branches={[]} component={component} shouldOrganizationBeDisplayed={false} /> ); expect(result.find('PrivateBadge')).toHaveLength(1); }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap index 6fea2eff38e..25470b77544 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap @@ -27,7 +27,8 @@ exports[`renders 1`] = ` /> } > - <ComponentNavBreadcrumbs + <ComponentNavHeader + branches={Array []} component={ Object { "breadcrumbs": Array [ @@ -43,6 +44,7 @@ exports[`renders 1`] = ` "qualifier": "TRK", } } + location={Object {}} /> <ComponentNavMeta component={ diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.tsx.snap deleted file mode 100644 index e907a97d1b5..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.tsx.snap +++ /dev/null @@ -1,98 +0,0 @@ -// 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> -`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap new file mode 100644 index 00000000000..eb990ac6401 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap @@ -0,0 +1,104 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap index 726f3168f9f..437ab7a3477 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap @@ -38,20 +38,24 @@ exports[`renders status of short-living branch 1`] = ` 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> `; diff --git a/server/sonar-web/src/main/js/app/styles/components/page.css b/server/sonar-web/src/main/js/app/styles/components/page.css index 53855c6747f..1ef923cf2b9 100644 --- a/server/sonar-web/src/main/js/app/styles/components/page.css +++ b/server/sonar-web/src/main/js/app/styles/components/page.css @@ -17,6 +17,10 @@ * 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; @@ -150,7 +154,8 @@ 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; } diff --git a/server/sonar-web/src/main/js/app/styles/init/base.css b/server/sonar-web/src/main/js/app/styles/init/base.css index 4b290fb3096..72a57e214d2 100644 --- a/server/sonar-web/src/main/js/app/styles/init/base.css +++ b/server/sonar-web/src/main/js/app/styles/init/base.css @@ -26,7 +26,7 @@ html, body { - background-color: #fff; + background-color: var(--barBackgroundColor); } body { diff --git a/server/sonar-web/src/main/js/app/styles/init/icons.css b/server/sonar-web/src/main/js/app/styles/init/icons.css index 2033873f784..407f8237df7 100644 --- a/server/sonar-web/src/main/js/app/styles/init/icons.css +++ b/server/sonar-web/src/main/js/app/styles/init/icons.css @@ -485,6 +485,7 @@ a:hover > .icon-radio { stroke-width: 1.41421356; stroke-opacity: 1; fill-opacity: 0; + vector-effect: non-scaling-stroke; transition: all 0.2s ease; } diff --git a/server/sonar-web/src/main/js/app/styles/print.css b/server/sonar-web/src/main/js/app/styles/print.css index 63fcfb1ee50..696c300636a 100644 --- a/server/sonar-web/src/main/js/app/styles/print.css +++ b/server/sonar-web/src/main/js/app/styles/print.css @@ -31,11 +31,10 @@ 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; diff --git a/server/sonar-web/src/main/js/app/styles/style.css b/server/sonar-web/src/main/js/app/styles/style.css index 921f952f4ee..a60b872bbc7 100644 --- a/server/sonar-web/src/main/js/app/styles/style.css +++ b/server/sonar-web/src/main/js/app/styles/style.css @@ -17,11 +17,6 @@ * 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; diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 70df316dea2..f9f9be013ac 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -62,13 +62,15 @@ export interface Extension { 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[]; @@ -141,13 +143,20 @@ export interface CurrentUser { 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 { diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutApp.js b/server/sonar-web/src/main/js/apps/about/components/AboutApp.js index 267d0fc7cf1..d0369971567 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutApp.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutApp.js @@ -76,11 +76,15 @@ class AboutApp extends React.PureComponent { 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() { diff --git a/server/sonar-web/src/main/js/apps/account/account.css b/server/sonar-web/src/main/js/apps/account/account.css index 12c3cabf574..73d42282e13 100644 --- a/server/sonar-web/src/main/js/apps/account/account.css +++ b/server/sonar-web/src/main/js/apps/account/account.css @@ -8,7 +8,7 @@ padding-top: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--barBorderColor); - background-color: var(--barBackgroundColor); + background-color: #fff; } .account-nav { diff --git a/server/sonar-web/src/main/js/apps/account/components/Password.js b/server/sonar-web/src/main/js/apps/account/components/Password.js index dec082bc69b..6e3bb3c3c69 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Password.js +++ b/server/sonar-web/src/main/js/apps/account/components/Password.js @@ -70,10 +70,10 @@ export default class Password extends Component { 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> )} diff --git a/server/sonar-web/src/main/js/apps/account/components/Security.js b/server/sonar-web/src/main/js/apps/account/components/Security.js index d52a3b6e1ea..3006ca55f1a 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Security.js +++ b/server/sonar-web/src/main/js/apps/account/components/Security.js @@ -31,11 +31,7 @@ function Security(props) { 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> ); diff --git a/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js b/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js index d7ba3d35197..294c2c209c6 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js +++ b/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js @@ -46,30 +46,32 @@ type Props = { 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> ); } diff --git a/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js b/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js index aa2c8d1179e..6e5fed53bcc 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js +++ b/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js @@ -40,13 +40,8 @@ class Notifications extends React.PureComponent { 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> ); diff --git a/server/sonar-web/src/main/js/apps/account/notifications/Projects.js b/server/sonar-web/src/main/js/apps/account/notifications/Projects.js index 14a7f74020c..5345b55591a 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/Projects.js +++ b/server/sonar-web/src/main/js/apps/account/notifications/Projects.js @@ -113,30 +113,32 @@ class Projects extends React.PureComponent { 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> ); diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap index 4a7d7d10c5a..64a62011d96 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap @@ -1,69 +1,73 @@ // 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> `; diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap index 8541cc01be7..855ccecb356 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap @@ -10,14 +10,11 @@ exports[`should match snapshot 1`] = ` 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> `; diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap index 3d6b8b28eb1..7f2fb250868 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap @@ -1,178 +1,190 @@ // 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> `; diff --git a/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx b/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx index 5b0b8ccc367..42273f32553 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx +++ b/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx @@ -21,12 +21,17 @@ import * as React from 'react'; 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( diff --git a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx index e6f34fbd8e2..d63555b1574 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx +++ b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx @@ -87,29 +87,22 @@ class UserOrganizations extends React.PureComponent<Props, State> { <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 diff --git a/server/sonar-web/src/main/js/apps/account/profile/Profile.js b/server/sonar-web/src/main/js/apps/account/profile/Profile.js index 4996bfcd124..464f549d5bd 100644 --- a/server/sonar-web/src/main/js/apps/account/profile/Profile.js +++ b/server/sonar-web/src/main/js/apps/account/profile/Profile.js @@ -45,29 +45,31 @@ function Profile(props /*: Props */) { 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> ); } diff --git a/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs b/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs index 2c8da3e0297..65bbf91e160 100644 --- a/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs +++ b/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs @@ -1,77 +1,81 @@ -<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> diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js index 0f0f843a1f5..56f9d0508db 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js @@ -49,34 +49,36 @@ export default class Tasks extends React.PureComponent { }); 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> ); } } diff --git a/server/sonar-web/src/main/js/apps/code/components/App.tsx b/server/sonar-web/src/main/js/apps/code/components/App.tsx index 77110f20481..0a5f41af961 100644 --- a/server/sonar-web/src/main/js/apps/code/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/App.tsx @@ -200,7 +200,9 @@ export default class App extends React.PureComponent<Props, State> { 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"> diff --git a/server/sonar-web/src/main/js/apps/code/components/Search.tsx b/server/sonar-web/src/main/js/apps/code/components/Search.tsx index 14539241c9a..fba3a124f5d 100644 --- a/server/sonar-web/src/main/js/apps/code/components/Search.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/Search.tsx @@ -182,12 +182,14 @@ export default class Search extends React.PureComponent<Props, State> { {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> ); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js index 7b8eb343c90..1c578244c01 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js @@ -44,6 +44,9 @@ class CodingRulesAppContainer extends React.PureComponent { */ 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( @@ -62,6 +65,9 @@ class CodingRulesAppContainer extends React.PureComponent { } componentWillUnmount() { + // $FlowFixMe + document.body.classList.remove('white-page'); + if (this.stop) { this.stop(); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.js b/server/sonar-web/src/main/js/apps/component-measures/components/App.js index 84bd294f9e9..7611ea8b4c8 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/App.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.js @@ -74,6 +74,8 @@ export default class App extends React.PureComponent { componentDidMount() { this.mounted = true; + // $FlowFixMe + document.body.classList.add('white-page'); this.props.fetchMetrics(); this.fetchMeasures(this.props); key.setScope('measures-files'); @@ -95,6 +97,8 @@ export default class App extends React.PureComponent { componentWillUnmount() { this.mounted = false; + // $FlowFixMe + document.body.classList.remove('white-page'); key.deleteScope('measures-files'); const footer = document.getElementById('footer'); if (footer) { diff --git a/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list.hbs b/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list.hbs index 20ca1491fa3..97c513248eb 100644 --- a/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list.hbs +++ b/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list.hbs @@ -1,12 +1,14 @@ -<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> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-list.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-list.hbs index b96143e7caa..ac23f3c1f65 100644 --- a/server/sonar-web/src/main/js/apps/groups/templates/groups-list.hbs +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-list.hbs @@ -1,4 +1,4 @@ -<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"> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs index e0d8614362f..ecf034da48e 100644 --- a/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs @@ -1,4 +1,4 @@ -<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;"> diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js index cc68fbb41c9..c13b7a9b0ff 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.js +++ b/server/sonar-web/src/main/js/apps/issues/components/App.js @@ -151,6 +151,9 @@ export default class App extends React.PureComponent { return; } + // $FlowFixMe + document.body.classList.add('white-page'); + const footer = document.getElementById('footer'); if (footer) { footer.classList.add('page-footer-with-sidebar'); @@ -206,6 +209,9 @@ export default class App extends React.PureComponent { componentWillUnmount() { this.detachShortcuts(); + // $FlowFixMe + document.body.classList.remove('white-page'); + const footer = document.getElementById('footer'); if (footer) { footer.classList.remove('page-footer-with-sidebar'); diff --git a/server/sonar-web/src/main/js/apps/issues/components/PageActions.js b/server/sonar-web/src/main/js/apps/issues/components/PageActions.js index 2aaca8517e8..b800d271f3f 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/PageActions.js +++ b/server/sonar-web/src/main/js/apps/issues/components/PageActions.js @@ -22,6 +22,7 @@ import React from 'react'; 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'; @@ -74,7 +75,10 @@ export default class PageActions extends React.PureComponent { </div> {this.props.canSetHome && ( - <HomePageSelect className="huge-spacer-left" currentPage={{ type: 'my-issues' }} /> + <HomePageSelect + className="huge-spacer-left" + currentPage={{ type: HomePageType.MyIssues }} + /> )} </div> ); diff --git a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx index 48924aba04e..c41ac4bf516 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx @@ -79,7 +79,7 @@ export default class PluginsList extends React.PureComponent<Props> { 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"> diff --git a/server/sonar-web/src/main/js/apps/marketplace/Search.tsx b/server/sonar-web/src/main/js/apps/marketplace/Search.tsx index 1eaef17728e..c114ebb6643 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/Search.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/Search.tsx @@ -48,7 +48,7 @@ export default class Search extends React.PureComponent<Props> { } ]; 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" diff --git a/server/sonar-web/src/main/js/apps/marketplace/style.css b/server/sonar-web/src/main/js/apps/marketplace/style.css index b435f1c3367..35aee754fed 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/style.css +++ b/server/sonar-web/src/main/js/apps/marketplace/style.css @@ -12,7 +12,6 @@ display: flex; flex-direction: column; justify-content: space-between; - background-color: #f3f3f3; margin-left: 8px; margin-right: 8px; } diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs index 9aeeeede6b3..179ee3ed067 100644 --- a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs @@ -1,5 +1,5 @@ <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> diff --git a/server/sonar-web/src/main/js/apps/organizations/components/MembersList.js b/server/sonar-web/src/main/js/apps/organizations/components/MembersList.js index c40344c98bd..af7bf261a15 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/MembersList.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/MembersList.js @@ -38,20 +38,22 @@ export default class MembersList extends React.PureComponent { 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> ); } } diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js index 5f7a3c867ef..92ceb30e062 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js @@ -107,87 +107,89 @@ class OrganizationEdit extends React.PureComponent { <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> ); } diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersList-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersList-test.js.snap index 4bc29637672..fc8f227d95e 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersList-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersList-test.js.snap @@ -1,44 +1,48 @@ // 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> `; diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap index 9dc712473f2..9c18fd59ac8 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap @@ -18,118 +18,122 @@ exports[`smoke test 1`] = ` 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> `; @@ -151,133 +155,137 @@ exports[`smoke test 2`] = ` 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> `; @@ -299,135 +307,139 @@ exports[`smoke test 3`] = ` 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> `; diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx index 077cc4a67ee..eb49059477f 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx @@ -18,15 +18,21 @@ * 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 && ( @@ -41,9 +47,23 @@ export default function OrganizationNavigationMeta({ organization }: Props) { <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); diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigationMeta-test.tsx b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigationMeta-test.tsx index 5b69f2c4853..ef2d8fca1dd 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigationMeta-test.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigationMeta-test.tsx @@ -19,13 +19,14 @@ */ 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', diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap index eb04cc431fe..07512698807 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap @@ -14,7 +14,7 @@ exports[`render 1`] = ` } } /> - <OrganizationNavigationMeta + <Connect(OrganizationNavigationMeta) organization={ Object { "key": "foo", diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap index bdd7594127b..207f8fe1faf 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap @@ -20,8 +20,8 @@ exports[`renders 1`] = ` <Connect(HomePageSelect) currentPage={ Object { - "key": "foo", - "type": "organization", + "parameter": "foo", + "type": "ORGANIZATION", } } /> diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js index d5cb1b15caf..16d88755c47 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js +++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js @@ -68,10 +68,6 @@ export default class OverviewApp extends React.PureComponent { componentDidMount() { this.mounted = true; - const domElement = document.querySelector('html'); - if (domElement) { - domElement.classList.add('dashboard-page'); - } this.loadMeasures().then(this.loadHistory); } @@ -86,10 +82,6 @@ export default class OverviewApp extends React.PureComponent { componentWillUnmount() { this.mounted = false; - const domElement = document.querySelector('html'); - if (domElement) { - domElement.classList.remove('dashboard-page'); - } } loadMeasures() { diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/List.js b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js index 9448e9d5797..97182a9b41c 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/List.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js @@ -44,10 +44,12 @@ export default class List extends React.PureComponent { )); 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> ); } } diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx b/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx index 189351ba346..798e2156ceb 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx @@ -92,14 +92,16 @@ export default class HoldersList extends React.PureComponent<Props> { )); 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> ); } } diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx index 9f1ee5f2a76..f4074fa61a1 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx @@ -50,10 +50,6 @@ export default class App extends React.PureComponent<Props, State> { componentDidMount() { this.mounted = true; - const html = document.querySelector('html'); - if (html) { - html.classList.add('dashboard-page'); - } this.fetchData(); } @@ -65,10 +61,6 @@ export default class App extends React.PureComponent<Props, State> { componentWillUnmount() { this.mounted = false; - const html = document.querySelector('html'); - if (html) { - html.classList.remove('dashboard-page'); - } } fetchData() { diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js index e94839f6b2e..0288b2e9fb5 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js @@ -101,7 +101,7 @@ class Key extends React.PureComponent { )} {hasModules && ( - <div> + <div className="boxed-group boxed-group-inner"> <div className="big-spacer-bottom"> <ul className="tabs"> <li> diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Table.js b/server/sonar-web/src/main/js/apps/project-admin/links/Table.js index 580413d4d1f..453c93ad44a 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/links/Table.js +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Table.js @@ -54,10 +54,12 @@ export default class Table extends React.PureComponent { )); 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> ); } } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js index 080842999d8..70c931e1cf7 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js @@ -94,8 +94,6 @@ export default class ProjectActivityAppContainer extends React.PureComponent { 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)) { @@ -129,8 +127,6 @@ export default class ProjectActivityAppContainer extends React.PureComponent { componentWillUnmount() { this.mounted = false; - const elem = document.querySelector('html'); - elem && elem.classList.remove('dashboard-page'); } addCustomEvent = (analysis /*: string */, name /*: string */, category /*: ?string */) => diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx index 19092afe655..f11038f2b18 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx @@ -119,26 +119,30 @@ export default class App extends React.PureComponent<Props, State> { {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> ); } diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap index 4914db6ffce..e9d41bf777e 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap @@ -41,69 +41,73 @@ exports[`renders sorted list of branches 1`] = ` /> </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> `; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx index 88f51601856..1e6374406ec 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx @@ -44,16 +44,18 @@ export default function Table(props: Props) { )); 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> ); } diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap index 5ac9cea89da..b77fae2826b 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap @@ -1,32 +1,65 @@ // 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, @@ -35,44 +68,26 @@ exports[`renders 1`] = ` "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, @@ -81,21 +96,10 @@ exports[`renders 1`] = ` "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> `; diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx index 15dc8f4e7ca..b8c83faa629 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx @@ -70,11 +70,6 @@ export default class AllProjects extends React.PureComponent<Props, State> { 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; @@ -95,11 +90,6 @@ export default class AllProjects extends React.PureComponent<Props, State> { 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'); diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx index 3f347339ad0..0b7666c9816 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx @@ -23,7 +23,7 @@ import SearchFilterContainer from '../filters/SearchFilterContainer'; 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'; @@ -101,12 +101,12 @@ export default function PageHeader(props: Props) { )} </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> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx index 8a5b4f6d216..3c79acd0113 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx @@ -49,6 +49,7 @@ export default function ProjectCardLeak({ organization, project }: Props) { className="spacer-right" component={project.key} favorite={project.isFavorite} + qualifier="TRK" /> )} <h2 className="project-card-name"> diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx index d8ecbe86f66..79926daf027 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx @@ -48,6 +48,7 @@ export default function ProjectCardOverall({ organization, project }: Props) { className="spacer-right" component={project.key} favorite={project.isFavorite} + qualifier="TRK" /> )} <h2 className="project-card-name"> diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx index d3399685066..81a0bb7102f 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx @@ -50,32 +50,34 @@ export default class Projects extends React.PureComponent<Props> { 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> ); } } diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap index 7a3728a28ad..1503fed2c4e 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap @@ -1,67 +1,71 @@ // 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> `; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js index 0ec8d28794d..4386f8cca8c 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js @@ -37,6 +37,8 @@ export default class QualityGatesApp extends Component { componentDidMount() { this.fetchQualityGates(); + // $FlowFixMe + document.body.classList.add('white-page'); const footer = document.getElementById('footer'); if (footer) { footer.classList.add('page-footer-with-sidebar'); @@ -44,6 +46,8 @@ export default class QualityGatesApp extends Component { } componentWillUnmount() { + // $FlowFixMe + document.body.classList.remove('white-page'); const footer = document.getElementById('footer'); if (footer) { footer.classList.remove('page-footer-with-sidebar'); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx index 8f8f056be4e..fda9bb7ed26 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx @@ -43,13 +43,6 @@ export default class App extends React.PureComponent<Props, State> { 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(); @@ -57,10 +50,6 @@ export default class App extends React.PureComponent<Props, State> { componentWillUnmount() { this.mounted = false; - const html = document.querySelector('html'); - if (html) { - html.classList.remove('dashboard-page'); - } } fetchProfiles() { diff --git a/server/sonar-web/src/main/js/apps/settings/components/App.js b/server/sonar-web/src/main/js/apps/settings/components/App.js index 7df88bb9fb7..a2c05ac3e3f 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/App.js +++ b/server/sonar-web/src/main/js/apps/settings/components/App.js @@ -47,10 +47,6 @@ export default class App extends React.PureComponent { 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 })); } @@ -62,13 +58,6 @@ export default class App extends React.PureComponent { } } - componentWillUnmount() { - const html = document.querySelector('html'); - if (html) { - html.classList.remove('dashboard-page'); - } - } - render() { if (!this.state.loaded) { return null; diff --git a/server/sonar-web/src/main/js/apps/users/UsersList.tsx b/server/sonar-web/src/main/js/apps/users/UsersList.tsx index a81a9441af3..9609acd0a9b 100644 --- a/server/sonar-web/src/main/js/apps/users/UsersList.tsx +++ b/server/sonar-web/src/main/js/apps/users/UsersList.tsx @@ -38,31 +38,33 @@ export default function UsersList({ 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> ); } diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap index 4b732ab0f36..64c13cf48e9 100644 --- a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap @@ -1,64 +1,68 @@ // 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> `; diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx index f0b5eed39e6..5126f39b525 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx +++ b/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx @@ -120,7 +120,7 @@ export default class Action extends React.PureComponent<Props, State> { ); } - return <hr />; + return null; } render() { @@ -130,8 +130,8 @@ export default class Action extends React.PureComponent<Props, State> { 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"> @@ -161,26 +161,28 @@ export default class Action extends React.PureComponent<Props, State> { )} </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> ); } diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap index 02cc7f1b708..ac543cd86e3 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap @@ -91,11 +91,11 @@ exports[`should display the response example 1`] = ` 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" @@ -118,43 +118,47 @@ exports[`should render correctly 1`] = ` </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> `; diff --git a/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css b/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css index c99b781ab07..ea4ec21da9d 100644 --- a/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css +++ b/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css @@ -26,10 +26,7 @@ } .web-api-domain-actions { -} - -.web-api-action { - padding-top: 30px; + margin-top: calc(2 * var(--gridSize)); } .web-api-action-title { diff --git a/server/sonar-web/src/main/js/components/SourceViewer/styles.css b/server/sonar-web/src/main/js/components/SourceViewer/styles.css index 8c9498bec49..c69d07eb481 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/styles.css +++ b/server/sonar-web/src/main/js/components/SourceViewer/styles.css @@ -20,7 +20,7 @@ .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; diff --git a/server/sonar-web/src/main/js/components/common/BranchStatus.css b/server/sonar-web/src/main/js/components/common/BranchStatus.css index ccfccb351f1..ac0a177c6a5 100644 --- a/server/sonar-web/src/main/js/components/common/BranchStatus.css +++ b/server/sonar-web/src/main/js/components/common/BranchStatus.css @@ -1,5 +1,8 @@ .branch-status { + display: flex; + align-items: center; min-width: 64px; + line-height: calc(2 * var(--gridSize)); text-align: right; } diff --git a/server/sonar-web/src/main/js/components/common/BranchStatus.tsx b/server/sonar-web/src/main/js/components/common/BranchStatus.tsx index bb9069bc234..e0e4fa55b1d 100644 --- a/server/sonar-web/src/main/js/components/common/BranchStatus.tsx +++ b/server/sonar-web/src/main/js/components/common/BranchStatus.tsx @@ -44,26 +44,26 @@ export default function BranchStatus({ branch, concise = false }: Props) { 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> diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap index 8833bab3ceb..d648e77db49 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap @@ -16,7 +16,7 @@ exports[`renders status of long-living branches 2`] = ` exports[`renders status of short-living branches 1`] = ` <ul - className="list-inline branch-status" + className="branch-status" > <li className="spacer-right" @@ -26,15 +26,21 @@ exports[`renders status of short-living branches 1`] = ` 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> @@ -43,7 +49,7 @@ exports[`renders status of short-living branches 1`] = ` exports[`renders status of short-living branches 2`] = ` <ul - className="list-inline branch-status" + className="branch-status" > <li className="spacer-right" @@ -53,15 +59,21 @@ exports[`renders status of short-living branches 2`] = ` 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> @@ -70,7 +82,7 @@ exports[`renders status of short-living branches 2`] = ` exports[`renders status of short-living branches 3`] = ` <ul - className="list-inline branch-status" + className="branch-status" > <li className="spacer-right" @@ -80,15 +92,21 @@ exports[`renders status of short-living branches 3`] = ` 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> diff --git a/server/sonar-web/src/main/js/components/controls/Favorite.tsx b/server/sonar-web/src/main/js/components/controls/Favorite.tsx index fb43626c05c..5ac11f9527a 100644 --- a/server/sonar-web/src/main/js/components/controls/Favorite.tsx +++ b/server/sonar-web/src/main/js/components/controls/Favorite.tsx @@ -25,13 +25,13 @@ interface Props { 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)} /> diff --git a/server/sonar-web/src/main/js/components/controls/FavoriteBase.tsx b/server/sonar-web/src/main/js/components/controls/FavoriteBase.tsx index f802e0885f7..a1f738dcba3 100644 --- a/server/sonar-web/src/main/js/components/controls/FavoriteBase.tsx +++ b/server/sonar-web/src/main/js/components/controls/FavoriteBase.tsx @@ -23,10 +23,11 @@ import Tooltip from './Tooltip'; 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>; } @@ -83,10 +84,10 @@ export default class FavoriteBase extends React.PureComponent<Props, State> { 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="#" diff --git a/server/sonar-web/src/main/js/components/controls/FavoriteIssueFilter.js b/server/sonar-web/src/main/js/components/controls/FavoriteIssueFilter.js deleted file mode 100644 index c00c2993d04..00000000000 --- a/server/sonar-web/src/main/js/components/controls/FavoriteIssueFilter.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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)} - /> - ); - } -} diff --git a/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx b/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx index 9d68ef7e1fc..572e65cb5ae 100644 --- a/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx +++ b/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx @@ -24,11 +24,12 @@ import Tooltip from './Tooltip'; 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 { @@ -48,9 +49,9 @@ class HomePageSelect extends React.PureComponent<Props> { }; render() { - const { currentPage, currentUser } = this.props; + const { currentPage, currentUser, onSonarCloud } = this.props; - if (!isLoggedIn(currentUser)) { + if (!isLoggedIn(currentUser) || !onSonarCloud) { return null; } @@ -59,7 +60,7 @@ class HomePageSelect extends React.PureComponent<Props> { 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} /> @@ -81,9 +82,14 @@ class HomePageSelect extends React.PureComponent<Props> { } } -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 }; diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Favorite-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/Favorite-test.tsx index 0601fc5bd29..05bfc43bfa9 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/Favorite-test.tsx +++ b/server/sonar-web/src/main/js/components/controls/__tests__/Favorite-test.tsx @@ -22,5 +22,5 @@ import { shallow } from 'enzyme'; import Favorite from '../Favorite'; it('renders', () => { - expect(shallow(<Favorite component="foo" favorite={true} />)).toMatchSnapshot(); + expect(shallow(<Favorite component="foo" favorite={true} qualifier="TRK" />)).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.tsx index a6231ceb7ff..3d93e7f9159 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.tsx +++ b/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.tsx @@ -19,7 +19,7 @@ */ 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', () => { @@ -46,8 +46,14 @@ it('should remove 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} + /> ); } diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Favorite-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Favorite-test.tsx.snap index dd978f6e724..592afbf9398 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Favorite-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Favorite-test.tsx.snap @@ -4,6 +4,7 @@ exports[`renders 1`] = ` <FavoriteBase addFavorite={[Function]} favorite={true} + qualifier="TRK" removeFavorite={[Function]} /> `; diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBase-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBase-test.tsx.snap index bb842b986b0..8186fad3a49 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBase-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBase-test.tsx.snap @@ -2,8 +2,8 @@ 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" @@ -19,8 +19,8 @@ exports[`should render favorite 1`] = ` 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" diff --git a/server/sonar-web/src/main/js/components/nav/ContextNavBar.css b/server/sonar-web/src/main/js/components/nav/ContextNavBar.css index 81a469b239b..3042b4cf120 100644 --- a/server/sonar-web/src/main/js/components/nav/ContextNavBar.css +++ b/server/sonar-web/src/main/js/components/nav/ContextNavBar.css @@ -1,6 +1,6 @@ .navbar-context, .navbar-context .navbar-inner { - background-color: var(--barBackgroundColor); + background-color: #fff; z-index: 420; } @@ -14,7 +14,7 @@ } .navbar-context-header { - display: inline-flex; + display: flex; align-items: center; height: calc(4 * var(--gridSize)); font-size: var(--bigFontSize); @@ -48,6 +48,7 @@ top: 36px; right: 0; padding: 0 20px; + white-space: nowrap; } .navbar-context-description { diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts index 10ab02703cb..f5e53d33c26 100644 --- a/server/sonar-web/src/main/js/helpers/urls.ts +++ b/server/sonar-web/src/main/js/helpers/urls.ts @@ -21,7 +21,7 @@ import { stringify } from 'querystring'; 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; @@ -174,13 +174,13 @@ export function getOrganizationUrl(organization: string) { 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' } }; } |