]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10804 Disable checkbox while loading in select lists
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 5 Jun 2018 06:37:16 +0000 (08:37 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 6 Jun 2018 18:20:50 +0000 (20:20 +0200)
server/sonar-web/src/main/js/app/styles/init/icons.css
server/sonar-web/src/main/js/apps/documentation/components/App.tsx
server/sonar-web/src/main/js/components/SelectList/SelectListListElement.tsx
server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListElement-test.tsx.snap
server/sonar-web/src/main/js/components/common/DeferredSpinner.tsx
server/sonar-web/src/main/js/components/common/__tests__/DeferredSpinner-test.js [deleted file]
server/sonar-web/src/main/js/components/common/__tests__/DeferredSpinner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DeferredSpinner-test.js.snap [deleted file]
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DeferredSpinner-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/Checkbox.tsx
server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx

index b262be7a0e183ee825b00dda2a72d7434a50b579..eba9e98a3d101ad0fe3f3334826de4e659d8479c 100644 (file)
@@ -53,7 +53,7 @@ a[class*=' icon-'] {
 .icon-checkbox {
   display: inline-block;
   vertical-align: top;
-  padding: 2px;
+  padding: 1px 2px;
   box-sizing: border-box;
 }
 
@@ -64,7 +64,9 @@ a[class*=' icon-'] {
   height: 10px;
   border: 1px solid var(--darkBlue);
   border-radius: 2px;
-  transition: all 0.2s ease;
+  transition-property: border-color, background-color, background-image;
+  transition-duration: 0.2s;
+  transition-timing-function: ease;
 }
 
 .icon-checkbox-checked:before {
@@ -73,10 +75,6 @@ a[class*=' icon-'] {
   border-color: var(--blue);
 }
 
-.ie9 .icon-checkbox-checked:before {
-  background-position: -3px 0;
-}
-
 .icon-checkbox-checked.icon-checkbox-single:before {
   background-image: url('data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20stroke-linejoin%3D%22round%22%20stroke-miterlimit%3D%221.414%22%3E%3Cpath%20d%3D%22M10%204.698C10%204.312%209.688%204%209.302%204H4.698C4.312%204%204%204.312%204%204.698v4.604c0%20.386.312.698.698.698h4.604c.386%200%20.698-.312.698-.698V4.698z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E');
 }
index 84ed6c38d7c2d66f3c035fecd1a4a83c946f1852..50f5672de0eb50812094fbdbb9227d37447a9e2d 100644 (file)
@@ -84,10 +84,6 @@ export default class App extends React.PureComponent<Props, State> {
   };
 
   renderContent() {
-    if (this.state.loading) {
-      return <DeferredSpinner />;
-    }
-
     if (this.state.notFound) {
       return <NotFound withContainer={false} />;
     }
@@ -131,7 +127,7 @@ export default class App extends React.PureComponent<Props, State> {
 
         <div className="layout-page-main">
           <div className="layout-page-main-inner documentation-layout-inner">
-            {this.renderContent()}
+            <DeferredSpinner loading={this.state.loading}>{this.renderContent()}</DeferredSpinner>;
           </div>
         </div>
       </div>
index 8fa07588bb21097b753644d29438209769b6c483..8ca761df26d2918b036645d2230d39b74fa3d466 100644 (file)
@@ -66,6 +66,7 @@ export default class SelectListListElement extends React.PureComponent<Props, St
           checked={this.props.selected}
           className={classNames('select-list-list-checkbox', { active: this.props.active })}
           disabled={this.props.disabled}
+          loading={this.state.loading}
           onCheck={this.handleCheck}>
           <span className="little-spacer-left">{this.props.renderElement(this.props.element)}</span>
         </Checkbox>
index 17bbd55a5cf5a49772fa4c8b319df1f0abb72938..76640ae00891c9a18cfffd6e3db108e497f8b53c 100644 (file)
@@ -7,6 +7,7 @@ exports[`should display a loader when checking 1`] = `
   <Checkbox
     checked={false}
     className="select-list-list-checkbox"
+    loading={false}
     onCheck={[Function]}
     thirdState={false}
   >
@@ -26,6 +27,7 @@ exports[`should display a loader when checking 2`] = `
   <Checkbox
     checked={false}
     className="select-list-list-checkbox"
+    loading={false}
     onCheck={[Function]}
     thirdState={false}
   >
index ffc8b0668b7c0244e7c19eac25cf7e1ff9842053..d762f197886316c300600a34a4f7e68bcf766004 100644 (file)
@@ -76,6 +76,6 @@ export default class DeferredSpinner extends React.PureComponent<Props, State> {
         this.props.customSpinner || <i className={classNames('spinner', this.props.className)} />
       );
     }
-    return (this.props.children as JSX.Element) || null;
+    return this.props.children || null;
   }
 }
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/DeferredSpinner-test.js b/server/sonar-web/src/main/js/components/common/__tests__/DeferredSpinner-test.js
deleted file mode 100644 (file)
index 9a44f75..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-// @flow
-import React from 'react';
-import { mount } from 'enzyme';
-import DeferredSpinner from '../DeferredSpinner';
-
-jest.useFakeTimers();
-
-it('renders spinner after timeout', () => {
-  const spinner = mount(<DeferredSpinner />);
-  expect(spinner).toMatchSnapshot();
-  jest.runAllTimers();
-  spinner.update();
-  expect(spinner).toMatchSnapshot();
-});
-
-it('add custom className', () => {
-  const spinner = mount(<DeferredSpinner className="foo" />);
-  jest.runAllTimers();
-  spinner.update();
-  expect(spinner).toMatchSnapshot();
-});
-
-it('renders children before timeout', () => {
-  const spinner = mount(
-    <DeferredSpinner>
-      <div>foo</div>
-    </DeferredSpinner>
-  );
-  expect(spinner).toMatchSnapshot();
-  jest.runAllTimers();
-  spinner.update();
-  expect(spinner).toMatchSnapshot();
-});
-
-it('is controlled by loading prop', () => {
-  const spinner = mount(
-    <DeferredSpinner loading={false}>
-      <div>foo</div>
-    </DeferredSpinner>
-  );
-  expect(spinner).toMatchSnapshot();
-  spinner.setProps({ loading: true });
-  expect(spinner).toMatchSnapshot();
-  jest.runAllTimers();
-  spinner.update();
-  expect(spinner).toMatchSnapshot();
-  spinner.setProps({ loading: false });
-  expect(spinner).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/DeferredSpinner-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/DeferredSpinner-test.tsx
new file mode 100644 (file)
index 0000000..dc46319
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { mount } from 'enzyme';
+import DeferredSpinner from '../DeferredSpinner';
+
+jest.useFakeTimers();
+
+it('renders spinner after timeout', () => {
+  const spinner = mount(<DeferredSpinner />);
+  expect(spinner).toMatchSnapshot();
+  jest.runAllTimers();
+  spinner.update();
+  expect(spinner).toMatchSnapshot();
+});
+
+it('add custom className', () => {
+  const spinner = mount(<DeferredSpinner className="foo" />);
+  jest.runAllTimers();
+  spinner.update();
+  expect(spinner).toMatchSnapshot();
+});
+
+it('renders children before timeout', () => {
+  const spinner = mount(
+    <DeferredSpinner>
+      <div>foo</div>
+    </DeferredSpinner>
+  );
+  expect(spinner).toMatchSnapshot();
+  jest.runAllTimers();
+  spinner.update();
+  expect(spinner).toMatchSnapshot();
+});
+
+it('is controlled by loading prop', () => {
+  const spinner = mount(
+    <DeferredSpinner loading={false}>
+      <div>foo</div>
+    </DeferredSpinner>
+  );
+  expect(spinner).toMatchSnapshot();
+  spinner.setProps({ loading: true });
+  expect(spinner).toMatchSnapshot();
+  jest.runAllTimers();
+  spinner.update();
+  expect(spinner).toMatchSnapshot();
+  spinner.setProps({ loading: false });
+  expect(spinner).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DeferredSpinner-test.js.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DeferredSpinner-test.js.snap
deleted file mode 100644 (file)
index 51d17f5..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`add custom className 1`] = `
-<DeferredSpinner
-  className="foo"
-  timeout={100}
->
-  <i
-    className="spinner foo"
-  />
-</DeferredSpinner>
-`;
-
-exports[`is controlled by loading prop 1`] = `
-<DeferredSpinner
-  loading={false}
-  timeout={100}
->
-  <div>
-    foo
-  </div>
-</DeferredSpinner>
-`;
-
-exports[`is controlled by loading prop 2`] = `
-<DeferredSpinner
-  loading={true}
-  timeout={100}
->
-  <div>
-    foo
-  </div>
-</DeferredSpinner>
-`;
-
-exports[`is controlled by loading prop 3`] = `
-<DeferredSpinner
-  loading={true}
-  timeout={100}
->
-  <i
-    className="spinner"
-  />
-</DeferredSpinner>
-`;
-
-exports[`is controlled by loading prop 4`] = `
-<DeferredSpinner
-  loading={false}
-  timeout={100}
->
-  <div>
-    foo
-  </div>
-</DeferredSpinner>
-`;
-
-exports[`renders children before timeout 1`] = `
-<DeferredSpinner
-  timeout={100}
->
-  <div>
-    foo
-  </div>
-</DeferredSpinner>
-`;
-
-exports[`renders children before timeout 2`] = `
-<DeferredSpinner
-  timeout={100}
->
-  <i
-    className="spinner"
-  />
-</DeferredSpinner>
-`;
-
-exports[`renders spinner after timeout 1`] = `
-<DeferredSpinner
-  timeout={100}
-/>
-`;
-
-exports[`renders spinner after timeout 2`] = `
-<DeferredSpinner
-  timeout={100}
->
-  <i
-    className="spinner"
-  />
-</DeferredSpinner>
-`;
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DeferredSpinner-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DeferredSpinner-test.tsx.snap
new file mode 100644 (file)
index 0000000..51d17f5
--- /dev/null
@@ -0,0 +1,92 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`add custom className 1`] = `
+<DeferredSpinner
+  className="foo"
+  timeout={100}
+>
+  <i
+    className="spinner foo"
+  />
+</DeferredSpinner>
+`;
+
+exports[`is controlled by loading prop 1`] = `
+<DeferredSpinner
+  loading={false}
+  timeout={100}
+>
+  <div>
+    foo
+  </div>
+</DeferredSpinner>
+`;
+
+exports[`is controlled by loading prop 2`] = `
+<DeferredSpinner
+  loading={true}
+  timeout={100}
+>
+  <div>
+    foo
+  </div>
+</DeferredSpinner>
+`;
+
+exports[`is controlled by loading prop 3`] = `
+<DeferredSpinner
+  loading={true}
+  timeout={100}
+>
+  <i
+    className="spinner"
+  />
+</DeferredSpinner>
+`;
+
+exports[`is controlled by loading prop 4`] = `
+<DeferredSpinner
+  loading={false}
+  timeout={100}
+>
+  <div>
+    foo
+  </div>
+</DeferredSpinner>
+`;
+
+exports[`renders children before timeout 1`] = `
+<DeferredSpinner
+  timeout={100}
+>
+  <div>
+    foo
+  </div>
+</DeferredSpinner>
+`;
+
+exports[`renders children before timeout 2`] = `
+<DeferredSpinner
+  timeout={100}
+>
+  <i
+    className="spinner"
+  />
+</DeferredSpinner>
+`;
+
+exports[`renders spinner after timeout 1`] = `
+<DeferredSpinner
+  timeout={100}
+/>
+`;
+
+exports[`renders spinner after timeout 2`] = `
+<DeferredSpinner
+  timeout={100}
+>
+  <i
+    className="spinner"
+  />
+</DeferredSpinner>
+`;
index 42ebbe7da1f3ccf812737029f40c36af4707332e..f4602d3b7ab5f92bf5e991ff7b24b41a0d313b4c 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import * as classNames from 'classnames';
+import DeferredSpinner from '../common/DeferredSpinner';
 
 interface Props {
   checked: boolean;
@@ -26,6 +27,7 @@ interface Props {
   children?: React.ReactNode;
   className?: string;
   id?: string;
+  loading?: boolean;
   onCheck: (checked: boolean, id?: string) => void;
   thirdState?: boolean;
 }
@@ -60,12 +62,18 @@ export default class Checkbox extends React.PureComponent<Props> {
           href="#"
           id={this.props.id}
           onClick={this.handleClick}>
-          <i className={className} />
+          <DeferredSpinner loading={Boolean(this.props.loading)}>
+            <i className={className} />
+          </DeferredSpinner>
           {this.props.children}
         </a>
       );
     }
 
+    if (this.props.loading) {
+      return <DeferredSpinner />;
+    }
+
     return (
       <a
         className={classNames(className, this.props.className)}
index 3804325fe1340b8cfc171bd9a7bc15c0ef70d785..4a3290011f79c497e17a157c66093be9d5f1cf40 100644 (file)
@@ -24,29 +24,34 @@ import { click } from '../../../helpers/testUtils';
 
 it('should render unchecked', () => {
   const checkbox = shallow(<Checkbox checked={false} onCheck={() => true} />);
-  expect(checkbox.is('.icon-checkbox-checked')).toBe(false);
+  expect(checkbox.is('.icon-checkbox-checked')).toBeFalsy();
 });
 
 it('should render checked', () => {
   const checkbox = shallow(<Checkbox checked={true} onCheck={() => true} />);
-  expect(checkbox.is('.icon-checkbox-checked')).toBe(true);
+  expect(checkbox.is('.icon-checkbox-checked')).toBeTruthy();
 });
 
 it('should render disabled', () => {
   const checkbox = shallow(<Checkbox checked={true} disabled={true} onCheck={() => true} />);
-  expect(checkbox.is('.icon-checkbox-disabled')).toBe(true);
+  expect(checkbox.is('.icon-checkbox-disabled')).toBeTruthy();
 });
 
 it('should render unchecked third state', () => {
   const checkbox = shallow(<Checkbox checked={false} onCheck={() => true} thirdState={true} />);
-  expect(checkbox.is('.icon-checkbox-single')).toBe(true);
-  expect(checkbox.is('.icon-checkbox-checked')).toBe(false);
+  expect(checkbox.is('.icon-checkbox-single')).toBeTruthy();
+  expect(checkbox.is('.icon-checkbox-checked')).toBeFalsy();
 });
 
 it('should render checked third state', () => {
   const checkbox = shallow(<Checkbox checked={true} onCheck={() => true} thirdState={true} />);
-  expect(checkbox.is('.icon-checkbox-single')).toBe(true);
-  expect(checkbox.is('.icon-checkbox-checked')).toBe(true);
+  expect(checkbox.is('.icon-checkbox-single')).toBeTruthy();
+  expect(checkbox.is('.icon-checkbox-checked')).toBeTruthy();
+});
+
+it('should render with a spinner', () => {
+  const checkbox = shallow(<Checkbox checked={false} loading={true} onCheck={() => true} />);
+  expect(checkbox.find('DeferredSpinner')).toBeTruthy();
 });
 
 it('should render children', () => {
@@ -56,7 +61,18 @@ it('should render children', () => {
     </Checkbox>
   );
   expect(checkbox.hasClass('link-checkbox')).toBeTruthy();
-  expect(checkbox.find('span')).toHaveLength(1);
+  expect(checkbox.find('span').exists()).toBeTruthy();
+});
+
+it('should render children with a spinner', () => {
+  const checkbox = shallow(
+    <Checkbox checked={false} loading={true} onCheck={() => true}>
+      <span>foo</span>
+    </Checkbox>
+  );
+  expect(checkbox.hasClass('link-checkbox')).toBeTruthy();
+  expect(checkbox.find('span').exists()).toBeTruthy();
+  expect(checkbox.find('DeferredSpinner').exists()).toBeTruthy();
 });
 
 it('should call onCheck', () => {
@@ -84,5 +100,5 @@ it('should apply custom class', () => {
   const checkbox = shallow(
     <Checkbox checked={true} className="customclass" onCheck={() => true} />
   );
-  expect(checkbox.is('.customclass')).toBe(true);
+  expect(checkbox.is('.customclass')).toBeTruthy();
 });