aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilippe Perrin <philippe.perrin@sonarsource.com>2019-10-02 18:39:32 +0200
committerSonarTech <sonartech@sonarsource.com>2019-10-07 20:21:05 +0200
commitdaf5a60dd259039b97fd3598f894169d7ecc74e5 (patch)
tree29cf6b11a2d998f0c4bf4fac6d740407d79de78e
parentdfe6b9e39a63507bba9900f5a68565ca82f3627f (diff)
downloadsonarqube-daf5a60dd259039b97fd3598f894169d7ecc74e5.tar.gz
sonarqube-daf5a60dd259039b97fd3598f894169d7ecc74e5.zip
SONAR-8884 Group languages related settings in a single Languages tab
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx61
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts22
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/CategoryOverrides.ts49
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/Languages.tsx130
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx56
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap86
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap85
-rw-r--r--server/sonar-web/src/main/js/apps/settings/styles.css9
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
14 files changed, 559 insertions, 39 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx
new file mode 100644
index 00000000000..4a4e634c2ee
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx
@@ -0,0 +1,61 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { translate } from 'sonar-ui-common/helpers/l10n';
+import { LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY } from './AdditionalCategoryKeys';
+import Languages from './Languages';
+import NewCodePeriod from './NewCodePeriod';
+
+export interface AdditionalCategory {
+ key: string;
+ name: string;
+ renderComponent: (
+ parentComponent: T.Component | undefined,
+ selectedCategory: string
+ ) => JSX.Element;
+ availableGlobally: boolean;
+ availableForProject: boolean;
+}
+
+export const ADDITIONAL_CATEGORIES: AdditionalCategory[] = [
+ {
+ key: LANGUAGES_CATEGORY,
+ name: translate('property.category.languages'),
+ renderComponent: getLanguagesComponent,
+ availableGlobally: true,
+ availableForProject: true
+ },
+ {
+ key: NEW_CODE_PERIOD_CATEGORY,
+ name: translate('settings.new_code_period.category'),
+ renderComponent: getNewCodePeriodComponent,
+ availableGlobally: true,
+ availableForProject: false
+ }
+];
+
+function getLanguagesComponent(component: any, originalCategory: string) {
+ return <Languages component={component} selectedCategory={originalCategory} />;
+}
+
+function getNewCodePeriodComponent() {
+ return <NewCodePeriod />;
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts
new file mode 100644
index 00000000000..a1ef533670a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts
@@ -0,0 +1,22 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+
+export const LANGUAGES_CATEGORY = 'languages';
+export const NEW_CODE_PERIOD_CATEGORY = 'new_code_period';
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
index 02cedc53b09..29e22177b69 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
@@ -22,27 +22,24 @@ import { sortBy } from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
import { IndexLink } from 'react-router';
-import { translate } from 'sonar-ui-common/helpers/l10n';
import { getSettingsAppAllCategories, Store } from '../../../store/rootReducer';
import { getCategoryName } from '../utils';
+import { ADDITIONAL_CATEGORIES } from './AdditionalCategories';
+import { CATEGORY_OVERRIDES } from './CategoryOverrides';
interface Category {
key: string;
name: string;
}
-interface Props {
+export interface CategoriesListProps {
categories: string[];
component?: T.Component;
defaultCategory: string;
selectedCategory: string;
}
-const FIXED_CATEGORIES = [
- { key: 'new_code_period', name: translate('settings.new_code_period.category') }
-];
-
-export class CategoriesList extends React.PureComponent<Props> {
+export class CategoriesList extends React.PureComponent<CategoriesListProps> {
renderLink(category: Category) {
const { component, defaultCategory, selectedCategory } = this.props;
const pathname = this.props.component ? '/project/settings' : '/settings';
@@ -64,11 +61,18 @@ export class CategoriesList extends React.PureComponent<Props> {
render() {
const categoriesWithName = this.props.categories
+ .filter(key => !CATEGORY_OVERRIDES[key.toLowerCase()])
.map(key => ({
key,
name: getCategoryName(key)
}))
- .concat(!this.props.component ? FIXED_CATEGORIES : []);
+ .concat(
+ this.props.component
+ ? // Project settings
+ ADDITIONAL_CATEGORIES.filter(c => c.availableForProject)
+ : // Global settings
+ ADDITIONAL_CATEGORIES.filter(c => c.availableGlobally)
+ );
const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase());
return (
<ul className="side-tabs-menu">
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
index 3a2d3a956da..0880ce8edff 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { find } from 'lodash';
import * as React from 'react';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
@@ -27,9 +29,10 @@ import { getSettingsAppDefaultCategory, Store } from '../../../store/rootReducer
import '../side-tabs.css';
import { fetchSettings } from '../store/actions';
import '../styles.css';
+import { ADDITIONAL_CATEGORIES } from './AdditionalCategories';
import AllCategoriesList from './AllCategoriesList';
import CategoryDefinitionsList from './CategoryDefinitionsList';
-import NewCodePeriod from './NewCodePeriod';
+import { CATEGORY_OVERRIDES } from './CategoryOverrides';
import PageHeader from './PageHeader';
import WildcardsHelp from './WildcardsHelp';
@@ -79,7 +82,16 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> {
}
const { query } = this.props.location;
- const selectedCategory = query.category || this.props.defaultCategory;
+
+ const originalCategory = (query.category as string) || this.props.defaultCategory;
+ const overriddenCategory = CATEGORY_OVERRIDES[originalCategory.toLowerCase()];
+ const selectedCategory = overriddenCategory || originalCategory;
+ const foundAdditionalCategory = find(ADDITIONAL_CATEGORIES, c => c.key === selectedCategory);
+ const isProjectSettings = this.props.component;
+ const shouldRenderAdditionalCategory =
+ foundAdditionalCategory &&
+ ((isProjectSettings && foundAdditionalCategory.availableForProject) ||
+ (!isProjectSettings && foundAdditionalCategory.availableGlobally));
return (
<div className="page page-limited" id="settings-page">
@@ -97,8 +109,8 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> {
/>
</div>
<div className="side-tabs-main">
- {!this.props.component && selectedCategory === 'new_code_period' ? (
- <NewCodePeriod />
+ {foundAdditionalCategory && shouldRenderAdditionalCategory ? (
+ foundAdditionalCategory.renderComponent(this.props.component, originalCategory)
) : (
<CategoryDefinitionsList
category={selectedCategory}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/CategoryOverrides.ts b/server/sonar-web/src/main/js/apps/settings/components/CategoryOverrides.ts
new file mode 100644
index 00000000000..df960088334
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/CategoryOverrides.ts
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { LANGUAGES_CATEGORY } from './AdditionalCategoryKeys';
+
+export const CATEGORY_OVERRIDES: T.Dict<string> = {
+ abap: LANGUAGES_CATEGORY,
+ apex: LANGUAGES_CATEGORY,
+ 'c / c++ / objective-c': LANGUAGES_CATEGORY,
+ 'c#': LANGUAGES_CATEGORY,
+ cobol: LANGUAGES_CATEGORY,
+ css: LANGUAGES_CATEGORY,
+ flex: LANGUAGES_CATEGORY,
+ go: LANGUAGES_CATEGORY,
+ html: LANGUAGES_CATEGORY,
+ java: LANGUAGES_CATEGORY,
+ javascript: LANGUAGES_CATEGORY,
+ kotlin: LANGUAGES_CATEGORY,
+ php: LANGUAGES_CATEGORY,
+ 'pl/i': LANGUAGES_CATEGORY,
+ 'pl/sql': LANGUAGES_CATEGORY,
+ python: LANGUAGES_CATEGORY,
+ rpg: LANGUAGES_CATEGORY,
+ ruby: LANGUAGES_CATEGORY,
+ scala: LANGUAGES_CATEGORY,
+ swift: LANGUAGES_CATEGORY,
+ 't-sql': LANGUAGES_CATEGORY,
+ typescript: LANGUAGES_CATEGORY,
+ 'vb.net': LANGUAGES_CATEGORY,
+ 'visual basic': LANGUAGES_CATEGORY,
+ xml: LANGUAGES_CATEGORY
+};
diff --git a/server/sonar-web/src/main/js/apps/settings/components/Languages.tsx b/server/sonar-web/src/main/js/apps/settings/components/Languages.tsx
new file mode 100644
index 00000000000..35a88f9280a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/Languages.tsx
@@ -0,0 +1,130 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import * as React from 'react';
+import { connect } from 'react-redux';
+import Select from 'sonar-ui-common/components/controls/Select';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
+import { getSettingsAppAllCategories, Store } from '../../../store/rootReducer';
+import { getCategoryName } from '../utils';
+import { LANGUAGES_CATEGORY } from './AdditionalCategoryKeys';
+import CategoryDefinitionsList from './CategoryDefinitionsList';
+import { CATEGORY_OVERRIDES } from './CategoryOverrides';
+
+export interface LanguagesProps {
+ categories: string[];
+ component?: T.Component;
+ location: Location;
+ selectedCategory: string;
+ router: Router;
+}
+
+interface LanguagesState {
+ availableLanguages: SelectOption[];
+ selectedLanguage: string | undefined;
+}
+
+interface SelectOption {
+ label: string;
+ originalValue: string;
+ value: string;
+}
+
+export class Languages extends React.PureComponent<LanguagesProps, LanguagesState> {
+ constructor(props: LanguagesProps) {
+ super(props);
+
+ this.state = {
+ availableLanguages: [],
+ selectedLanguage: undefined
+ };
+ }
+
+ componentDidMount() {
+ const { selectedCategory, categories } = this.props;
+ const lowerCasedLanguagesCategory = LANGUAGES_CATEGORY.toLowerCase();
+ const lowerCasedSelectedCategory = selectedCategory.toLowerCase();
+
+ const availableLanguages = categories
+ .filter(c => CATEGORY_OVERRIDES[c.toLowerCase()] === lowerCasedLanguagesCategory)
+ .map(c => ({
+ label: getCategoryName(c),
+ value: c.toLowerCase(),
+ originalValue: c
+ }));
+
+ let selectedLanguage = undefined;
+
+ if (
+ lowerCasedSelectedCategory !== lowerCasedLanguagesCategory &&
+ availableLanguages.find(c => c.value === lowerCasedSelectedCategory)
+ ) {
+ selectedLanguage = lowerCasedSelectedCategory;
+ }
+
+ this.setState({
+ availableLanguages,
+ selectedLanguage
+ });
+ }
+
+ handleOnChange = (newOption: SelectOption) => {
+ this.setState({ selectedLanguage: newOption.value });
+
+ const { location, router } = this.props;
+
+ router.push({
+ ...location,
+ query: { ...location.query, category: newOption.originalValue }
+ });
+ };
+
+ render() {
+ const { component } = this.props;
+ const { availableLanguages, selectedLanguage } = this.state;
+
+ return (
+ <>
+ <h2 className="settings-sub-category-name">{translate('property.category.languages')}</h2>
+ <div data-test="language-select">
+ <Select
+ className="input-large"
+ onChange={this.handleOnChange}
+ options={availableLanguages}
+ placeholder={translate('settings.languages.select_a_language_placeholder')}
+ value={selectedLanguage}
+ />
+ </div>
+ {selectedLanguage && (
+ <div className="settings-sub-category">
+ <CategoryDefinitionsList category={selectedLanguage} component={component} />
+ </div>
+ )}
+ </>
+ );
+ }
+}
+
+export default withRouter(
+ connect((state: Store) => ({
+ categories: getSettingsAppAllCategories(state)
+ }))(Languages)
+);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
index 699ee214aab..9bbdb156d93 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
@@ -19,16 +19,46 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { CategoriesList } from '../AllCategoriesList';
+import { mockComponent } from '../../../../helpers/testMocks';
+import { AdditionalCategory } from '../AdditionalCategories';
+import { CategoriesList, CategoriesListProps } from '../AllCategoriesList';
-it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find('li')).toHaveLength(3);
+jest.mock('../AdditionalCategories', () => ({
+ ADDITIONAL_CATEGORIES: [
+ {
+ key: 'CAT_1',
+ name: 'CAT_1_NAME',
+ renderComponent: jest.fn(),
+ availableGlobally: true,
+ availableForProject: true
+ },
+ {
+ key: 'CAT_2',
+ name: 'CAT_2_NAME',
+ renderComponent: jest.fn(),
+ availableGlobally: true,
+ availableForProject: false
+ },
+ {
+ key: 'CAT_3',
+ name: 'CAT_3_NAME',
+ renderComponent: jest.fn(),
+ availableGlobally: false,
+ availableForProject: true
+ }
+ ] as AdditionalCategory[]
+}));
+
+it('should render correctly in global mode', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should render correctly in project mode', () => {
+ expect(shallowRender({ component: mockComponent() })).toMatchSnapshot();
});
-function shallowRender(props: Partial<CategoriesList['props']> = {}) {
- const categories = ['COBOL', 'general'];
+function shallowRender(props?: Partial<CategoriesListProps>) {
+ const categories = ['general'];
return shallow(
<CategoriesList
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx
index 65bbcb4824e..46e41ab1b67 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx
@@ -19,8 +19,9 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { App } from '../AppContainer';
import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
+import { LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY } from '../AdditionalCategoryKeys';
+import { App } from '../AppContainer';
it('should render correctly', () => {
const wrapper = shallowRender();
@@ -29,7 +30,14 @@ it('should render correctly', () => {
it('should render newCodePeriod correctly', () => {
const wrapper = shallowRender({
- location: mockLocation({ query: { category: 'new_code_period' } })
+ location: mockLocation({ query: { category: NEW_CODE_PERIOD_CATEGORY } })
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render languages correctly', () => {
+ const wrapper = shallowRender({
+ location: mockLocation({ query: { category: LANGUAGES_CATEGORY } })
});
expect(wrapper).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx
new file mode 100644
index 00000000000..1ab345b09e9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { mockLocation, mockRouter } from '../../../../helpers/testMocks';
+import { Languages, LanguagesProps } from '../Languages';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render correctly with an unknow language', () => {
+ const wrapper = shallowRender({ selectedCategory: 'unknown' });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should correctly handle a change of the selected language', () => {
+ const push = jest.fn();
+ const router = mockRouter({ push });
+ const wrapper = shallowRender({ router });
+ expect(wrapper.state().selectedLanguage).toBe('java');
+
+ wrapper.instance().handleOnChange({ label: '', originalValue: 'CoBoL', value: 'cobol' });
+ expect(wrapper.state().selectedLanguage).toBe('cobol');
+ expect(push).toHaveBeenCalledWith(expect.objectContaining({ query: { category: 'CoBoL' } }));
+});
+
+function shallowRender(props: Partial<LanguagesProps> = {}) {
+ return shallow<Languages>(
+ <Languages
+ categories={['Java', 'JavaScript', 'COBOL']}
+ location={mockLocation()}
+ router={mockRouter()}
+ selectedCategory="java"
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap
index 88899f05465..d2529468bd1 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap
@@ -1,26 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should render correctly 1`] = `
+exports[`should render correctly in global mode 1`] = `
<ul
className="side-tabs-menu"
>
<li
- key="COBOL"
+ key="CAT_1"
>
<IndexLink
className=""
- title="COBOL"
+ title="CAT_1_NAME"
to={
Object {
"pathname": "/settings",
"query": Object {
- "category": "cobol",
+ "category": "cat_1",
"id": undefined,
},
}
}
>
- COBOL
+ CAT_1_NAME
+ </IndexLink>
+ </li>
+ <li
+ key="CAT_2"
+ >
+ <IndexLink
+ className=""
+ title="CAT_2_NAME"
+ to={
+ Object {
+ "pathname": "/settings",
+ "query": Object {
+ "category": "cat_2",
+ "id": undefined,
+ },
+ }
+ }
+ >
+ CAT_2_NAME
</IndexLink>
</li>
<li
@@ -42,23 +61,68 @@ exports[`should render correctly 1`] = `
general
</IndexLink>
</li>
+</ul>
+`;
+
+exports[`should render correctly in project mode 1`] = `
+<ul
+ className="side-tabs-menu"
+>
<li
- key="new_code_period"
+ key="CAT_1"
>
<IndexLink
className=""
- title="settings.new_code_period.category"
+ title="CAT_1_NAME"
to={
Object {
- "pathname": "/settings",
+ "pathname": "/project/settings",
"query": Object {
- "category": "new_code_period",
- "id": undefined,
+ "category": "cat_1",
+ "id": "my-project",
},
}
}
>
- settings.new_code_period.category
+ CAT_1_NAME
+ </IndexLink>
+ </li>
+ <li
+ key="CAT_3"
+ >
+ <IndexLink
+ className=""
+ title="CAT_3_NAME"
+ to={
+ Object {
+ "pathname": "/project/settings",
+ "query": Object {
+ "category": "cat_3",
+ "id": "my-project",
+ },
+ }
+ }
+ >
+ CAT_3_NAME
+ </IndexLink>
+ </li>
+ <li
+ key="general"
+ >
+ <IndexLink
+ className=""
+ title="general"
+ to={
+ Object {
+ "pathname": "/project/settings",
+ "query": Object {
+ "category": undefined,
+ "id": "my-project",
+ },
+ }
+ }
+ >
+ general
</IndexLink>
</li>
</ul>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap
index 1c4a7ae75b8..2f43729d145 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap
@@ -2,4 +2,6 @@
exports[`should render correctly 1`] = `""`;
+exports[`should render languages correctly 1`] = `""`;
+
exports[`should render newCodePeriod correctly 1`] = `""`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap
new file mode 100644
index 00000000000..b351829d1cc
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap
@@ -0,0 +1,85 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <h2
+ className="settings-sub-category-name"
+ >
+ property.category.languages
+ </h2>
+ <div
+ data-test="language-select"
+ >
+ <Select
+ className="input-large"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "Java",
+ "originalValue": "Java",
+ "value": "java",
+ },
+ Object {
+ "label": "JavaScript",
+ "originalValue": "JavaScript",
+ "value": "javascript",
+ },
+ Object {
+ "label": "COBOL",
+ "originalValue": "COBOL",
+ "value": "cobol",
+ },
+ ]
+ }
+ placeholder="settings.languages.select_a_language_placeholder"
+ value="java"
+ />
+ </div>
+ <div
+ className="settings-sub-category"
+ >
+ <Connect(SubCategoryDefinitionsList)
+ category="java"
+ />
+ </div>
+</Fragment>
+`;
+
+exports[`should render correctly with an unknow language 1`] = `
+<Fragment>
+ <h2
+ className="settings-sub-category-name"
+ >
+ property.category.languages
+ </h2>
+ <div
+ data-test="language-select"
+ >
+ <Select
+ className="input-large"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "Java",
+ "originalValue": "Java",
+ "value": "java",
+ },
+ Object {
+ "label": "JavaScript",
+ "originalValue": "JavaScript",
+ "value": "javascript",
+ },
+ Object {
+ "label": "COBOL",
+ "originalValue": "COBOL",
+ "value": "cobol",
+ },
+ ]
+ }
+ placeholder="settings.languages.select_a_language_placeholder"
+ />
+ </div>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/settings/styles.css b/server/sonar-web/src/main/js/apps/settings/styles.css
index bdbca6a2ad7..37198351c10 100644
--- a/server/sonar-web/src/main/js/apps/settings/styles.css
+++ b/server/sonar-web/src/main/js/apps/settings/styles.css
@@ -82,13 +82,8 @@
border-top: 1px dotted var(--barBorderColor);
}
-.settings-sub-categories-list {
-}
-
-.settings-sub-categories-list > li {
-}
-
-.settings-sub-categories-list > li + li {
+.settings-sub-categories-list > li + li,
+.settings-sub-category {
margin: 30px -20px 0;
padding: 30px 20px;
border-top: 1px solid var(--barBorderColor);
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 707315c440f..105b7be3c1c 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -920,6 +920,7 @@ settings.new_code_period.title=Default New Code Period behavior
settings.new_code_period.description=The New Code Period is the period used to compare measures and track new issues. {link}
settings.new_code_period.description2=This setting is the default for all projects. A specific New Code Period setting can be configured at project level.
+settings.languages.select_a_language_placeholder=Select a language
property.category.general=General
property.category.general.email=Email
@@ -941,6 +942,7 @@ property.category.duplications=Duplications
property.category.localization=Localization
property.category.exclusions=Analysis Scope
property.category.webhooks=Webhooks
+property.category.languages=Languages
property.sonar.inclusions.name=Source File Inclusions
property.sonar.inclusions.description=Patterns used to include some source files and only these ones in analysis.
property.sonar.test.inclusions.name=Test File Inclusions