diff --git a/src/ui/public/chrome/chrome.html b/src/ui/public/chrome/chrome.html index 419615d665dda..42b78e7ee7f39 100644 --- a/src/ui/public/chrome/chrome.html +++ b/src/ui/public/chrome/chrome.html @@ -26,7 +26,7 @@
- +
diff --git a/src/ui/public/chrome/directives/app_switcher/app_switcher.html b/src/ui/public/chrome/directives/app_switcher/app_switcher.html index 3a70738dcb73b..7d8291bc26ae5 100644 --- a/src/ui/public/chrome/directives/app_switcher/app_switcher.html +++ b/src/ui/public/chrome/directives/app_switcher/app_switcher.html @@ -1,26 +1,11 @@ - + diff --git a/src/ui/public/chrome/directives/app_switcher/app_switcher.js b/src/ui/public/chrome/directives/app_switcher/app_switcher.js index 9119e0bf63828..3181f88d62cdc 100644 --- a/src/ui/public/chrome/directives/app_switcher/app_switcher.js +++ b/src/ui/public/chrome/directives/app_switcher/app_switcher.js @@ -1,7 +1,7 @@ import DomLocationProvider from 'ui/dom_location'; import { parse } from 'url'; import { bindKey } from 'lodash'; -import '../app_switcher/app_switcher.less'; +import './app_switcher.less'; import uiModules from 'ui/modules'; import appSwitcherTemplate from './app_switcher.html'; diff --git a/src/ui/public/chrome/directives/app_switcher/app_switcher.less b/src/ui/public/chrome/directives/app_switcher/app_switcher.less index e1cc894fd3a27..0d22b23c0aa0a 100644 --- a/src/ui/public/chrome/directives/app_switcher/app_switcher.less +++ b/src/ui/public/chrome/directives/app_switcher/app_switcher.less @@ -70,81 +70,3 @@ body { overflow-x: hidden; } .flex-parent(@shrink: 0); box-shadow: -4px 0px 3px rgba(0,0,0,0.2); } - -.app-links { - text-align: justify; - - .app-link { - width: @as-open-width; - height: @app-icon-height; - line-height: @app-line-height; - - > a { - display: block; - height: 100%; - color: #ebf7fa; - } - - &.is-app-switcher-app-link-disabled { - opacity: 0.5; - - .app-link__anchor { - cursor: default; - } - } - - .app-icon { - float: left; - font-weight: bold; - text-align: center; - font-size: 1.7em; - display: inline-block; - height: @app-icon-height; - width: @as-closed-width; - - > img { - height: 18px; - margin-top: 8px; - filter: invert(100%); - } - > i { - color: #fff; - line-height: @app-icon-height - } - } - - .app-icon-missing { - float: left; - text-align: center; - font-size: 1.7em; - display: inline-block; - height: @app-icon-height; - line-height: @app-icon-height; - width: @as-closed-width; - background-position: center; - background-size: contain; - background-repeat: no-repeat; - } - - .app-title { - width: calc(@as-open-width - @as-closed-width); - display: inline-block; - float: right; - font-size: 0.9em; - text-align: left; - padding-left: 3px; - line-height: @app-icon-height; - white-space: nowrap; - } - - &:hover, - &.active { - background-color: @app-links-active-background; - > a { - color: @white; - text-decoration: none; - } - } - } - -} diff --git a/src/ui/public/chrome/directives/app_switcher_link/__tests__/app-switcher-link.js b/src/ui/public/chrome/directives/app_switcher_link/__tests__/app-switcher-link.js new file mode 100644 index 0000000000000..25768f2072893 --- /dev/null +++ b/src/ui/public/chrome/directives/app_switcher_link/__tests__/app-switcher-link.js @@ -0,0 +1,211 @@ +import sinon from 'auto-release-sinon'; +import ngMock from 'ng_mock'; +import expect from 'expect.js'; + +import '../app_switcher_link'; + +describe('appSwitcherLink directive', () => { + let scope; + let $compile; + + beforeEach(ngMock.module('kibana')); + + beforeEach(() => { + ngMock.inject(($rootScope, _$compile_) => { + scope = $rootScope.$new(); + $compile = _$compile_; + }); + }); + + function create(attrs) { + const template = ` + + `; + + const element = $compile(template)(scope); + + scope.$apply(() => { + Object.assign(scope, attrs); + }); + + return element; + } + + describe('interface', () => { + + describe('appSwitcherLinkIsActive attribute', () => { + it(`doesn't apply the active class when false`, () => { + const element = create({ + appSwitcherLinkIsActive: false, + }); + expect(element.hasClass('active')).to.be(false); + }); + + it('applies the active class when true', () => { + const element = create({ + appSwitcherLinkIsActive: true, + }); + expect(element.hasClass('active')).to.be(true); + }); + }); + + describe('appSwitcherLinkIsDisabled attribute', () => { + it(`doesn't apply the is-app-switcher-link-disabled class when false`, () => { + const element = create({ + appSwitcherLinkIsDisabled: false, + }); + expect(element.hasClass('is-app-switcher-link-disabled')).to.be(false); + }); + + it('applies the is-app-switcher-link-disabled class when true', () => { + const element = create({ + appSwitcherLinkIsDisabled: true, + }); + expect(element.hasClass('is-app-switcher-link-disabled')).to.be(true); + }); + }); + + describe('appSwitcherLinkTooltip attribute', () => { + it('is applied to the tooltip directive', () => { + const attrs = { + appSwitcherLinkTooltip: 'hello i am a tooltip', + }; + const element = create(attrs); + expect(element.attr('tooltip')).to.be(attrs.appSwitcherLinkTooltip); + }); + }); + + describe('appSwitcherLinkOnClick attribute', () => { + it('is called when the link is clicked', () => { + const attrs = { + appSwitcherLinkOnClick: sinon.spy(), + }; + const element = create(attrs); + element.find('[data-test-subj=appLink]').click(); + sinon.assert.called(attrs.appSwitcherLinkOnClick); + }); + }); + + describe('appSwitcherLinkHref attribute', () => { + it('is applied to the link', () => { + const attrs = { + appSwitcherLinkHref: 'link to a website', + }; + const element = create(attrs); + const link = element.find('[data-test-subj=appLink]'); + expect(link.attr('href')).to.be(attrs.appSwitcherLinkHref); + }); + }); + + describe('appSwitcherLinkKbnRoute attribute', () => { + it(`is applied to the link when href isn't defined`, () => { + const attrs = { + appSwitcherLinkKbnRoute: '#test', + }; + const element = create(attrs); + const link = element.find('[data-test-subj=appLink]'); + expect(link.attr('href')).to.be(attrs.appSwitcherLinkKbnRoute); + }); + + it(`isn't applied to the link when href is defined`, () => { + const attrs = { + appSwitcherLinkHref: 'link to a website', + appSwitcherLinkKbnRoute: '#test', + }; + const element = create(attrs); + const link = element.find('[data-test-subj=appLink]'); + expect(link.attr('href')).not.to.be(attrs.appSwitcherLinkKbnRoute); + }); + }); + + describe('appSwitcherLinkIcon attribute', () => { + describe('when present', () => { + it('displays the img element', () => { + const attrs = { + appSwitcherLinkIcon: 'icon url', + }; + const element = create(attrs); + const img = element.find('img'); + expect(img.length).to.be(1); + }); + + it('hides the placeholder', () => { + const attrs = { + appSwitcherLinkIcon: 'icon url', + }; + const element = create(attrs); + const placeholder = element.find('[data-test-subj=appLinkIconPlaceholder]'); + expect(placeholder.length).to.be(0); + }); + + it(`is set as the img src`, () => { + const attrs = { + appSwitcherLinkIcon: 'icon url', + }; + const element = create(attrs); + const img = element.find('img'); + expect(img.attr('src')).to.contain(encodeURI(attrs.appSwitcherLinkIcon)); + }); + }); + + describe('when not present', () => { + it('hides the img element', () => { + const attrs = { + appSwitcherLinkIcon: undefined, + }; + const element = create(attrs); + const img = element.find('img'); + expect(img.length).to.be(0); + }); + + it('displays the placeholder', () => { + const attrs = { + appSwitcherLinkIcon: undefined, + }; + const element = create(attrs); + const placeholder = element.find('[data-test-subj=appLinkIconPlaceholder]'); + expect(placeholder.length).to.be(1); + }); + + it(`uses the title's first letter as the placeholder`, () => { + const attrs = { + appSwitcherLinkIcon: undefined, + appSwitcherLinkTitle: 'Xyz', + }; + const element = create(attrs); + const placeholder = element.find('[data-test-subj=appLinkIconPlaceholder]'); + expect(placeholder.text()).to.contain(attrs.appSwitcherLinkTitle[0]); + }); + }); + }); + + describe('appSwitcherLinkTitle attribute', () => { + it('is displayed', () => { + const attrs = { + appSwitcherLinkTitle: 'demo title', + }; + const element = create(attrs); + const title = element.find('.app-switcher-link__title'); + expect(title.text().trim()).to.be(attrs.appSwitcherLinkTitle); + }); + + it('is set as a title attribute on the anchor tag', () => { + const attrs = { + appSwitcherLinkTitle: 'demo title', + }; + const element = create(attrs); + const link = element.find('[data-test-subj=appLink]'); + expect(link.attr('title')).to.be(attrs.appSwitcherLinkTitle); + }); + }); + }); +}); diff --git a/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.html b/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.html new file mode 100644 index 0000000000000..6a7843d0430cf --- /dev/null +++ b/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.html @@ -0,0 +1,36 @@ + diff --git a/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.js b/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.js new file mode 100644 index 0000000000000..11adda2828a78 --- /dev/null +++ b/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.js @@ -0,0 +1,35 @@ + +import appSwitcherLinkTemplate from './app_switcher_link.html'; +import './app_switcher_link.less'; +import uiModules from 'ui/modules'; + +const module = uiModules.get('kibana'); + +module.directive('appSwitcherLink', chrome => { + return { + restrict: 'E', + replace: true, + scope: { + isActive: '=appSwitcherLinkIsActive', + isDisabled: '=appSwitcherLinkIsDisabled', + tooltip: '=appSwitcherLinkTooltip', + onClick: '&appSwitcherLinkOnClick', + href: '=appSwitcherLinkHref', + kbnRoute: '=appSwitcherLinkKbnRoute', + icon: '=appSwitcherLinkIcon', + title: '=appSwitcherLinkTitle' + }, + template: appSwitcherLinkTemplate, + link: scope => { + scope.getHref = () => { + if (scope.href) { + return scope.href; + } + + if (scope.kbnRoute) { + return chrome.addBasePath(scope.kbnRoute); + } + }; + } + }; +}); diff --git a/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.less b/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.less new file mode 100644 index 0000000000000..c2b84d8376781 --- /dev/null +++ b/src/ui/public/chrome/directives/app_switcher_link/app_switcher_link.less @@ -0,0 +1,77 @@ +@import (reference) "~ui/styles/variables"; + +.app-switcher-link { + width: @as-open-width; + height: @app-icon-height; + line-height: @app-line-height; + + &.is-app-switcher-link-disabled { + opacity: 0.5; + + .app-switcher-link__anchor { + cursor: default; + } + } + + &:hover, + &.active { + background-color: @app-links-active-background; + + .app-switcher-link__anchor { + color: @white; + text-decoration: none; + } + } +} + + .app-switcher-link__anchor { + display: block; + height: 100%; + color: #ebf7fa; + + /** + * 1. TODO: Override anchor styles. Fix this by removing a tag styles. + */ + &:focus { + color: #ebf7fa; /* 1 */ + } + } + + .app-switcher-link__icon { + display: inline-block; + width: @as-closed-width; + height: @app-icon-height; + float: left; + text-align: center; + font-size: 1.7em; + } + + /** + * This imgae is used to display the icon. + */ + .app-switcher-link__icon-image { + height: 18px; + margin-top: 8px; + filter: invert(100%); + } + + /** + * This placeholder text gets shown if there is no specified icon. + */ + .app-switcher-link__icon-placeholder { + line-height: @app-icon-height; + background-position: center; + background-size: contain; + background-repeat: no-repeat; + } + + .app-switcher-link__title { + width: calc(@as-open-width - @as-closed-width); + display: inline-block; + float: right; + font-size: 0.9em; + text-align: left; + padding-left: 3px; + line-height: @app-icon-height; + white-space: nowrap; + } diff --git a/src/ui/public/chrome/directives/index.js b/src/ui/public/chrome/directives/index.js index c39bb921d3a5b..7be137eb52539 100644 --- a/src/ui/public/chrome/directives/index.js +++ b/src/ui/public/chrome/directives/index.js @@ -1,4 +1,5 @@ import './app_switcher'; +import './app_switcher_link'; import kbnChromeProv from './kbn_chrome'; import kbnChromeNavControlsProv from './append_nav_controls'; import './kbn_loading_indicator'; diff --git a/src/ui/public/styles/variables/for-theme.less b/src/ui/public/styles/variables/for-theme.less index 21511cc7dccc5..8f49e090bf361 100644 --- a/src/ui/public/styles/variables/for-theme.less +++ b/src/ui/public/styles/variables/for-theme.less @@ -106,7 +106,6 @@ // AppSwitcher ================================================================= @app-switcher-application-bg: @gray-lighter; -@app-switcher-app-link-color: @white; @app-switcher-app-title-color: @text-color; @app-switcher-app-description-color: @gray;