@@ -17,7 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { getJSON, post, postJSON } from 'sonar-ui-common/helpers/request'; | |||
import { getJSON } from 'sonar-ui-common/helpers/request'; | |||
import throwGlobalError from '../app/utils/throwGlobalError'; | |||
export interface MetricsResponse { | |||
@@ -55,36 +55,3 @@ export function getAllMetrics(data?: { | |||
}); | |||
} | |||
} | |||
export function getMetricDomains(): Promise<string[]> { | |||
return getJSON('/api/metrics/domains').then(r => r.domains, throwGlobalError); | |||
} | |||
export function getMetricTypes(): Promise<string[]> { | |||
return getJSON('/api/metrics/types').then(r => r.types, throwGlobalError); | |||
} | |||
export function createMetric(data: { | |||
description?: string; | |||
domain?: string; | |||
key: string; | |||
name: string; | |||
type: string; | |||
}): Promise<T.Metric> { | |||
return postJSON('/api/metrics/create', data).catch(throwGlobalError); | |||
} | |||
export function updateMetric(data: { | |||
description?: string; | |||
domain?: string; | |||
id: string; | |||
key?: string; | |||
name?: string; | |||
type?: string; | |||
}) { | |||
return post('/api/metrics/update', data).catch(throwGlobalError); | |||
} | |||
export function deleteMetric(data: { keys: string }) { | |||
return post('/api/metrics/delete', data).catch(throwGlobalError); | |||
} |
@@ -103,11 +103,6 @@ export default class SettingsNav extends React.PureComponent<Props> { | |||
{translate('property.category.security.encryption')} | |||
</IndexLink> | |||
</li> | |||
<li> | |||
<IndexLink activeClassName="active" to="/admin/custom_metrics"> | |||
{translate('custom_metrics.page')} | |||
</IndexLink> | |||
</li> | |||
<li> | |||
<IndexLink activeClassName="active" to="/admin/webhooks"> | |||
{translate('webhooks.page')} |
@@ -58,14 +58,6 @@ exports[`should work with extensions 1`] = ` | |||
property.category.security.encryption | |||
</IndexLink> | |||
</li> | |||
<li> | |||
<IndexLink | |||
activeClassName="active" | |||
to="/admin/custom_metrics" | |||
> | |||
custom_metrics.page | |||
</IndexLink> | |||
</li> | |||
<li> | |||
<IndexLink | |||
activeClassName="active" | |||
@@ -203,14 +195,6 @@ Array [ | |||
property.category.security.encryption | |||
</IndexLink> | |||
</li> | |||
<li> | |||
<IndexLink | |||
activeClassName="active" | |||
to="/admin/custom_metrics" | |||
> | |||
custom_metrics.page | |||
</IndexLink> | |||
</li> | |||
<li> | |||
<IndexLink | |||
activeClassName="active" |
@@ -36,7 +36,6 @@ import backgroundTasksRoutes from '../../apps/background-tasks/routes'; | |||
import codeRoutes from '../../apps/code/routes'; | |||
import codingRulesRoutes from '../../apps/coding-rules/routes'; | |||
import componentMeasuresRoutes from '../../apps/component-measures/routes'; | |||
import customMetricsRoutes from '../../apps/custom-metrics/routes'; | |||
import documentationRoutes from '../../apps/documentation/routes'; | |||
import groupsRoutes from '../../apps/groups/routes'; | |||
import Issues from '../../apps/issues/components/AppContainer'; | |||
@@ -231,7 +230,6 @@ function renderAdminRoutes() { | |||
)} | |||
/> | |||
<RouteWithChildRoutes path="background_tasks" childRoutes={backgroundTasksRoutes} /> | |||
<RouteWithChildRoutes path="custom_metrics" childRoutes={customMetricsRoutes} /> | |||
<RouteWithChildRoutes path="groups" childRoutes={groupsRoutes} /> | |||
<RouteWithChildRoutes path="permission_templates" childRoutes={permissionTemplatesRoutes} /> | |||
<RouteWithChildRoutes path="permissions" childRoutes={globalPermissionsRoutes} /> |
@@ -1,174 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { Helmet } from 'react-helmet-async'; | |||
import ListFooter from 'sonar-ui-common/components/controls/ListFooter'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { | |||
createMetric, | |||
deleteMetric, | |||
getMetricDomains, | |||
getMetrics, | |||
getMetricTypes, | |||
MetricsResponse, | |||
updateMetric | |||
} from '../../../api/metrics'; | |||
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | |||
import { MetricProps } from './Form'; | |||
import Header from './Header'; | |||
import List from './List'; | |||
interface Props {} | |||
interface State { | |||
domains?: string[]; | |||
loading: boolean; | |||
metrics?: T.Metric[]; | |||
paging?: T.Paging; | |||
types?: string[]; | |||
} | |||
const PAGE_SIZE = 50; | |||
export default class App extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { loading: true }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
this.fetchData(); | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
fetchData = () => { | |||
Promise.all([ | |||
getMetricDomains(), | |||
getMetricTypes(), | |||
getMetrics({ isCustom: true, ps: PAGE_SIZE }) | |||
]).then(([domains, types, metricsResponse]) => { | |||
if (this.mounted) { | |||
this.setState({ | |||
domains, | |||
loading: false, | |||
metrics: metricsResponse.metrics, | |||
paging: this.getPaging(metricsResponse), | |||
types | |||
}); | |||
} | |||
}, this.stopLoading); | |||
}; | |||
fetchMore = () => { | |||
const { paging } = this.state; | |||
if (paging) { | |||
this.setState({ loading: true }); | |||
getMetrics({ isCustom: true, p: paging.pageIndex + 1, ps: PAGE_SIZE }).then( | |||
metricsResponse => { | |||
if (this.mounted) { | |||
this.setState(({ metrics = [] }: State) => ({ | |||
loading: false, | |||
metrics: [...metrics, ...metricsResponse.metrics], | |||
paging: this.getPaging(metricsResponse) | |||
})); | |||
} | |||
}, | |||
this.stopLoading | |||
); | |||
} | |||
}; | |||
stopLoading = () => { | |||
if (this.mounted) { | |||
this.setState({ loading: false }); | |||
} | |||
}; | |||
getPaging = (response: MetricsResponse): T.Paging => ({ | |||
pageIndex: response.p, | |||
pageSize: response.ps, | |||
total: response.total | |||
}); | |||
handleCreate = (data: MetricProps) => { | |||
return createMetric(data).then(metric => { | |||
if (this.mounted) { | |||
this.setState(({ metrics = [], paging }: State) => ({ | |||
metrics: [...metrics, metric], | |||
paging: paging && { ...paging, total: paging.total + 1 } | |||
})); | |||
} | |||
}); | |||
}; | |||
handleEdit = (data: { id: string } & MetricProps) => { | |||
return updateMetric(data).then(() => { | |||
if (this.mounted) { | |||
this.setState(({ metrics = [] }: State) => ({ | |||
metrics: metrics.map(metric => (metric.id === data.id ? { ...metric, ...data } : metric)) | |||
})); | |||
} | |||
}); | |||
}; | |||
handleDelete = (metricKey: string) => { | |||
return deleteMetric({ keys: metricKey }).then(() => { | |||
if (this.mounted) { | |||
this.setState(({ metrics = [], paging }: State) => ({ | |||
metrics: metrics.filter(metric => metric.key !== metricKey), | |||
paging: paging && { ...paging, total: paging.total - 1 } | |||
})); | |||
} | |||
}); | |||
}; | |||
render() { | |||
const { domains, loading, metrics, paging, types } = this.state; | |||
return ( | |||
<> | |||
<Suggestions suggestions="custom_metrics" /> | |||
<Helmet defer={false} title={translate('custom_metrics.page')} /> | |||
<div className="page page-limited" id="custom-metrics-page"> | |||
<Header domains={domains} loading={loading} onCreate={this.handleCreate} types={types} /> | |||
{metrics && ( | |||
<List | |||
domains={domains} | |||
metrics={metrics} | |||
onDelete={this.handleDelete} | |||
onEdit={this.handleEdit} | |||
types={types} | |||
/> | |||
)} | |||
{metrics && paging && ( | |||
<ListFooter | |||
count={metrics.length} | |||
loadMore={this.fetchMore} | |||
ready={!loading} | |||
total={paging.total} | |||
/> | |||
)} | |||
</div> | |||
</> | |||
); | |||
} | |||
} |
@@ -1,76 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { Button } from 'sonar-ui-common/components/controls/buttons'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import Form, { MetricProps } from './Form'; | |||
interface Props { | |||
domains: string[]; | |||
onCreate: (data: MetricProps) => Promise<void>; | |||
types: string[]; | |||
} | |||
interface State { | |||
modal: boolean; | |||
} | |||
export default class CreateButton extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { modal: false }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
handleClick = () => { | |||
this.setState({ modal: true }); | |||
}; | |||
handleClose = () => { | |||
if (this.mounted) { | |||
this.setState({ modal: false }); | |||
} | |||
}; | |||
render() { | |||
return ( | |||
<> | |||
<Button id="metrics-create" onClick={this.handleClick}> | |||
{translate('custom_metrics.create_metric')} | |||
</Button> | |||
{this.state.modal && ( | |||
<Form | |||
confirmButtonText={translate('create')} | |||
domains={this.props.domains} | |||
header={translate('custom_metrics.create_metric')} | |||
onClose={this.handleClose} | |||
onSubmit={this.props.onCreate} | |||
types={this.props.types} | |||
/> | |||
)} | |||
</> | |||
); | |||
} | |||
} |
@@ -1,60 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons'; | |||
import SimpleModal from 'sonar-ui-common/components/controls/SimpleModal'; | |||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | |||
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; | |||
interface Props { | |||
metric: T.Metric; | |||
onClose: () => void; | |||
onSubmit: () => Promise<void>; | |||
} | |||
export default function DeleteForm({ metric, onClose, onSubmit }: Props) { | |||
const header = translate('custom_metrics.delete_metric'); | |||
return ( | |||
<SimpleModal header={header} onClose={onClose} onSubmit={onSubmit}> | |||
{({ onCloseClick, onFormSubmit, submitting }) => ( | |||
<form onSubmit={onFormSubmit}> | |||
<header className="modal-head"> | |||
<h2>{header}</h2> | |||
</header> | |||
<div className="modal-body"> | |||
{translateWithParameters('custom_metrics.delete_metric.confirmation', metric.name)} | |||
</div> | |||
<footer className="modal-foot"> | |||
<DeferredSpinner className="spacer-right" loading={submitting} /> | |||
<SubmitButton className="button-red" disabled={submitting}> | |||
{translate('delete')} | |||
</SubmitButton> | |||
<ResetButtonLink disabled={submitting} onClick={onCloseClick}> | |||
{translate('cancel')} | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
)} | |||
</SimpleModal> | |||
); | |||
} |
@@ -1,198 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons'; | |||
import Select, { Creatable } from 'sonar-ui-common/components/controls/Select'; | |||
import SimpleModal from 'sonar-ui-common/components/controls/SimpleModal'; | |||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | |||
import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker'; | |||
import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
export interface MetricProps { | |||
description: string; | |||
domain?: string; | |||
key: string; | |||
name: string; | |||
type: string; | |||
} | |||
interface Props { | |||
confirmButtonText: string; | |||
domains: string[]; | |||
metric?: T.Metric; | |||
header: string; | |||
onClose: () => void; | |||
onSubmit: (data: MetricProps) => Promise<void>; | |||
types: string[]; | |||
} | |||
interface State extends MetricProps {} | |||
export default class Form extends React.PureComponent<Props, State> { | |||
constructor(props: Props) { | |||
super(props); | |||
this.state = { | |||
description: (props.metric && props.metric.description) || '', | |||
domain: props.metric && props.metric.domain, | |||
key: (props.metric && props.metric.key) || '', | |||
name: (props.metric && props.metric.name) || '', | |||
type: (props.metric && props.metric.type) || 'INT' | |||
}; | |||
} | |||
handleSubmit = () => { | |||
return this.props | |||
.onSubmit({ | |||
description: this.state.description, | |||
domain: this.state.domain, | |||
key: this.state.key, | |||
name: this.state.name, | |||
type: this.state.type | |||
}) | |||
.then(this.props.onClose); | |||
}; | |||
handleKeyChange = (event: React.ChangeEvent<HTMLInputElement>) => { | |||
this.setState({ key: event.currentTarget.value }); | |||
}; | |||
handleDescriptionChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => { | |||
this.setState({ description: event.currentTarget.value }); | |||
}; | |||
handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => { | |||
this.setState({ name: event.currentTarget.value }); | |||
}; | |||
handleDomainChange = (option: { value: string } | null) => { | |||
this.setState({ domain: option ? option.value : undefined }); | |||
}; | |||
handleTypeChange = ({ value }: { value: string }) => { | |||
this.setState({ type: value }); | |||
}; | |||
render() { | |||
const domains = [...this.props.domains]; | |||
if (this.state.domain) { | |||
domains.push(this.state.domain); | |||
} | |||
return ( | |||
<SimpleModal | |||
header={this.props.header} | |||
onClose={this.props.onClose} | |||
onSubmit={this.handleSubmit} | |||
size="small"> | |||
{({ onCloseClick, onFormSubmit, submitting }) => ( | |||
<form onSubmit={onFormSubmit}> | |||
<header className="modal-head"> | |||
<h2>{this.props.header}</h2> | |||
</header> | |||
<div className="modal-body modal-container"> | |||
<MandatoryFieldsExplanation className="modal-field" /> | |||
<div className="modal-field"> | |||
<label htmlFor="create-metric-key"> | |||
{translate('key')} | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<input | |||
autoFocus={true} | |||
id="create-metric-key" | |||
maxLength={64} | |||
name="key" | |||
onChange={this.handleKeyChange} | |||
required={true} | |||
type="text" | |||
value={this.state.key} | |||
/> | |||
</div> | |||
<div className="modal-field"> | |||
<label htmlFor="create-metric-name"> | |||
{translate('name')} | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<input | |||
id="create-metric-name" | |||
maxLength={64} | |||
name="name" | |||
onChange={this.handleNameChange} | |||
required={true} | |||
type="text" | |||
value={this.state.name} | |||
/> | |||
</div> | |||
<div className="modal-field"> | |||
<label htmlFor="create-metric-description">{translate('description')}</label> | |||
<textarea | |||
id="create-metric-description" | |||
name="description" | |||
onChange={this.handleDescriptionChange} | |||
value={this.state.description} | |||
/> | |||
</div> | |||
<div className="modal-field"> | |||
<label htmlFor="create-metric-domain">{translate('custom_metrics.domain')}</label> | |||
<Creatable | |||
id="create-metric-domain" | |||
onChange={this.handleDomainChange} | |||
options={domains.map(domain => ({ label: domain, value: domain }))} | |||
value={this.state.domain} | |||
/> | |||
</div> | |||
<div className="modal-field"> | |||
<label htmlFor="create-metric-type"> | |||
{translate('type')} | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<Select | |||
clearable={false} | |||
id="create-metric-type" | |||
onChange={this.handleTypeChange} | |||
options={this.props.types.map(type => ({ | |||
label: translate('metric.type', type), | |||
value: type | |||
}))} | |||
value={this.state.type} | |||
/> | |||
</div> | |||
</div> | |||
<footer className="modal-foot"> | |||
<DeferredSpinner className="spacer-right" loading={submitting} /> | |||
<SubmitButton disabled={submitting} id="create-metric-submit"> | |||
{this.props.confirmButtonText} | |||
</SubmitButton> | |||
<ResetButtonLink | |||
disabled={submitting} | |||
id="create-metric-cancel" | |||
onClick={onCloseClick}> | |||
{translate('cancel')} | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
)} | |||
</SimpleModal> | |||
); | |||
} | |||
} |
@@ -1,50 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { Alert } from 'sonar-ui-common/components/ui/Alert'; | |||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import CreateButton from './CreateButton'; | |||
import { MetricProps } from './Form'; | |||
interface Props { | |||
domains: string[] | undefined; | |||
loading: boolean; | |||
onCreate: (data: MetricProps) => Promise<void>; | |||
types: string[] | undefined; | |||
} | |||
export default function Header({ domains, loading, onCreate, types }: Props) { | |||
return ( | |||
<header className="page-header" id="custom-metrics-header"> | |||
<h1 className="page-title">{translate('custom_metrics.page')}</h1> | |||
<DeferredSpinner loading={loading} /> | |||
<div className="page-actions"> | |||
{domains && types && <CreateButton domains={domains} onCreate={onCreate} types={types} />} | |||
</div> | |||
<div className="page-description"> | |||
<Alert display="inline" variant="error"> | |||
{translate('custom_metrics.deprecated')} | |||
</Alert> | |||
<p>{translate('custom_metrics.page.description')}</p> | |||
</div> | |||
</header> | |||
); | |||
} |
@@ -1,146 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import ActionsDropdown, { | |||
ActionsDropdownDivider, | |||
ActionsDropdownItem | |||
} from 'sonar-ui-common/components/controls/ActionsDropdown'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import DeleteForm from './DeleteForm'; | |||
import Form, { MetricProps } from './Form'; | |||
interface Props { | |||
domains?: string[]; | |||
metric: T.Metric; | |||
onDelete: (metricKey: string) => Promise<void>; | |||
onEdit: (data: { id: string } & MetricProps) => Promise<void>; | |||
types?: string[]; | |||
} | |||
interface State { | |||
deleteForm: boolean; | |||
editForm: boolean; | |||
} | |||
export default class Item extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { deleteForm: false, editForm: false }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
handleEditClick = () => { | |||
this.setState({ editForm: true }); | |||
}; | |||
handleDeleteClick = () => { | |||
this.setState({ deleteForm: true }); | |||
}; | |||
closeEditForm = () => { | |||
if (this.mounted) { | |||
this.setState({ editForm: false }); | |||
} | |||
}; | |||
closeDeleteForm = () => { | |||
if (this.mounted) { | |||
this.setState({ deleteForm: false }); | |||
} | |||
}; | |||
handleEditFormSubmit = (data: MetricProps) => { | |||
return this.props.onEdit({ id: this.props.metric.id, ...data }); | |||
}; | |||
handleDeleteFormSubmit = () => { | |||
return this.props.onDelete(this.props.metric.key); | |||
}; | |||
render() { | |||
const { domains, metric, types } = this.props; | |||
return ( | |||
<tr data-metric={metric.key}> | |||
<td className="width-30"> | |||
<div> | |||
<strong className="js-metric-name">{metric.name}</strong> | |||
<span className="js-metric-key note little-spacer-left">{metric.key}</span> | |||
</div> | |||
</td> | |||
<td className="width-20"> | |||
<span className="js-metric-domain">{metric.domain}</span> | |||
</td> | |||
<td className="width-20"> | |||
<span className="js-metric-type">{translate('metric.type', metric.type)}</span> | |||
</td> | |||
<td className="width-20" title={metric.description}> | |||
<span className="js-metric-description">{metric.description}</span> | |||
</td> | |||
<td className="thin nowrap"> | |||
<ActionsDropdown> | |||
{domains && types && ( | |||
<ActionsDropdownItem className="js-metric-update" onClick={this.handleEditClick}> | |||
{translate('update_details')} | |||
</ActionsDropdownItem> | |||
)} | |||
<ActionsDropdownDivider /> | |||
<ActionsDropdownItem | |||
className="js-metric-delete" | |||
destructive={true} | |||
onClick={this.handleDeleteClick}> | |||
{translate('delete')} | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</td> | |||
{this.state.editForm && domains && types && ( | |||
<Form | |||
confirmButtonText={translate('update_verb')} | |||
domains={domains} | |||
header={translate('custom_metrics.update_metric')} | |||
metric={metric} | |||
onClose={this.closeEditForm} | |||
onSubmit={this.handleEditFormSubmit} | |||
types={types} | |||
/> | |||
)} | |||
{this.state.deleteForm && ( | |||
<DeleteForm | |||
metric={metric} | |||
onClose={this.closeDeleteForm} | |||
onSubmit={this.handleDeleteFormSubmit} | |||
/> | |||
)} | |||
</tr> | |||
); | |||
} | |||
} |
@@ -1,57 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { sortBy } from 'lodash'; | |||
import * as React from 'react'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { MetricProps } from './Form'; | |||
import Item from './Item'; | |||
interface Props { | |||
domains?: string[]; | |||
metrics: T.Metric[]; | |||
onDelete: (metricKey: string) => Promise<void>; | |||
onEdit: (data: { id: string } & MetricProps) => Promise<void>; | |||
types?: string[]; | |||
} | |||
export default function List({ domains, metrics, onDelete, onEdit, types }: Props) { | |||
return ( | |||
<div className="boxed-group boxed-group-inner" id="custom-metrics-list"> | |||
{metrics.length > 0 ? ( | |||
<table className="data zebra zebra-hover"> | |||
<tbody> | |||
{sortBy(metrics, metric => metric.name.toLowerCase()).map(metric => ( | |||
<Item | |||
domains={domains} | |||
key={metric.key} | |||
metric={metric} | |||
onDelete={onDelete} | |||
onEdit={onEdit} | |||
types={types} | |||
/> | |||
))} | |||
</tbody> | |||
</table> | |||
) : ( | |||
<p>{translate('no_results')}</p> | |||
)} | |||
</div> | |||
); | |||
} |
@@ -1,77 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; | |||
import App from '../App'; | |||
jest.mock('../../../../api/metrics', () => ({ | |||
getMetricDomains: () => Promise.resolve(['Coverage', 'Issues']), | |||
getMetricTypes: () => Promise.resolve(['INT', 'STRING']), | |||
getMetrics: () => | |||
Promise.resolve({ | |||
metrics: [{ id: '3', key: 'foo', name: 'Foo', type: 'INT' }], | |||
p: 1, | |||
ps: 1, | |||
total: 1 | |||
}), | |||
deleteMetric: () => Promise.resolve(), | |||
updateMetric: () => Promise.resolve(), | |||
createMetric: () => | |||
Promise.resolve({ id: '4', domain: 'Coverage', key: 'bar', name: 'Bar', type: 'INT' }) | |||
})); | |||
it('should work', async () => { | |||
const wrapper = shallow<App>(<App />); | |||
wrapper.instance().mounted = true; | |||
expect(wrapper).toMatchSnapshot(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot(); | |||
// create | |||
wrapper.find('Header').prop<Function>('onCreate')({ | |||
domain: 'Coverage', | |||
key: 'bar', | |||
name: 'Bar', | |||
type: 'INT' | |||
}); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().metrics).toMatchSnapshot(); | |||
expect(wrapper.state().paging!.total).toBe(2); | |||
// edit | |||
wrapper.find('List').prop<Function>('onEdit')({ | |||
domain: undefined, | |||
id: '4', | |||
key: 'bar', | |||
name: 'Bar', | |||
type: 'STRING' | |||
}); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().metrics).toMatchSnapshot(); | |||
expect(wrapper.state().paging!.total).toBe(2); | |||
// delete | |||
wrapper.find('List').prop<Function>('onDelete')('bar'); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().metrics).toMatchSnapshot(); | |||
expect(wrapper.state().paging!.total).toBe(1); | |||
}); |
@@ -1,37 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { click } from 'sonar-ui-common/helpers/testUtils'; | |||
import CreateButton from '../CreateButton'; | |||
it('should create new group', () => { | |||
const onCreate = jest.fn(() => Promise.resolve()); | |||
const wrapper = shallow( | |||
<CreateButton domains={['Coverage', 'Issues']} onCreate={onCreate} types={['INT', 'STRING']} /> | |||
); | |||
expect(wrapper).toMatchSnapshot(); | |||
click(wrapper.find('#metrics-create')); | |||
expect(wrapper).toMatchSnapshot(); | |||
wrapper.find('Form').prop<Function>('onSubmit')({ key: 'foo', name: 'foo', type: 'INT' }); | |||
expect(onCreate).toBeCalledWith({ key: 'foo', name: 'foo', type: 'INT' }); | |||
}); |
@@ -1,29 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import DeleteForm from '../DeleteForm'; | |||
it('should render', () => { | |||
const metric = { id: '3', key: 'foo', name: 'Foo', type: 'INT' }; | |||
expect( | |||
shallow(<DeleteForm metric={metric} onClose={jest.fn()} onSubmit={jest.fn()} />).dive() | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,90 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { change, click, submit } from 'sonar-ui-common/helpers/testUtils'; | |||
import Form from '../Form'; | |||
it('should render form', async () => { | |||
const onClose = jest.fn(); | |||
const onSubmit = jest.fn(() => Promise.resolve()); | |||
const wrapper = shallow( | |||
<Form | |||
confirmButtonText="confirmButtonText" | |||
domains={['Coverage', 'Issues']} | |||
header="header" | |||
onClose={onClose} | |||
onSubmit={onSubmit} | |||
types={['INT', 'STRING']} | |||
/> | |||
).dive(); | |||
expect(wrapper).toMatchSnapshot(); | |||
change(wrapper.find('[name="key"]'), 'foo'); | |||
change(wrapper.find('[name="name"]'), 'Foo'); | |||
change(wrapper.find('[name="description"]'), 'bar'); | |||
wrapper.find('Creatable').prop<Function>('onChange')({ value: 'Coverage' }); | |||
submit(wrapper.find('form')); | |||
expect(onSubmit).toBeCalledWith({ | |||
description: 'bar', | |||
domain: 'Coverage', | |||
key: 'foo', | |||
name: 'Foo', | |||
type: 'INT' | |||
}); | |||
await new Promise(setImmediate); | |||
expect(onClose).toBeCalled(); | |||
onClose.mockClear(); | |||
click(wrapper.find('ResetButtonLink')); | |||
expect(onClose).toBeCalled(); | |||
}); | |||
it('should create new domain', () => { | |||
const wrapper = shallow( | |||
<Form | |||
confirmButtonText="confirmButtonText" | |||
domains={['Coverage', 'Issues']} | |||
header="header" | |||
onClose={jest.fn()} | |||
onSubmit={jest.fn()} | |||
types={['INT', 'STRING']} | |||
/> | |||
); | |||
const optionsBefore = [ | |||
{ label: 'Coverage', value: 'Coverage' }, | |||
{ label: 'Issues', value: 'Issues' } | |||
]; | |||
expect(getSelect().prop('options')).toEqual(optionsBefore); | |||
getSelect().prop<Function>('onChange')({ value: 'Another' }); | |||
wrapper.update(); | |||
expect(getSelect().prop('options')).toEqual([ | |||
...optionsBefore, | |||
{ label: 'Another', value: 'Another' } | |||
]); | |||
function getSelect() { | |||
return wrapper.dive().find('Creatable'); | |||
} | |||
}); |
@@ -1,38 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import Header from '../Header'; | |||
it('should create new metric', () => { | |||
const onCreate = jest.fn(() => Promise.resolve()); | |||
const wrapper = shallow( | |||
<Header | |||
domains={['Coverage', 'Issues']} | |||
loading={false} | |||
onCreate={onCreate} | |||
types={['INT', 'STRING']} | |||
/> | |||
); | |||
expect(wrapper).toMatchSnapshot(); | |||
wrapper.find('CreateButton').prop<Function>('onCreate')({ key: 'foo', name: 'Foo', type: 'INT' }); | |||
expect(onCreate).toBeCalledWith({ key: 'foo', name: 'Foo', type: 'INT' }); | |||
}); |
@@ -1,66 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { click } from 'sonar-ui-common/helpers/testUtils'; | |||
import Item from '../Item'; | |||
const metric = { id: '3', key: 'foo', name: 'Foo', type: 'INT' }; | |||
it('should render', () => { | |||
expect( | |||
shallow(<Item metric={metric} onDelete={jest.fn()} onEdit={jest.fn()} />) | |||
).toMatchSnapshot(); | |||
}); | |||
it('should edit metric', () => { | |||
const onEdit = jest.fn(); | |||
const wrapper = shallow( | |||
<Item | |||
domains={['Coverage', 'Issues']} | |||
metric={metric} | |||
onDelete={jest.fn()} | |||
onEdit={onEdit} | |||
types={['INT', 'STRING']} | |||
/> | |||
); | |||
click(wrapper.find('.js-metric-update')); | |||
wrapper.update(); | |||
wrapper.find('Form').prop<Function>('onSubmit')({ | |||
...metric, | |||
description: 'bla bla', | |||
domain: 'Coverage' | |||
}); | |||
expect(onEdit).toBeCalledWith({ ...metric, description: 'bla bla', domain: 'Coverage' }); | |||
}); | |||
it('should delete metric', () => { | |||
const onDelete = jest.fn(); | |||
const wrapper = shallow(<Item metric={metric} onDelete={onDelete} onEdit={jest.fn()} />); | |||
click(wrapper.find('.js-metric-delete')); | |||
wrapper.update(); | |||
wrapper.find('DeleteForm').prop<Function>('onSubmit')(); | |||
expect(onDelete).toBeCalledWith('foo'); | |||
}); |
@@ -1,36 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import List from '../List'; | |||
it('should render', () => { | |||
const metrics = [ | |||
{ id: '3', key: 'foo', name: 'Foo', type: 'INT' }, | |||
{ id: '4', domain: 'Coverage', key: 'bar', name: 'Bar', type: 'INT' } | |||
]; | |||
expect( | |||
shallow(<List metrics={metrics} onDelete={jest.fn()} onEdit={jest.fn()} />) | |||
).toMatchSnapshot(); | |||
}); | |||
it('should render no results', () => { | |||
expect(shallow(<List metrics={[]} onDelete={jest.fn()} onEdit={jest.fn()} />)).toMatchSnapshot(); | |||
}); |
@@ -1,136 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should work 1`] = ` | |||
<Fragment> | |||
<Suggestions | |||
suggestions="custom_metrics" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
title="custom_metrics.page" | |||
/> | |||
<div | |||
className="page page-limited" | |||
id="custom-metrics-page" | |||
> | |||
<Header | |||
loading={true} | |||
onCreate={[Function]} | |||
/> | |||
</div> | |||
</Fragment> | |||
`; | |||
exports[`should work 2`] = ` | |||
<Fragment> | |||
<Suggestions | |||
suggestions="custom_metrics" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
title="custom_metrics.page" | |||
/> | |||
<div | |||
className="page page-limited" | |||
id="custom-metrics-page" | |||
> | |||
<Header | |||
domains={ | |||
Array [ | |||
"Coverage", | |||
"Issues", | |||
] | |||
} | |||
loading={false} | |||
onCreate={[Function]} | |||
types={ | |||
Array [ | |||
"INT", | |||
"STRING", | |||
] | |||
} | |||
/> | |||
<List | |||
domains={ | |||
Array [ | |||
"Coverage", | |||
"Issues", | |||
] | |||
} | |||
metrics={ | |||
Array [ | |||
Object { | |||
"id": "3", | |||
"key": "foo", | |||
"name": "Foo", | |||
"type": "INT", | |||
}, | |||
] | |||
} | |||
onDelete={[Function]} | |||
onEdit={[Function]} | |||
types={ | |||
Array [ | |||
"INT", | |||
"STRING", | |||
] | |||
} | |||
/> | |||
<ListFooter | |||
count={1} | |||
loadMore={[Function]} | |||
ready={true} | |||
total={1} | |||
/> | |||
</div> | |||
</Fragment> | |||
`; | |||
exports[`should work 3`] = ` | |||
Array [ | |||
Object { | |||
"id": "3", | |||
"key": "foo", | |||
"name": "Foo", | |||
"type": "INT", | |||
}, | |||
Object { | |||
"domain": "Coverage", | |||
"id": "4", | |||
"key": "bar", | |||
"name": "Bar", | |||
"type": "INT", | |||
}, | |||
] | |||
`; | |||
exports[`should work 4`] = ` | |||
Array [ | |||
Object { | |||
"id": "3", | |||
"key": "foo", | |||
"name": "Foo", | |||
"type": "INT", | |||
}, | |||
Object { | |||
"domain": undefined, | |||
"id": "4", | |||
"key": "bar", | |||
"name": "Bar", | |||
"type": "STRING", | |||
}, | |||
] | |||
`; | |||
exports[`should work 5`] = ` | |||
Array [ | |||
Object { | |||
"id": "3", | |||
"key": "foo", | |||
"name": "Foo", | |||
"type": "INT", | |||
}, | |||
] | |||
`; |
@@ -1,41 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should create new group 1`] = ` | |||
<Fragment> | |||
<Button | |||
id="metrics-create" | |||
onClick={[Function]} | |||
> | |||
custom_metrics.create_metric | |||
</Button> | |||
</Fragment> | |||
`; | |||
exports[`should create new group 2`] = ` | |||
<Fragment> | |||
<Button | |||
id="metrics-create" | |||
onClick={[Function]} | |||
> | |||
custom_metrics.create_metric | |||
</Button> | |||
<Form | |||
confirmButtonText="create" | |||
domains={ | |||
Array [ | |||
"Coverage", | |||
"Issues", | |||
] | |||
} | |||
header="custom_metrics.create_metric" | |||
onClose={[Function]} | |||
onSubmit={[MockFunction]} | |||
types={ | |||
Array [ | |||
"INT", | |||
"STRING", | |||
] | |||
} | |||
/> | |||
</Fragment> | |||
`; |
@@ -1,45 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render 1`] = ` | |||
<Modal | |||
contentLabel="custom_metrics.delete_metric" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
custom_metrics.delete_metric | |||
</h2> | |||
</header> | |||
<div | |||
className="modal-body" | |||
> | |||
custom_metrics.delete_metric.confirmation.Foo | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<DeferredSpinner | |||
className="spacer-right" | |||
loading={false} | |||
/> | |||
<SubmitButton | |||
className="button-red" | |||
disabled={false} | |||
> | |||
delete | |||
</SubmitButton> | |||
<ResetButtonLink | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
</Modal> | |||
`; |
@@ -1,156 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render form 1`] = ` | |||
<Modal | |||
contentLabel="header" | |||
onRequestClose={[MockFunction]} | |||
size="small" | |||
> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
header | |||
</h2> | |||
</header> | |||
<div | |||
className="modal-body modal-container" | |||
> | |||
<MandatoryFieldsExplanation | |||
className="modal-field" | |||
/> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="create-metric-key" | |||
> | |||
key | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<input | |||
autoFocus={true} | |||
id="create-metric-key" | |||
maxLength={64} | |||
name="key" | |||
onChange={[Function]} | |||
required={true} | |||
type="text" | |||
value="" | |||
/> | |||
</div> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="create-metric-name" | |||
> | |||
name | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<input | |||
id="create-metric-name" | |||
maxLength={64} | |||
name="name" | |||
onChange={[Function]} | |||
required={true} | |||
type="text" | |||
value="" | |||
/> | |||
</div> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="create-metric-description" | |||
> | |||
description | |||
</label> | |||
<textarea | |||
id="create-metric-description" | |||
name="description" | |||
onChange={[Function]} | |||
value="" | |||
/> | |||
</div> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="create-metric-domain" | |||
> | |||
custom_metrics.domain | |||
</label> | |||
<Creatable | |||
id="create-metric-domain" | |||
onChange={[Function]} | |||
options={ | |||
Array [ | |||
Object { | |||
"label": "Coverage", | |||
"value": "Coverage", | |||
}, | |||
Object { | |||
"label": "Issues", | |||
"value": "Issues", | |||
}, | |||
] | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="create-metric-type" | |||
> | |||
type | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<Select | |||
clearable={false} | |||
id="create-metric-type" | |||
onChange={[Function]} | |||
options={ | |||
Array [ | |||
Object { | |||
"label": "metric.type.INT", | |||
"value": "INT", | |||
}, | |||
Object { | |||
"label": "metric.type.STRING", | |||
"value": "STRING", | |||
}, | |||
] | |||
} | |||
value="INT" | |||
/> | |||
</div> | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<DeferredSpinner | |||
className="spacer-right" | |||
loading={false} | |||
/> | |||
<SubmitButton | |||
disabled={false} | |||
id="create-metric-submit" | |||
> | |||
confirmButtonText | |||
</SubmitButton> | |||
<ResetButtonLink | |||
disabled={false} | |||
id="create-metric-cancel" | |||
onClick={[Function]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
</Modal> | |||
`; |
@@ -1,49 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should create new metric 1`] = ` | |||
<header | |||
className="page-header" | |||
id="custom-metrics-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
custom_metrics.page | |||
</h1> | |||
<DeferredSpinner | |||
loading={false} | |||
/> | |||
<div | |||
className="page-actions" | |||
> | |||
<CreateButton | |||
domains={ | |||
Array [ | |||
"Coverage", | |||
"Issues", | |||
] | |||
} | |||
onCreate={[MockFunction]} | |||
types={ | |||
Array [ | |||
"INT", | |||
"STRING", | |||
] | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="page-description" | |||
> | |||
<Alert | |||
display="inline" | |||
variant="error" | |||
> | |||
custom_metrics.deprecated | |||
</Alert> | |||
<p> | |||
custom_metrics.page.description | |||
</p> | |||
</div> | |||
</header> | |||
`; |
@@ -1,61 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render 1`] = ` | |||
<tr | |||
data-metric="foo" | |||
> | |||
<td | |||
className="width-30" | |||
> | |||
<div> | |||
<strong | |||
className="js-metric-name" | |||
> | |||
Foo | |||
</strong> | |||
<span | |||
className="js-metric-key note little-spacer-left" | |||
> | |||
foo | |||
</span> | |||
</div> | |||
</td> | |||
<td | |||
className="width-20" | |||
> | |||
<span | |||
className="js-metric-domain" | |||
/> | |||
</td> | |||
<td | |||
className="width-20" | |||
> | |||
<span | |||
className="js-metric-type" | |||
> | |||
metric.type.INT | |||
</span> | |||
</td> | |||
<td | |||
className="width-20" | |||
> | |||
<span | |||
className="js-metric-description" | |||
/> | |||
</td> | |||
<td | |||
className="thin nowrap" | |||
> | |||
<ActionsDropdown> | |||
<ActionsDropdownDivider /> | |||
<ActionsDropdownItem | |||
className="js-metric-delete" | |||
destructive={true} | |||
onClick={[Function]} | |||
> | |||
delete | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</td> | |||
</tr> | |||
`; |
@@ -1,53 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render 1`] = ` | |||
<div | |||
className="boxed-group boxed-group-inner" | |||
id="custom-metrics-list" | |||
> | |||
<table | |||
className="data zebra zebra-hover" | |||
> | |||
<tbody> | |||
<Item | |||
key="bar" | |||
metric={ | |||
Object { | |||
"domain": "Coverage", | |||
"id": "4", | |||
"key": "bar", | |||
"name": "Bar", | |||
"type": "INT", | |||
} | |||
} | |||
onDelete={[MockFunction]} | |||
onEdit={[MockFunction]} | |||
/> | |||
<Item | |||
key="foo" | |||
metric={ | |||
Object { | |||
"id": "3", | |||
"key": "foo", | |||
"name": "Foo", | |||
"type": "INT", | |||
} | |||
} | |||
onDelete={[MockFunction]} | |||
onEdit={[MockFunction]} | |||
/> | |||
</tbody> | |||
</table> | |||
</div> | |||
`; | |||
exports[`should render no results 1`] = ` | |||
<div | |||
className="boxed-group boxed-group-inner" | |||
id="custom-metrics-list" | |||
> | |||
<p> | |||
no_results | |||
</p> | |||
</div> | |||
`; |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent'; | |||
const routes = [ | |||
{ | |||
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) } | |||
} | |||
]; | |||
export default routes; |
@@ -2547,105 +2547,8 @@ | |||
{ | |||
"path": "api/metrics", | |||
"since": "2.6", | |||
"description": "Get information on automatic metrics, and manage custom metrics. See also api/custom_measures.", | |||
"description": "Get information on automatic metrics, and manage metrics.", | |||
"actions": [ | |||
{ | |||
"key": "create", | |||
"description": "Create custom metric.<br /> Requires 'Administer System' permission.", | |||
"since": "5.2", | |||
"internal": false, | |||
"post": true, | |||
"hasResponseExample": false, | |||
"changelog": [], | |||
"params": [ | |||
{ | |||
"key": "description", | |||
"description": "Description", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "Size of the team", | |||
"maximumLength": 255 | |||
}, | |||
{ | |||
"key": "domain", | |||
"description": "Domain", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "Tests", | |||
"maximumLength": 64 | |||
}, | |||
{ | |||
"key": "key", | |||
"description": "Key", | |||
"required": true, | |||
"internal": false, | |||
"exampleValue": "team_size", | |||
"maximumLength": 64 | |||
}, | |||
{ | |||
"key": "name", | |||
"description": "Name", | |||
"required": true, | |||
"internal": false, | |||
"exampleValue": "Team Size", | |||
"maximumLength": 64 | |||
}, | |||
{ | |||
"key": "type", | |||
"description": "Metric type key", | |||
"required": true, | |||
"internal": false, | |||
"exampleValue": "INT", | |||
"possibleValues": [ | |||
"INT", | |||
"FLOAT", | |||
"PERCENT", | |||
"BOOL", | |||
"STRING", | |||
"MILLISEC", | |||
"DATA", | |||
"LEVEL", | |||
"DISTRIB", | |||
"RATING", | |||
"WORK_DUR" | |||
] | |||
} | |||
] | |||
}, | |||
{ | |||
"key": "delete", | |||
"description": "Delete metrics and associated measures. Delete only custom metrics.<br />Ids or keys must be provided. <br />Requires 'Administer System' permission.", | |||
"since": "5.2", | |||
"internal": false, | |||
"post": true, | |||
"hasResponseExample": false, | |||
"changelog": [], | |||
"params": [ | |||
{ | |||
"key": "ids", | |||
"description": "Metrics ids to delete.", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "5, 23, 42" | |||
}, | |||
{ | |||
"key": "keys", | |||
"description": "Metrics keys to delete", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "team_size, business_value" | |||
} | |||
] | |||
}, | |||
{ | |||
"key": "domains", | |||
"description": "List all custom metric domains.", | |||
"since": "5.2", | |||
"internal": false, | |||
"post": false, | |||
"hasResponseExample": true, | |||
"changelog": [] | |||
}, | |||
{ | |||
"key": "search", | |||
"description": "Search for metrics", | |||
@@ -2709,75 +2612,6 @@ | |||
"post": false, | |||
"hasResponseExample": true, | |||
"changelog": [] | |||
}, | |||
{ | |||
"key": "update", | |||
"description": "Update a custom metric.<br /> Requires 'Administer System' permission.", | |||
"since": "5.2", | |||
"internal": false, | |||
"post": true, | |||
"hasResponseExample": false, | |||
"changelog": [], | |||
"params": [ | |||
{ | |||
"key": "description", | |||
"description": "Description", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "Size of the team", | |||
"maximumLength": 255 | |||
}, | |||
{ | |||
"key": "domain", | |||
"description": "Domain", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "Tests", | |||
"maximumLength": 64 | |||
}, | |||
{ | |||
"key": "id", | |||
"description": "Id of the custom metric to update", | |||
"required": true, | |||
"internal": false, | |||
"exampleValue": "42" | |||
}, | |||
{ | |||
"key": "key", | |||
"description": "Key", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "team_size", | |||
"maximumLength": 64 | |||
}, | |||
{ | |||
"key": "name", | |||
"description": "Name", | |||
"required": false, | |||
"internal": false, | |||
"maximumLength": 64 | |||
}, | |||
{ | |||
"key": "type", | |||
"description": "Metric type key", | |||
"required": false, | |||
"internal": false, | |||
"exampleValue": "INT", | |||
"possibleValues": [ | |||
"INT", | |||
"FLOAT", | |||
"PERCENT", | |||
"BOOL", | |||
"STRING", | |||
"MILLISEC", | |||
"DATA", | |||
"LEVEL", | |||
"DISTRIB", | |||
"RATING", | |||
"WORK_DUR" | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, |