]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10646 Change hover effect of oauth provider buttons
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 1 May 2018 11:41:20 +0000 (13:41 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 3 May 2018 18:20:50 +0000 (20:20 +0200)
server/sonar-web/src/main/js/apps/sessions/components/OAuthProviders.css
server/sonar-web/src/main/js/apps/sessions/components/OAuthProviders.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/OAuthProviders-test.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/OAuthProviders-test.tsx.snap
server/sonar-web/src/main/js/helpers/__tests__/colors-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/colors.ts

index 01d8ef46d8d3f11a668b236772fa52b4ebb73fc5..f77c7c0220b91feb9a010da31a3a7f23561d379e 100644 (file)
 
 .oauth-providers > ul > li > a:hover,
 .oauth-providers > ul > li > a:focus {
-  box-shadow: 0 0 16px rgba(0, 0, 0, 0.2);
+  box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.1);
+}
+
+.oauth-providers > ul > li > a.dark-text {
+  color: var(--secondFontColor);
+}
+
+.oauth-providers > ul > li > a.dark-text:hover,
+.oauth-providers > ul > li > a.dark-text:focus {
+  box-shadow: inset 0 0 0 100px rgba(0, 0, 0, 0.1);
 }
 
 .oauth-providers > ul > li > a > span {
index 817cb3d2ade8e819474964f1605abd493812daa7..b13fae5dd5ecf9f1f23e14717b3a90f577298524 100644 (file)
@@ -24,7 +24,7 @@ import * as theme from '../../../app/theme';
 import { IdentityProvider } from '../../../app/types';
 import Tooltip from '../../../components/controls/Tooltip';
 import HelpIcon from '../../../components/icons-components/HelpIcon';
-import { getTextColor } from '../../../helpers/colors';
+import { isDarkColor } from '../../../helpers/colors';
 import { getBaseUrl } from '../../../helpers/urls';
 import './OAuthProviders.css';
 
@@ -41,38 +41,55 @@ export default function OAuthProviders(props: Props) {
     <section className={classNames('oauth-providers', props.className)}>
       <ul>
         {props.identityProviders.map(identityProvider => (
-          <li key={identityProvider.key}>
-            <a
-              href={
-                `${getBaseUrl()}/sessions/init/${identityProvider.key}` +
-                `?return_to=${encodeURIComponent(props.returnTo)}`
-              }
-              style={{
-                backgroundColor: identityProvider.backgroundColor,
-                color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
-              }}>
-              <img
-                alt={identityProvider.name}
-                height="20"
-                src={getBaseUrl() + identityProvider.iconPath}
-                width="20"
-              />
-              <span>{formatFunction(identityProvider.name)}</span>
-            </a>
-            {identityProvider.helpMessage && (
-              <Tooltip overlay={identityProvider.helpMessage}>
-                <div className="oauth-providers-help">
-                  <HelpIcon fill={theme.blue} />
-                </div>
-              </Tooltip>
-            )}
-          </li>
+          <OAuthProvider
+            format={formatFunction}
+            identityProvider={identityProvider}
+            key={identityProvider.key}
+            returnTo={props.returnTo}
+          />
         ))}
       </ul>
     </section>
   );
 }
 
+interface ItemProps {
+  format: (name: string) => React.ReactNode;
+  identityProvider: IdentityProvider;
+  returnTo: string;
+}
+
+function OAuthProvider({ format, identityProvider, returnTo }: ItemProps) {
+  const hasDarkBackground = isDarkColor(identityProvider.backgroundColor);
+
+  return (
+    <li>
+      <a
+        className={classNames({ 'dark-text': !hasDarkBackground })}
+        href={
+          `${getBaseUrl()}/sessions/init/${identityProvider.key}` +
+          `?return_to=${encodeURIComponent(returnTo)}`
+        }
+        style={{ backgroundColor: identityProvider.backgroundColor }}>
+        <img
+          alt={identityProvider.name}
+          height="20"
+          src={getBaseUrl() + identityProvider.iconPath}
+          width="20"
+        />
+        <span>{format(identityProvider.name)}</span>
+      </a>
+      {identityProvider.helpMessage && (
+        <Tooltip overlay={identityProvider.helpMessage}>
+          <div className="oauth-providers-help">
+            <HelpIcon fill={theme.blue} />
+          </div>
+        </Tooltip>
+      )}
+    </li>
+  );
+}
+
 function defaultFormatLabel(name: string) {
   return translateWithParameters('login.login_with_x', name);
 }
index 65755a26b81a8623d385023dab183ee6b5688e44..e0e1c357f249d37fcbe581996c6f1921f40a8de2 100644 (file)
@@ -38,19 +38,23 @@ const identityProviders = [
 ];
 
 it('should render correctly', () => {
-  expect(
-    shallow(<OAuthProviders identityProviders={identityProviders} returnTo="" />)
-  ).toMatchSnapshot();
+  const wrapper = shallow(<OAuthProviders identityProviders={identityProviders} returnTo="" />);
+  expect(wrapper).toMatchSnapshot();
+  wrapper.find('OAuthProvider').forEach(node => expect(node.dive()).toMatchSnapshot());
 });
 
 it('should use the custom label formatter', () => {
+  const wrapper = shallow(
+    <OAuthProviders
+      formatLabel={name => 'custom_format.' + name}
+      identityProviders={[identityProviders[0]]}
+      returnTo=""
+    />
+  );
   expect(
-    shallow(
-      <OAuthProviders
-        formatLabel={name => 'custom_format.' + name}
-        identityProviders={[identityProviders[0]]}
-        returnTo=""
-      />
-    )
+    wrapper
+      .find('OAuthProvider')
+      .first()
+      .dive()
   ).toMatchSnapshot();
 });
index e7adf301d2c549bafc889f05ae1b499a5f1ed3b2..e37305eb82069e9961956eeef2fa46b4e50dff78 100644 (file)
@@ -5,95 +5,116 @@ exports[`should render correctly 1`] = `
   className="oauth-providers"
 >
   <ul>
-    <li
+    <OAuthProvider
+      format={[Function]}
+      identityProvider={
+        Object {
+          "backgroundColor": "#000",
+          "iconPath": "/some/path",
+          "key": "foo",
+          "name": "Foo",
+        }
+      }
       key="foo"
-    >
-      <a
-        href="/sessions/init/foo?return_to="
-        style={
-          Object {
-            "backgroundColor": "#000",
-            "color": "#fff",
-          }
+      returnTo=""
+    />
+    <OAuthProvider
+      format={[Function]}
+      identityProvider={
+        Object {
+          "backgroundColor": "#00F",
+          "helpMessage": "Help message!",
+          "iconPath": "/icon/path",
+          "key": "bar",
+          "name": "Bar",
         }
-      >
-        <img
-          alt="Foo"
-          height="20"
-          src="/some/path"
-          width="20"
-        />
-        <span>
-          login.login_with_x.Foo
-        </span>
-      </a>
-    </li>
-    <li
+      }
       key="bar"
-    >
-      <a
-        href="/sessions/init/bar?return_to="
-        style={
-          Object {
-            "backgroundColor": "#00F",
-            "color": "#fff",
-          }
-        }
-      >
-        <img
-          alt="Bar"
-          height="20"
-          src="/icon/path"
-          width="20"
-        />
-        <span>
-          login.login_with_x.Bar
-        </span>
-      </a>
-      <Tooltip
-        overlay="Help message!"
-      >
-        <div
-          className="oauth-providers-help"
-        >
-          <HelpIcon
-            fill="#4b9fd5"
-          />
-        </div>
-      </Tooltip>
-    </li>
+      returnTo=""
+    />
   </ul>
 </section>
 `;
 
-exports[`should use the custom label formatter 1`] = `
-<section
-  className="oauth-providers"
->
-  <ul>
-    <li
-      key="foo"
+exports[`should render correctly 2`] = `
+<li>
+  <a
+    className=""
+    href="/sessions/init/foo?return_to="
+    style={
+      Object {
+        "backgroundColor": "#000",
+      }
+    }
+  >
+    <img
+      alt="Foo"
+      height="20"
+      src="/some/path"
+      width="20"
+    />
+    <span>
+      login.login_with_x.Foo
+    </span>
+  </a>
+</li>
+`;
+
+exports[`should render correctly 3`] = `
+<li>
+  <a
+    className=""
+    href="/sessions/init/bar?return_to="
+    style={
+      Object {
+        "backgroundColor": "#00F",
+      }
+    }
+  >
+    <img
+      alt="Bar"
+      height="20"
+      src="/icon/path"
+      width="20"
+    />
+    <span>
+      login.login_with_x.Bar
+    </span>
+  </a>
+  <Tooltip
+    overlay="Help message!"
+  >
+    <div
+      className="oauth-providers-help"
     >
-      <a
-        href="/sessions/init/foo?return_to="
-        style={
-          Object {
-            "backgroundColor": "#000",
-            "color": "#fff",
-          }
-        }
-      >
-        <img
-          alt="Foo"
-          height="20"
-          src="/some/path"
-          width="20"
-        />
-        <span>
-          custom_format.Foo
-        </span>
-      </a>
-    </li>
-  </ul>
-</section>
+      <HelpIcon
+        fill="#4b9fd5"
+      />
+    </div>
+  </Tooltip>
+</li>
+`;
+
+exports[`should use the custom label formatter 1`] = `
+<li>
+  <a
+    className=""
+    href="/sessions/init/foo?return_to="
+    style={
+      Object {
+        "backgroundColor": "#000",
+      }
+    }
+  >
+    <img
+      alt="Foo"
+      height="20"
+      src="/some/path"
+      width="20"
+    />
+    <span>
+      custom_format.Foo
+    </span>
+  </a>
+</li>
 `;
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/colors-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/colors-test.ts
new file mode 100644 (file)
index 0000000..7aed0b0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 colors from '../colors';
+
+describe('#stringToColor', () => {
+  it('should return a color for a text', () => {
+    expect(colors.stringToColor('skywalker')).toBe('#97f047');
+  });
+});
+
+describe('#isDarkColor', () => {
+  it('should be dark', () => {
+    expect(colors.isDarkColor('#000000')).toBeTruthy();
+    expect(colors.isDarkColor('#222222')).toBeTruthy();
+    expect(colors.isDarkColor('#000')).toBeTruthy();
+  });
+  it('should be light', () => {
+    expect(colors.isDarkColor('#FFFFFF')).toBeFalsy();
+    expect(colors.isDarkColor('#CDCDCD')).toBeFalsy();
+    expect(colors.isDarkColor('#FFF')).toBeFalsy();
+  });
+});
+
+describe('#getTextColor', () => {
+  it('should return dark color', () => {
+    expect(colors.getTextColor('#FFF', 'dark', 'light')).toBe('dark');
+    expect(colors.getTextColor('#FFF')).toBe('#222');
+  });
+  it('should return light color', () => {
+    expect(colors.getTextColor('#000', 'dark', 'light')).toBe('light');
+    expect(colors.getTextColor('#000')).toBe('#fff');
+  });
+});
index 3d26b37b9d53b9b389b85bbe70e615c6b938f5a6..291bfd17f180a7478adb9680416366a90700d5c5 100644 (file)
@@ -32,17 +32,20 @@ export function stringToColor(str: string) {
   return color;
 }
 
-export function getTextColor(background: string, dark = '#222', light = '#fff') {
-  background = background.substr(1);
-  if (background.length === 3) {
+export function isDarkColor(color: string) {
+  color = color.substr(1);
+  if (color.length === 3) {
     // shortcut notation: #f90
-    background =
-      background[0] + background[0] + background[1] + background[1] + background[2] + background[2];
+    color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2];
   }
-  const rgb = parseInt(background.substr(1), 16);
+  const rgb = parseInt(color.substr(1), 16);
   const r = (rgb >> 16) & 0xff;
   const g = (rgb >> 8) & 0xff;
   const b = (rgb >> 0) & 0xff;
   const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-  return luma > 140 ? dark : light;
+  return luma < 140;
+}
+
+export function getTextColor(background: string, dark = '#222', light = '#fff') {
+  return isDarkColor(background) ? light : dark;
 }