]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14498 Better JSON field
authorJeremy Davis <jeremy.davis@sonarsource.com>
Fri, 26 Feb 2021 09:27:33 +0000 (10:27 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 26 Feb 2021 20:07:41 +0000 (20:07 +0000)
server/sonar-web/src/main/js/apps/settings/components/inputs/InputForJSON.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/inputs/PrimitiveInput.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForJSON-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/__snapshots__/InputForJSON-test.tsx.snap [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForJSON.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForJSON.tsx
new file mode 100644 (file)
index 0000000..701734e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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 { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { DefaultSpecializedInputProps } from '../../utils';
+
+const JSON_SPACE_SIZE = 4;
+
+interface State {
+  formatError: boolean;
+}
+
+export default class InputForJSON extends React.PureComponent<DefaultSpecializedInputProps, State> {
+  state: State = { formatError: false };
+
+  handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
+    this.setState({ formatError: false });
+    this.props.onChange(event.target.value);
+  };
+
+  format = () => {
+    const { value } = this.props;
+    try {
+      if (value) {
+        this.props.onChange(JSON.stringify(JSON.parse(value), undefined, JSON_SPACE_SIZE));
+      }
+    } catch (e) {
+      this.setState({ formatError: true });
+    }
+  };
+
+  render() {
+    const { formatError } = this.state;
+    return (
+      <div className="display-flex-end">
+        <textarea
+          className="settings-large-input text-top monospaced spacer-right"
+          name={this.props.name}
+          onChange={this.handleInputChange}
+          rows={5}
+          value={this.props.value || ''}
+        />
+        <div>
+          {formatError && <Alert variant="info">{translate('settings.json.format_error')} </Alert>}
+          <Button className="spacer-top" onClick={this.format}>
+            {translate('settings.json.format')}
+          </Button>
+        </div>
+      </div>
+    );
+  }
+}
index ce91fc76318f17fbe7bfc303a6b573c3774bdba9..59a221e3bc7f1bdeb3ff82f370307cd348a0b48b 100644 (file)
@@ -26,6 +26,7 @@ import {
   isDefaultOrInherited
 } from '../../utils';
 import InputForBoolean from './InputForBoolean';
+import InputForJSON from './InputForJSON';
 import InputForNumber from './InputForNumber';
 import InputForPassword from './InputForPassword';
 import InputForSingleSelectList from './InputForSingleSelectList';
@@ -37,7 +38,7 @@ const typeMapping: {
 } = {
   STRING: InputForString,
   TEXT: InputForText,
-  JSON: InputForText,
+  JSON: InputForJSON,
   PASSWORD: InputForPassword,
   BOOLEAN: InputForBoolean,
   INTEGER: InputForNumber,
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForJSON-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForJSON-test.tsx
new file mode 100644 (file)
index 0000000..1eee226
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 } from 'sonar-ui-common/helpers/testUtils';
+import { DefaultSpecializedInputProps } from '../../../utils';
+import InputForJSON from '../InputForJSON';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should call onChange', () => {
+  const onChange = jest.fn();
+  const wrapper = shallowRender({ onChange });
+
+  change(wrapper.find('textarea'), '{"a": 1}');
+  expect(onChange).toBeCalledWith('{"a": 1}');
+});
+
+it('should handle formatting for invalid JSON', () => {
+  const onChange = jest.fn();
+  const wrapper = shallowRender({ onChange, value: '{"a": 1b}' });
+  wrapper.instance().format();
+  expect(onChange).not.toBeCalled();
+
+  expect(wrapper.state().formatError).toBe(true);
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('should handle formatting for valid JSON', () => {
+  const onChange = jest.fn();
+  const wrapper = shallowRender({ onChange, value: '{"a": 1}' });
+  wrapper.instance().format();
+  expect(onChange).toBeCalledWith(`{
+    "a": 1
+}`);
+
+  expect(wrapper.state().formatError).toBe(false);
+});
+
+it('should handle ignore formatting if empty', () => {
+  const onChange = jest.fn();
+  const wrapper = shallowRender({ onChange, value: '' });
+  wrapper.instance().format();
+  expect(onChange).not.toBeCalled();
+
+  expect(wrapper.state().formatError).toBe(false);
+});
+
+function shallowRender(props: Partial<DefaultSpecializedInputProps> = {}) {
+  return shallow<InputForJSON>(
+    <InputForJSON isDefault={false} name="foo" onChange={jest.fn()} value="" {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/__snapshots__/InputForJSON-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/__snapshots__/InputForJSON-test.tsx.snap
new file mode 100644 (file)
index 0000000..686f509
--- /dev/null
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should handle formatting for invalid JSON 1`] = `
+<div
+  className="display-flex-end"
+>
+  <textarea
+    className="settings-large-input text-top monospaced spacer-right"
+    name="foo"
+    onChange={[Function]}
+    rows={5}
+    value="{\\"a\\": 1b}"
+  />
+  <div>
+    <Alert
+      variant="info"
+    >
+      settings.json.format_error
+       
+    </Alert>
+    <Button
+      className="spacer-top"
+      onClick={[Function]}
+    >
+      settings.json.format
+    </Button>
+  </div>
+</div>
+`;
+
+exports[`should render correctly 1`] = `
+<div
+  className="display-flex-end"
+>
+  <textarea
+    className="settings-large-input text-top monospaced spacer-right"
+    name="foo"
+    onChange={[Function]}
+    rows={5}
+    value=""
+  />
+  <div>
+    <Button
+      className="spacer-top"
+      onClick={[Function]}
+    >
+      settings.json.format
+    </Button>
+  </div>
+</div>
+`;
index 6fd9a323fca64550b0fb681e2182e173ea9ca093..219b4e6a55484980b72769dcb28d880f5836dd61 100644 (file)
@@ -1050,6 +1050,9 @@ settings.default.password=<password>
 settings.reset_confirm.title=Reset Setting
 settings.reset_confirm.description=Are you sure that you want to reset this setting?
 
+settings.json.format=Format JSON
+settings.json.format_error=Formatting requires valid JSON. Please fix it and retry.
+
 settings.analysis_scope.wildcards.introduction=You can use the following wildcards.
 settings.analysis_scope.wildcards.zero_more_char=Match zero or more characters
 settings.analysis_scope.wildcards.zero_more_dir=Match zero or more directories