diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-02-08 12:05:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-08 12:05:28 +0100 |
commit | e497662d7c337fcc7c5293e2a38fd63cf8783bfd (patch) | |
tree | e79714a1c32e06280992ddb739f4ef371475ca38 /server/sonar-web/src/main/js | |
parent | d4055f5f79cee7de87d3aa587f03ceab6f79db4c (diff) | |
download | sonarqube-e497662d7c337fcc7c5293e2a38fd63cf8783bfd.tar.gz sonarqube-e497662d7c337fcc7c5293e2a38fd63cf8783bfd.zip |
SONAR-10375 Project's homepage sidebar improvements (#3032)
Diffstat (limited to 'server/sonar-web/src/main/js')
15 files changed, 148 insertions, 119 deletions
diff --git a/server/sonar-web/src/main/js/app/theme.js b/server/sonar-web/src/main/js/app/theme.js index b5ebfb7b796..04f518b8060 100644 --- a/server/sonar-web/src/main/js/app/theme.js +++ b/server/sonar-web/src/main/js/app/theme.js @@ -70,6 +70,8 @@ module.exports = { contextNavHeightRaw: 9 * grid, + pagePadding: '20px', + // different defaultShadow: '0 6px 12px rgba(0, 0, 0, 0.175)', diff --git a/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx b/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx index b86c74027f4..4a4e572f7c3 100644 --- a/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx +++ b/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx @@ -65,7 +65,7 @@ export default class BadgesModal extends React.PureComponent<Props, State> { const header = translate('overview.badges.title'); const fullBadgeOptions = { branch, project, ...badgeOptions }; return ( - <> + <div className="overview-meta-card"> <button className="js-project-badges" onClick={this.handleOpen}> {translate('overview.badges.get_badge')} </button> @@ -106,7 +106,7 @@ export default class BadgesModal extends React.PureComponent<Props, State> { </footer> </Modal> )} - </> + </div> ); } } diff --git a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap index f43e19a5715..e813fd772bd 100644 --- a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap @@ -1,14 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should display the modal after click 1`] = ` -<React.Fragment> +<div + className="overview-meta-card" +> <button className="js-project-badges" onClick={[Function]} > overview.badges.get_badge </button> -</React.Fragment> +</div> `; exports[`should display the modal after click 2`] = ` diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx index 44285fe03da..4e7dd8c57b5 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx @@ -191,7 +191,7 @@ export class OverviewApp extends React.PureComponent<Props, State> { </div> </div> - <div className="page-sidebar-fixed"> + <div className="overview-sidebar page-sidebar-fixed"> <Meta branch={branchName} component={component} diff --git a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx index ac3f80bb2a8..6984424fa8f 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx +++ b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx @@ -116,7 +116,9 @@ export default class AnalysesList extends React.PureComponent<Props, State> { return ( <div className="overview-meta-card"> - <h4 className="overview-meta-header">{translate('project_activity.page')}</h4> + <h4 className="overview-meta-header"> + {translate('overview.project_activity', this.props.component.qualifier)} + </h4> <PreviewGraph branch={this.props.branch} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx b/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx index 82521e8c55c..4a55b5e29a4 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx @@ -30,6 +30,7 @@ import BadgesModal from '../badges/BadgesModal'; import AnalysesList from '../events/AnalysesList'; import { Visibility, Component, Metric } from '../../../app/types'; import { History } from '../../../api/time-machine'; +import { translate } from '../../../helpers/l10n'; import { MeasureEnhanced } from '../../../helpers/measures'; interface Props { @@ -55,25 +56,18 @@ export default class Meta extends React.PureComponent<Props> { const isProject = qualifier === 'TRK'; const isPrivate = visibility === Visibility.Private; - const hasDescription = !!description; - const hasQualityProfiles = Array.isArray(qualityProfiles) && qualityProfiles.length > 0; - const hasQualityGate = !!qualityGate; - - const shouldShowQualityProfiles = isProject && hasQualityProfiles; - const shouldShowQualityGate = isProject && hasQualityGate; - const hasOrganization = component.organization != null && organizationsEnabled; - return ( <div className="overview-meta"> - {hasDescription && ( - <div className="overview-meta-card overview-meta-description">{description}</div> - )} - - <MetaSize branch={branch} component={component} measures={this.props.measures} /> - - {isProject && ( - <MetaTags component={component} onComponentChange={this.props.onComponentChange} /> - )} + <div className="overview-meta-card"> + <h4 className="overview-meta-header"> + {translate('overview.about_this_project', qualifier)} + </h4> + {description !== undefined && <p className="overview-meta-description">{description}</p>} + {isProject && ( + <MetaTags component={component} onComponentChange={this.props.onComponentChange} /> + )} + <MetaSize branch={branch} component={component} measures={this.props.measures} /> + </div> <AnalysesList branch={branch} @@ -83,27 +77,32 @@ export default class Meta extends React.PureComponent<Props> { qualifier={component.qualifier} /> - {shouldShowQualityGate && ( - <MetaQualityGate - gate={qualityGate} - organization={hasOrganization && component.organization} - /> - )} - - {shouldShowQualityProfiles && ( - <MetaQualityProfiles - component={component} - customOrganizations={organizationsEnabled} - organization={component.organization} - profiles={qualityProfiles} - /> + {isProject && ( + <div className="overview-meta-card"> + {qualityGate && ( + <MetaQualityGate + organization={organizationsEnabled ? component.organization : undefined} + qualityGate={qualityGate} + /> + )} + + {qualityProfiles && + qualityProfiles.length > 0 && ( + <MetaQualityProfiles + headerClassName={qualityGate ? 'big-spacer-top' : undefined} + organization={organizationsEnabled ? component.organization : undefined} + profiles={qualityProfiles} + /> + )} + </div> )} {isProject && <MetaLinks component={component} />} - <MetaKey component={component} /> - - {hasOrganization && <MetaOrganizationKey component={component} />} + <div className="overview-meta-card"> + <MetaKey componentKey={component.key} qualifier={component.qualifier} /> + {organizationsEnabled && <MetaOrganizationKey organization={component.organization} />} + </div> {onSonarCloud && isProject && diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaOrganizationKey.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaKey.tsx index 30bd9aa417b..4d6c5748748 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaOrganizationKey.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaKey.tsx @@ -17,22 +17,23 @@ * 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 * as React from 'react'; +import ClipboardButton from '../../../components/controls/ClipboardButton'; import { translate } from '../../../helpers/l10n'; -const MetaOrganizationKey = ({ component }) => { +interface Props { + componentKey: string; + qualifier: string; +} + +export default function MetaKey({ componentKey, qualifier }: Props) { return ( - <div className="overview-meta-card"> - <h4 className="overview-meta-header">{translate('organization_key')}</h4> - <input - className="overview-key" - type="text" - value={component.organization} - readOnly={true} - onClick={e => e.target.select()} - /> - </div> + <> + <h4 className="overview-meta-header">{translate('overview.project_key', qualifier)}</h4> + <div className="display-flex-center"> + <input className="overview-key" type="text" value={componentKey} readOnly={true} /> + <ClipboardButton className="little-spacer-left" copyValue={componentKey} /> + </div> + </> ); -}; - -export default MetaOrganizationKey; +} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx index 66980dda926..907d76a3877 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx @@ -22,6 +22,7 @@ import MetaLink from './MetaLink'; import { getProjectLinks, ProjectLink } from '../../../api/projectLinks'; import { orderLinks } from '../../project-admin/links/utils'; import { LightComponent } from '../../../app/types'; +import { translate } from '../../../helpers/l10n'; interface Props { component: LightComponent; @@ -71,6 +72,7 @@ export default class MetaLinks extends React.PureComponent<Props, State> { return ( <div className="overview-meta-card"> + <h4 className="overview-meta-header">{translate('overview.external_links')}</h4> <ul className="overview-meta-list"> {orderedLinks.map(link => <MetaLink key={link.id} link={link} />)} </ul> diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaKey.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaOrganizationKey.tsx index cfb43f2d58e..9d8ca5a8000 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaKey.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaOrganizationKey.tsx @@ -17,22 +17,22 @@ * 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 * as React from 'react'; import { translate } from '../../../helpers/l10n'; +import ClipboardButton from '../../../components/controls/ClipboardButton'; -const MetaKey = ({ component }) => { +interface Props { + organization: string; +} + +export default function MetaOrganizationKey({ organization }: Props) { return ( - <div className="overview-meta-card"> - <h4 className="overview-meta-header">{translate('key')}</h4> - <input - className="overview-key" - type="text" - value={component.key} - readOnly={true} - onClick={e => e.target.select()} - /> - </div> + <> + <h4 className="overview-meta-header big-spacer-top">{translate('organization_key')}</h4> + <div className="display-flex-center"> + <input className="overview-key" type="text" value={organization} readOnly={true} /> + <ClipboardButton className="little-spacer-left" copyValue={organization} /> + </div> + </> ); -}; - -export default MetaKey; +} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityGate.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityGate.tsx index 2181137c139..bd713048330 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityGate.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityGate.tsx @@ -17,26 +17,29 @@ * 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 * as React from 'react'; import { Link } from 'react-router'; import { translate } from '../../../helpers/l10n'; import { getQualityGateUrl } from '../../../helpers/urls'; -const MetaQualityGate = ({ gate, organization }) => { +interface Props { + organization?: string; + qualityGate: { isDefault?: boolean; key: string; name: string }; +} + +export default function MetaQualityGate({ qualityGate, organization }: Props) { return ( - <div className="overview-meta-card"> + <> <h4 className="overview-meta-header">{translate('overview.quality_gate')}</h4> <ul className="overview-meta-list"> <li> - {gate.isDefault && ( + {qualityGate.isDefault && ( <span className="note spacer-right">{'(' + translate('default') + ')'}</span> )} - <Link to={getQualityGateUrl(gate.key, organization)}>{gate.name}</Link> + <Link to={getQualityGateUrl(qualityGate.key, organization)}>{qualityGate.name}</Link> </li> </ul> - </div> + </> ); -}; - -export default MetaQualityGate; +} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.tsx index c39bcf3846d..c9648810b85 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.tsx @@ -17,31 +17,33 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router'; +import * as classNames from 'classnames'; import Tooltip from '../../../components/controls/Tooltip'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getQualityProfileUrl } from '../../../helpers/urls'; import { searchRules } from '../../../api/rules'; import { getLanguages } from '../../../store/rootReducer'; -class MetaQualityProfiles extends React.PureComponent { - /*:: mounted: boolean; */ +interface StateProps { + languages: { [key: string]: { name: string } }; +} - /*:: props: { - component: { organization: string }, - customOrganizations: boolean, - languages: { [string]: { name: string } }, - organization: string | void; - profiles: Array<{ key: string, language: string, name: string }> - }; -*/ +interface OwnProps { + headerClassName?: string; + organization?: string; + profiles: { key: string; language: string; name: string }[]; +} - state = { - deprecatedByKey: {} - }; +interface State { + deprecatedByKey: { [key: string]: number }; +} + +class MetaQualityProfiles extends React.PureComponent<StateProps & OwnProps, State> { + mounted: boolean; + state: State = { deprecatedByKey: {} }; componentDidMount() { this.mounted = true; @@ -59,7 +61,7 @@ class MetaQualityProfiles extends React.PureComponent { Promise.all(requests).then( responses => { if (this.mounted) { - const deprecatedByKey = {}; + const deprecatedByKey: { [key: string]: number } = {}; responses.forEach((count, i) => { const profileKey = this.props.profiles[i].key; deprecatedByKey[profileKey] = count; @@ -71,7 +73,7 @@ class MetaQualityProfiles extends React.PureComponent { ); } - loadDeprecatedRulesForProfile(profileKey) { + loadDeprecatedRulesForProfile(profileKey: string) { const data = { activation: 'true', organization: this.props.organization, @@ -82,18 +84,16 @@ class MetaQualityProfiles extends React.PureComponent { return searchRules(data).then(r => r.total); } - getDeprecatedRulesCount(profile) { + getDeprecatedRulesCount(profile: { key: string }) { const count = this.state.deprecatedByKey[profile.key]; return count || 0; } - renderProfile(profile) { + renderProfile(profile: { key: string; language: string; name: string }) { const languageFromStore = this.props.languages[profile.language]; const languageName = languageFromStore ? languageFromStore.name : profile.language; - const path = this.props.customOrganizations - ? getQualityProfileUrl(profile.name, profile.language, this.props.component.organization) - : getQualityProfileUrl(profile.name, profile.language); + const path = getQualityProfileUrl(profile.name, profile.language, this.props.organization); const inner = ( <div className="text-ellipsis"> @@ -117,22 +117,24 @@ class MetaQualityProfiles extends React.PureComponent { } render() { - const { profiles } = this.props; + const { headerClassName, profiles } = this.props; return ( - <div className="overview-meta-card"> - <h4 className="overview-meta-header">{translate('overview.quality_profiles')}</h4> + <> + <h4 className={classNames('overview-meta-header', headerClassName)}> + {translate('overview.quality_profiles')} + </h4> <ul className="overview-meta-list"> {profiles.map(profile => this.renderProfile(profile))} </ul> - </div> + </> ); } } -const mapStateToProps = state => ({ +const mapStateToProps = (state: any) => ({ languages: getLanguages(state) }); -export default connect(mapStateToProps)(MetaQualityProfiles); +export default connect<StateProps, {}, OwnProps>(mapStateToProps)(MetaQualityProfiles); diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx index 6465c861817..335d0b48795 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx @@ -89,7 +89,7 @@ export default class MetaSize extends React.PureComponent<Props> { } return ( - <div id="overview-size" className="overview-meta-card"> + <div className="big-spacer-top" id="overview-size"> {this.props.component.qualifier === 'APP' && this.renderProjects()} {this.renderLoC(ncloc)} {this.renderLoCDistribution()} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx index e0caea4f6fb..209b8a97880 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx @@ -99,7 +99,7 @@ export default class MetaTags extends React.PureComponent<Props, State> { if (this.canUpdateTags()) { return ( - <div className="overview-meta-card overview-meta-tags" ref={card => (this.card = card)}> + <div className="big-spacer-top overview-meta-tags" ref={card => (this.card = card)}> <button className="button-link" onClick={this.handleClick} @@ -120,7 +120,7 @@ export default class MetaTags extends React.PureComponent<Props, State> { ); } else { return ( - <div className="overview-meta-card overview-meta-tags"> + <div className="big-spacer-top overview-meta-tags"> <TagsList allowUpdate={false} className="note" diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap index f7c3da2dfca..1eea19831ab 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should open the tag selector on click 1`] = ` <div - className="overview-meta-card overview-meta-tags" + className="big-spacer-top overview-meta-tags" > <button className="button-link" @@ -23,7 +23,7 @@ exports[`should open the tag selector on click 1`] = ` exports[`should open the tag selector on click 2`] = ` <div - className="overview-meta-card overview-meta-tags" + className="big-spacer-top overview-meta-tags" > <button className="button-link" @@ -62,7 +62,7 @@ exports[`should open the tag selector on click 2`] = ` exports[`should open the tag selector on click 3`] = ` <div - className="overview-meta-card overview-meta-tags" + className="big-spacer-top overview-meta-tags" > <button className="button-link" @@ -83,7 +83,7 @@ exports[`should open the tag selector on click 3`] = ` exports[`should render with tags and admin rights 1`] = ` <div - className="overview-meta-card overview-meta-tags" + className="big-spacer-top overview-meta-tags" > <button className="button-link" @@ -104,7 +104,7 @@ exports[`should render with tags and admin rights 1`] = ` exports[`should render without tags and admin rights 1`] = ` <div - className="overview-meta-card overview-meta-tags" + className="big-spacer-top overview-meta-tags" > <TagsList allowUpdate={false} diff --git a/server/sonar-web/src/main/js/apps/overview/styles.css b/server/sonar-web/src/main/js/apps/overview/styles.css index c79f253c270..4563968dc1a 100644 --- a/server/sonar-web/src/main/js/apps/overview/styles.css +++ b/server/sonar-web/src/main/js/apps/overview/styles.css @@ -26,6 +26,16 @@ transition: transform 0.5s ease, opacity 0.5s ease; } +.overview-sidebar { + margin-top: calc(-1 * var(--pagePadding)); + margin-bottom: calc(-1 * var(--pagePadding)); + margin-left: var(--pagePadding); + padding-left: calc(var(--pagePadding) - 1px); + padding-top: var(--pagePadding); + padding-bottom: var(--pagePadding); + border-left: 1px solid var(--barBorderColor); +} + /* * Title */ @@ -308,15 +318,23 @@ .overview-meta-card { min-width: 200px; - padding-bottom: 20px; box-sizing: border-box; } +.overview-meta-card + .overview-meta-card { + margin-top: calc(2 * var(--gridSize)); + padding-top: calc(2 * var(--gridSize) - 1px); + border-top: 1px solid var(--barBorderColor); +} + .overview-meta-description { + margin-top: calc(-0.5 * var(--gridSize)); line-height: 1.5; + color: var(--secondFontColor); } .overview-meta-header { + margin-bottom: calc(0.5 * var(--gridSize)); color: var(--baseFontColor); } @@ -365,9 +383,7 @@ } .overview-analysis + .overview-analysis { - margin-top: 8px; - padding-top: 8px; - border-top: 1px solid var(--barBorderColor); + margin-top: calc(2 * var(--gridSize)); } .overview-analysis-graph { |