GoodBye Notifier (#41663) (#42122)

* Begin notifier removal

* Remove remaining notifier traces

* Remove dead translations

* Remove Angular from config listener

* Import angular-sanitize explicitly in map
This commit is contained in:
Tim Roes 2019-07-29 14:32:36 +02:00 committed by GitHub
parent 73ab20ed80
commit 92d1e5f5df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 9 additions and 952 deletions

View file

@ -61,7 +61,6 @@ This list of plugins is not guaranteed to work on your version of Kibana. Instea
[float]
=== Other
* https://github.com/nreese/kibana-time-plugin[Time picker as a dashboard panel] Widget to view and edit the time range from within dashboards.
* https://github.com/sw-jung/kibana_notification_center[Notification Center] (sw-jung) - for better experience of notifier toasts.
* https://github.com/Webiks/kibana-API.git[Kibana-API] (webiks) Exposes an API with Kibana functionality.
Use it to create, edit and embed visualizations, and also to search inside an embedded dashboard.

View file

@ -45,7 +45,6 @@ const module = uiModules.get('apps/context', [
'elasticsearch',
'kibana',
'kibana/config',
'kibana/notify',
'ngRoute',
]);

View file

@ -78,7 +78,6 @@ const fetchStatuses = {
};
const app = uiModules.get('apps/discover', [
'kibana/notify',
'kibana/courier',
'kibana/url',
'kibana/index_patterns'

View file

@ -24,7 +24,6 @@ import { createLegacyClass } from 'ui/utils/legacy_class';
import { SavedObjectProvider } from 'ui/saved_objects/saved_object';
const module = uiModules.get('discover/saved_searches', [
'kibana/notify',
'kibana/courier'
]);

View file

@ -22,9 +22,7 @@ import 'ui/notify';
import { uiModules } from 'ui/modules';
import { SavedObjectLoader, SavedObjectsClientProvider } from 'ui/saved_objects';
import { savedObjectManagementRegistry } from '../../management/saved_object_registry';
const module = uiModules.get('discover/saved_searches', [
'kibana/notify'
]);
const module = uiModules.get('discover/saved_searches');
// Register this service with the saved object registry so it can be
// edited by the object editor.

View file

@ -28,7 +28,6 @@ import 'plugins/kibana/doc_viewer';
import { getRootBreadcrumbs } from 'plugins/kibana/discover/breadcrumbs';
const app = uiModules.get('apps/doc', [
'kibana/notify',
'kibana/courier',
'kibana/index_patterns'
]);

View file

@ -106,7 +106,6 @@ uiRoutes
uiModules
.get('app/visualize', [
'kibana/notify',
'kibana/url'
])
.directive('visualizeApp', function () {

View file

@ -1,8 +1,4 @@
<div class="app-wrapper-panel">
<kbn-notifications
list="notifList"
></kbn-notifications>
<div id="globalBannerList"></div>
<div

View file

@ -25,7 +25,6 @@ import { uiModules } from '../../modules';
import template from './kbn_chrome.html';
import {
notify,
GlobalBannerList,
banners,
} from '../../notify';
@ -58,9 +57,6 @@ export function kbnChromeProvider(chrome, internals) {
controllerAs: 'chrome',
controller($scope, $location, Private) {
// Notifications
$scope.notifList = notify._notifs;
$scope.getFirstPathSegment = () => {
return $location.path().split('/')[1];
};

View file

@ -1,114 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import angular from 'angular';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import 'plugins/kibana/discover/index';
let $parentScope;
let $scope;
let $elem;
const init = function (text) {
// Load the application
ngMock.module('kibana');
// Create the scope
ngMock.inject(function ($rootScope, $compile) {
// Give us a scope
$parentScope = $rootScope;
// Create the element
$elem = angular.element(
'<kbn-truncated source="' + text + '" length="10"></kbn-truncated>'
);
// And compile it
$compile($elem)($parentScope);
// Fire a digest cycle
$elem.scope().$digest();
// Grab the isolate scope so we can test it
$scope = $elem.isolateScope();
});
};
function trimmed(text) {
return text.trim().replace(/\s+/g, ' ');
}
describe('kbnTruncate directive', function () {
describe('long strings', function () {
beforeEach(function () {
init('some string of text over 10 characters');
});
it('should trim long strings', function (done) {
expect(trimmed($elem.text())).to.be('some … more');
done();
});
it('should have a link to see more text', function (done) {
expect($elem.find('[ng-click="toggle()"]').text()).to.be('more');
done();
});
it('should show more text if the link is clicked and less text if clicked again', function (done) {
$scope.toggle();
$scope.$digest();
expect(trimmed($elem.text())).to.be('some string of text over 10 characters less');
expect($elem.find('[ng-click="toggle()"]').text()).to.be('less');
$scope.toggle();
$scope.$digest();
expect(trimmed($elem.text())).to.be('some … more');
expect($elem.find('[ng-click="toggle()"]').text()).to.be('more');
done();
});
});
describe('short strings', function () {
beforeEach(function () {
init('short');
});
it('should not trim short strings', function (done) {
expect(trimmed($elem.text())).to.be('short');
done();
});
it('should not have a link', function (done) {
expect($elem.find('[ng-click="toggle()"]').length).to.be(0);
done();
});
});
});

View file

@ -1,165 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import sinon from 'sinon';
import { Notifier } from '..';
import { metadata } from 'ui/metadata';
describe('Notifier', function () {
let $interval;
let notifier;
let params;
const message = 'Oh, the humanity!';
beforeEach(function () {
ngMock.module('kibana');
ngMock.inject(function (_$interval_) {
$interval = _$interval_;
});
});
beforeEach(function () {
params = { location: 'foo' };
notifier = new Notifier(params);
});
afterEach(function () {
Notifier.prototype._notifs.length = 0;
});
describe('#constructor()', function () {
it('sets #from from given location', function () {
expect(notifier.from).to.equal(params.location);
});
});
describe('#error', function () {
testVersionInfo('error');
it('prepends location to message for content', function () {
expect(notify('error').content).to.equal(params.location + ': ' + message);
});
it('sets type to "danger"', function () {
expect(notify('error').type).to.equal('danger');
});
it('sets icon to "warning"', function () {
expect(notify('error').icon).to.equal('warning');
});
it('sets title to "Error"', function () {
expect(notify('error').title).to.equal('Error');
});
it('sets lifetime to 5 minutes', function () {
expect(notify('error').lifetime).to.equal(300000);
});
it('sets timeRemaining and decrements', function () {
const notif = notify('error');
expect(notif.timeRemaining).to.equal(300);
$interval.flush(1000);
expect(notif.timeRemaining).to.equal(299);
});
it('closes notification on lifetime expiry', function () {
const expectation = sinon.mock();
const notif = notifier.error(message, expectation);
expectation.once();
expectation.withExactArgs('ignore');
$interval.flush(300000);
expect(notif.timerId).to.be(undefined);
});
it('allows canceling of timer', function () {
const notif = notify('error');
expect(notif.timerId).to.not.be(undefined);
notif.cancelTimer();
expect(notif.timerId).to.be(undefined);
});
it('resets timer on addition to stack', function () {
const notif = notify('error');
$interval.flush(100000);
expect(notif.timeRemaining).to.equal(200);
notify('error');
expect(notif.timeRemaining).to.equal(300);
});
it('allows reporting', function () {
const includesReport = _.includes(notify('error').actions, 'report');
expect(includesReport).to.true;
});
it('allows accepting', function () {
const includesAccept = _.includes(notify('error').actions, 'accept');
expect(includesAccept).to.true;
});
it('includes stack', function () {
expect(notify('error').stack).to.be.defined;
});
it('has css class helper functions', function () {
expect(notify('error').getIconClass()).to.equal('fa fa-warning');
expect(notify('error').getButtonClass()).to.equal('kuiButton--danger');
expect(notify('error').getAlertClassStack()).to.equal('kbnToast kbnToast-isStack kbnToast--danger');
expect(notify('error').getAlertClass()).to.equal('kbnToast kbnToast--danger');
expect(notify('error').getButtonGroupClass()).to.equal('kbnToast__controls');
expect(notify('error').getToastMessageClass()).to.equal('kbnToast__message');
});
});
function notify(fnName, opts) {
notifier[fnName](message, opts);
return latestNotification();
}
function latestNotification() {
return _.last(notifier._notifs);
}
function testVersionInfo(fnName) {
describe('when version is configured', function () {
it('adds version to notification', function () {
const notification = notify(fnName);
expect(notification.info.version).to.equal(metadata.version);
});
});
describe('when build number is configured', function () {
it('adds buildNum to notification', function () {
const notification = notify(fnName);
expect(notification.info.buildNum).to.equal(metadata.buildNum);
});
});
}
});

View file

@ -1,2 +1 @@
@import './banners/index';
@import './partials/index';

View file

@ -1,66 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import truncText from 'trunc-text';
import { i18n } from '@kbn/i18n';
import truncHTML from 'trunc-html';
import { uiModules } from '../../modules';
import truncatedTemplate from '../partials/truncated.html';
import 'angular-sanitize';
const module = uiModules.get('kibana', ['ngSanitize']);
module.directive('kbnTruncated', function () {
return {
restrict: 'E',
scope: {
source: '@',
length: '@',
isHtml: '@'
},
template: truncatedTemplate,
link: function ($scope) {
const source = $scope.source;
const max = $scope.length;
const truncated = $scope.isHtml
? truncHTML(source, max).html
: truncText(source, max);
$scope.content = truncated;
if (source === truncated) {
return;
}
$scope.truncated = true;
$scope.expanded = false;
const showLessText = i18n.translate('common.ui.directives.truncated.showLessLinkText', {
defaultMessage: 'less'
});
const showMoreText = i18n.translate('common.ui.directives.truncated.showMoreLinkText', {
defaultMessage: 'more'
});
$scope.action = showMoreText;
$scope.toggle = () => {
$scope.expanded = !$scope.expanded;
$scope.content = $scope.expanded ? source : truncated;
$scope.action = $scope.expanded ? showLessText : showMoreText;
};
}
};
});

View file

@ -17,8 +17,6 @@
* under the License.
*/
export { notify } from './notify';
export { Notifier } from './notifier';
export { fatalError, addFatalErrorCallback } from './fatal_error';
export { toastNotifications } from './toasts';
export { GlobalBannerList, banners } from './banners';

View file

@ -1,219 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import { metadata } from '../metadata';
import { formatMsg, formatStack } from './lib';
import '../render_directive';
import { i18n } from '@kbn/i18n';
const notifs = [];
const { version, buildNum } = metadata;
function closeNotif(notif, cb = _.noop, key) {
return function () {
// this === notif
const i = notifs.indexOf(notif);
if (i !== -1) notifs.splice(i, 1);
cancelTimer(notif);
cb(key);
};
}
function cancelTimer(notif) {
if (notif.timerId) {
Notifier.config.clearInterval(notif.timerId);
notif.timerId = undefined;
}
}
function timerCanceler(notif, cb = _.noop, key) {
return function cancelNotifTimer() {
cancelTimer(notif);
cb(key);
};
}
/**
* Initiates a timer to update _timeRemaining_ on the notif at second
* intervals and clears the notif once the notif _lifetime_ has been reached.
*/
function startNotifTimer(notif, cb) {
const interval = 1000;
if (notif.lifetime === Infinity || notif.lifetime === 0) {
return;
}
notif.timeRemaining = Math.floor(notif.lifetime / interval);
notif.timerId = Notifier.config.setInterval(
function () {
notif.timeRemaining -= 1;
if (notif.timeRemaining <= 0) {
closeNotif(notif, cb, 'ignore')();
}
},
interval,
notif.timeRemaining
);
notif.cancelTimer = timerCanceler(notif, cb);
}
function restartNotifTimer(notif, cb) {
cancelTimer(notif);
startNotifTimer(notif, cb);
}
const typeToButtonClassMap = {
danger: 'kuiButton--danger', // NOTE: `error` type is internally named as `danger`
info: 'kuiButton--secondary',
};
const typeToAlertClassMap = {
danger: `kbnToast--danger`,
info: `kbnToast--info`,
};
function add(notif, cb) {
_.set(notif, 'info.version', version);
_.set(notif, 'info.buildNum', buildNum);
notif.clear = closeNotif(notif);
if (notif.actions) {
notif.actions.forEach(function (action) {
notif[action] = closeNotif(notif, cb, action);
});
} else if (notif.customActions) {
// wrap all of the custom functions in a close
notif.customActions = notif.customActions.map((action) => {
return {
key: action.text,
dataTestSubj: action.dataTestSubj,
callback: closeNotif(notif, action.callback, action.text),
getButtonClass() {
const buttonTypeClass = typeToButtonClassMap[notif.type];
return `${buttonTypeClass}`;
},
};
});
}
notif.count = (notif.count || 0) + 1;
notif.isTimed = function isTimed() {
return notif.timerId ? true : false;
};
// decorate the notification with helper functions for the template
notif.getButtonClass = () => `${typeToButtonClassMap[notif.type]}`;
notif.getAlertClassStack = () => `kbnToast kbnToast-isStack ${typeToAlertClassMap[notif.type]}`;
notif.getIconClass = () => `fa fa-${notif.icon}`;
notif.getToastMessageClass = () => 'kbnToast__message';
notif.getAlertClass = () => `kbnToast ${typeToAlertClassMap[notif.type]}`;
notif.getButtonGroupClass = () => 'kbnToast__controls';
let dup = null;
if (notif.content) {
dup = _.find(notifs, function (item) {
return item.content === notif.content && item.lifetime === notif.lifetime;
});
}
if (dup) {
dup.count += 1;
dup.stacks = _.union(dup.stacks, [notif.stack]);
restartNotifTimer(dup, cb);
return dup;
}
startNotifTimer(notif, cb);
notif.stacks = [notif.stack];
notifs.push(notif);
return notif;
}
Notifier.prototype.add = add;
/**
* Functionality to check that
*/
export function Notifier(opts) {
const self = this;
opts = opts || {};
// label type thing to say where notifications came from
self.from = opts.location;
const notificationLevels = ['error'];
notificationLevels.forEach(function (m) {
self[m] = _.bind(self[m], self);
});
}
Notifier.config = {
bannerLifetime: 3000000,
errorLifetime: 300000,
infoLifetime: 5000,
setInterval: window.setInterval,
clearInterval: window.clearInterval,
};
Notifier.applyConfig = function (config) {
_.merge(Notifier.config, config);
};
// simply a pointer to the global notif list
Notifier.prototype._notifs = notifs;
const overridableOptions = ['lifetime', 'icon'];
/**
* Alert the user of an error that occured
* @param {Error|String} err
* @param {Function} cb
*/
Notifier.prototype.error = function (err, opts, cb) {
if (_.isFunction(opts)) {
cb = opts;
opts = {};
}
const config = _.assign({
type: 'danger',
content: formatMsg(err, this.from),
icon: 'warning',
title: i18n.translate('common.ui.notify.toaster.errorTitle', {
defaultMessage: 'Error',
}),
lifetime: Notifier.config.errorLifetime,
actions: ['report', 'accept'],
stack: formatStack(err)
}, _.pick(opts, overridableOptions));
return add(config, cb);
};

View file

@ -19,14 +19,10 @@
import React from 'react';
import { MarkdownSimple } from 'ui/markdown';
import { uiModules } from '../modules';
import { metadata } from '../metadata';
import chrome from '../chrome';
import { fatalError } from './fatal_error';
import { banners } from './banners';
import { Notifier } from './notifier';
import template from './partials/toaster.html';
import './filters/markdown';
import './directives/truncated';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@ -34,60 +30,16 @@ import {
EuiButton,
} from '@elastic/eui';
const module = uiModules.get('kibana/notify');
const config = chrome.getUiSettingsClient();
module.directive('kbnNotifications', function () {
return {
restrict: 'E',
scope: {
list: '=list'
},
replace: true,
template
};
});
export const notify = new Notifier();
module.factory('createNotifier', function () {
return function (opts) {
return new Notifier(opts);
};
});
module.factory('Notifier', function () {
return Notifier;
});
// teach Notifier how to use angular interval services
module.run(function (config, $interval, $compile) {
Notifier.applyConfig({
setInterval: $interval,
clearInterval: $interval.cancel
});
config.getUpdate$().subscribe(() => {
applyConfig(config);
Notifier.$compile = $compile;
});
// if kibana is not included then the notify service can't
// expect access to config (since it's dependent on kibana)
if (!!metadata.kbnIndex) {
require('ui/config');
module.run(function (config) {
config.watchAll(() => applyConfig(config));
});
}
let bannerId;
let bannerTimeoutId;
function applyConfig(config) {
Notifier.applyConfig({
errorLifetime: config.get('notifications:lifetime:error'),
warningLifetime: config.get('notifications:lifetime:warning'),
infoLifetime: config.get('notifications:lifetime:info')
});
// Show user-defined banner.
const bannerContent = config.get('notifications:banner');
const bannerLifetime = config.get('notifications:lifetime:banner');

View file

@ -1 +0,0 @@
@import './toaster';

View file

@ -1,96 +0,0 @@
// REDO Bootstrap alternatives to match EUI
@import '@elastic/eui/src/components/call_out/variables';
@import '@elastic/eui/src/components/call_out/mixins';
.kbnToaster__container {
visibility: visible;
width: 100%;
.kbnToaster {
margin: 0;
padding: 0;
list-style: none;
}
/**
* 1. Shouldn't look like a button.
*/
.kbnToaster__countdown {
appearance: none;
background: none;
border: none;
}
.kbnToast {
display: flex;
align-items: center;
padding: $euiSizeXS $euiSize;
> * {
flex: 0 0 auto;
&:not(:last-child) {
margin-right: $euiSizeXS;
}
}
}
.kbnToast__message {
@include euiFontSizeS;
text-overflow: ellipsis;
flex: 1 1 auto;
white-space: normal;
}
.kbnToast-isStack {
@include euiFontSizeS;
padding-bottom: $euiSizeS;
pre {
display: inline-block;
width: 100%;
margin: $euiSizeS 0;
word-break: normal;
word-wrap: normal;
white-space: pre-wrap;
}
}
.kbnToast__controls {
display: flex;
}
}
$kbnToastTypes: (
info: 'primary',
warning: 'warning',
danger: 'danger',
success: 'success',
);
@each $name, $color in $kbnToastTypes {
$foreground: euiCallOutColor($color, 'foreground');
.kbnToast--#{$name} {
background-color: euiCallOutColor($color, 'background');
color: $foreground;
// Fix dark mode contrast by forcing button colors to use the same foreground color
// Override hover/focus text color changes by using !important
@if ($name = 'danger') {
.kuiButton--danger,
.kuiButton--danger:hover {
color: $foreground !important;
border-color: $foreground;
}
}
@if ($name = 'info') {
.kuiButton--secondary,
.kuiButton--secondary:hover {
color: $foreground !important;
border-color: $foreground;
}
}
}
}

View file

@ -1,105 +0,0 @@
<div class="kbnToaster__container">
<ul class="kbnToaster">
<li ng-repeat="notif in list" kbn-toast notif="notif">
<div ng-class="notif.getAlertClass()">
<span ng-show="notif.count > 1" class="euiBadge euiBadge--default">
<span class="euiBadge__content">
<span class="euiBadge__text">{{ notif.count }}</span>
</span>
</span>
<i ng-class="notif.getIconClass()" tooltip="{{notif.title}}"></i>
<kbn-truncated
ng-if="notif.content"
source="{{notif.content | markdown}}"
is-html="true"
length="250"
ng-class="notif.getToastMessageClass()"
></kbn-truncated>
<render-directive
ng-if="notif.directive"
definition="notif.directive"
notif="notif"
ng-class="notif.getToastMessageClass()"
></render-directive>
<div class="kuiButtonGroup" ng-class="notif.getButtonGroupClass()">
<button
type="button"
ng-if="notif.stack && !notif.showStack"
class="kuiButton kuiButton--small"
ng-class="notif.getButtonClass()"
ng-click="notif.cancelTimer(); notif.showStack = true"
i18n-id="common.ui.notify.toaster.moreInfoButtonLabel"
i18n-default-message="More Info"
>
</button>
<button
type="button"
ng-if="notif.stack && notif.showStack"
class="kuiButton kuiButton--small"
ng-class="notif.getButtonClass()"
ng-click="notif.showStack = false"
i18n-id="common.ui.notify.toaster.lessInfoButtonLabel"
i18n-default-message="Less Info"
>
</button>
<button
data-test-subj="notifierDismissButton"
type="button"
ng-if="notif.accept"
class="kuiButton kuiButton--small"
ng-class="notif.getButtonClass()"
ng-click="notif.accept()"
i18n-id="common.ui.notify.toaster.okButtonLabel"
i18n-default-message="OK"
>
</button>
<button
type="button"
ng-if="notif.address"
class="kuiButton kuiButton--small"
ng-class="notif.getButtonClass()"
ng-click="notif.address()"
i18n-id="common.ui.notify.toaster.fixItButtonLabel"
i18n-default-message="Fix it"
>
</button>
<button
type="button"
class="kuiButton kuiButton--small"
ng-repeat="action in notif.customActions"
ng-class="action.getButtonClass()"
ng-click="action.callback()"
ng-bind="action.key"
data-test-subj="{{action.dataTestSubj}}"
></button>
</div>
<!-- Countdown badge -->
<button
class="euiBadge euiBadge--default"
type="button"
ng-if="notif.isTimed()"
class="kbnToaster__countdown"
ng-click="notif.cancelTimer()"
title="{{ ::'common.ui.notify.toaster.stopCountdownButtonTooltip' | i18n: { defaultMessage: 'Stop' } }}"
>
<span class="euiBadge__content">
<span
i18n-id="common.ui.notify.toaster.timeRemainingInSecondsLabel"
i18n-default-message="{timeRemaining}s"
i18n-values="{ timeRemaining: notif.timeRemaining }"
></span>
</span>
</button>
</div>
<div ng-if="notif.stack && notif.showStack" ng-class="notif.getAlertClassStack()">
<pre ng-repeat="stack in notif.stacks" ng-bind="stack"></pre>
</div>
</li>
</ul>
</div>

View file

@ -1,5 +0,0 @@
<span ng-if="!isHtml">{{content}}</span>
<span ng-if="isHtml" ng-bind-html="content"></span>
<span ng-if="truncated">
<a ng-click="toggle()">{{action}}</a>
</span>

View file

@ -41,7 +41,7 @@ describe('wrapRouteWithPrep fn', function () {
const delayUserWork = opts.delayUserWork ? 50 : 0;
return function () {
ngMock.module('kibana', 'kibana/notify');
ngMock.module('kibana');
let setupComplete = false;
let userWorkComplete = false;
let route;

View file

@ -22,7 +22,6 @@ import chrome from '../chrome';
import { parse as parseUrl } from 'url';
import sinon from 'sinon';
import { Notifier } from '../notify';
import { metadata } from '../metadata';
import { UiSettingsClient } from '../../../../core/public';
@ -67,15 +66,6 @@ function createStubUiSettings() {
createStubUiSettings();
sinon.stub(chrome, 'getUiSettingsClient').callsFake(() => stubUiSettings);
beforeEach(function () {
// ensure that notifications are not left in the notifiers
if (Notifier.prototype._notifs.length) {
const notifs = JSON.stringify(Notifier.prototype._notifs);
Notifier.prototype._notifs.length = 0;
throw new Error('notifications were left in the notifier: ' + notifs);
}
});
afterEach(function () {
createStubUiSettings();
});

View file

@ -23,6 +23,7 @@ import MarkdownIt from 'markdown-it';
import { ORIGIN } from '../../../../core_plugins/tile_map/common/origin';
import { EMSClient } from '../../../../core_plugins/tile_map/common/ems_client';
import { i18n } from '@kbn/i18n';
import 'angular-sanitize';
const markdownIt = new MarkdownIt({
html: false,
@ -31,7 +32,7 @@ const markdownIt = new MarkdownIt({
const TMS_IN_YML_ID = 'TMS in config/kibana.yml';
uiModules.get('kibana')
uiModules.get('kibana', ['ngSanitize'])
.service('serviceSettings', function ($sanitize, mapConfig, tilemapsConfig, kbnVersion) {
const attributionFromConfig = $sanitize(markdownIt.render(tilemapsConfig.deprecated.config.options.attribution || ''));

View file

@ -1,49 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export default function ({ getService, getPageObjects }) {
const log = getService('log');
const PageObjects = getPageObjects(['monitoring', 'settings']);
describe('dismiss x-pack', function () {
// Putting everything here in 'before' so it doesn't count as a test
// since x-pack may or may not be installed. We just want the banner closed.
before(function () {
log.debug('check for X-Pack welcome, opt-out, and dismiss it');
// find class kbnToaster and see if there's any list items in it?
return PageObjects.settings
.navigateTo()
.then(() => {
return PageObjects.monitoring.getToasterContents();
})
.then(contents => {
// Welcome to X-Pack!
// Sharing your cluster statistics with us helps us improve. Your data is never shared with anyone. Not interested? Opt out here.
// Dismiss
log.debug('Toast banner contents = ' + contents);
if (contents.includes('X-Pack')) {
return PageObjects.monitoring.clickOptOut().then(() => {
return PageObjects.monitoring.dismissWelcome();
});
}
});
});
});
}

View file

@ -35,7 +35,6 @@ export default async function ({ readConfigFile }) {
require.resolve('./apps/status_page'),
require.resolve('./apps/timelion'),
require.resolve('./apps/visualize'),
require.resolve('./apps/xpack'),
],
pageObjects,
services,

View file

@ -21,7 +21,6 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
const config = getService('config');
const log = getService('log');
const retry = getService('retry');
const find = getService('find');
const testSubjects = getService('testSubjects');
const appsMenu = getService('appsMenu');
const globalNav = getService('globalNav');
@ -65,21 +64,6 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
await this.awaitGlobalLoadingIndicatorHidden();
}
async getToastMessage(findTimeout = defaultFindTimeout) {
const toastMessage = await find.displayedByCssSelector(
'kbn-truncated.kbnToast__message',
findTimeout
);
const messageText = await toastMessage.getVisibleText();
log.debug(`getToastMessage: ${messageText}`);
return messageText;
}
async clickToastOK() {
log.debug('clickToastOK');
await find.clickByCssSelector('button[ng-if="notif.accept"]');
}
async waitUntilLoadingHasFinished() {
try {
await this.isGlobalLoadingIndicatorVisible();

View file

@ -18,7 +18,6 @@
*/
export function MonitoringPageProvider({ getService }) {
const testSubjects = getService('testSubjects');
const find = getService('find');
class MonitoringPage {
@ -27,15 +26,6 @@ export function MonitoringPageProvider({ getService }) {
return await el.getVisibleText();
}
dismissWelcome() {
return testSubjects.click('notifierDismissButton');
}
async getToasterContents() {
const el = await find.byCssSelector('div.kbnToaster__container');
return await el.getVisibleText();
}
async clickOptOut() {
return find.clickByLinkText('Opt out here');
}

View file

@ -17,7 +17,6 @@
/* hide unusable controls */
.kbnGlobalNav,
kbn-top-nav,
.kbnToaster__container,
filter-bar,
::-webkit-scrollbar,
.euiNavDrawer {

View file

@ -17,7 +17,6 @@
/* hide unusable controls */
.kbnGlobalNav,
kbn-top-nav,
.kbnToaster__container,
filter-bar,
::-webkit-scrollbar,
.euiNavDrawer {

View file

@ -298,8 +298,6 @@
"common.ui.directives.fieldNameIcons.stringFieldAriaLabel": "文字列フィールド",
"common.ui.directives.fieldNameIcons.unknownFieldAriaLabel": "不明なフィールド",
"common.ui.directives.paginate.size.allDropDownOptionLabel": "すべて",
"common.ui.directives.truncated.showLessLinkText": "隠す",
"common.ui.directives.truncated.showMoreLinkText": "もっと",
"common.ui.dualRangeControl.mustSetBothErrorMessage": "下と上の値の両方を設定する必要があります",
"common.ui.dualRangeControl.outsideOfRangeErrorMessage": "値は {min} と {max} の間でなければなりません",
"common.ui.dualRangeControl.upperValidErrorMessage": "上の値は下の値以上でなければなりません",
@ -507,13 +505,6 @@
"common.ui.notify.fatalError.unavailableServerErrorMessage": "HTTP リクエストが接続に失敗しました。Kibana サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。",
"common.ui.notify.toaster.errorMessage": "エラー: {errorMessage}\n {errorStack}",
"common.ui.notify.toaster.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}",
"common.ui.notify.toaster.errorTitle": "エラー",
"common.ui.notify.toaster.fixItButtonLabel": "修正",
"common.ui.notify.toaster.lessInfoButtonLabel": "情報を縮小",
"common.ui.notify.toaster.moreInfoButtonLabel": "情報を拡張",
"common.ui.notify.toaster.okButtonLabel": "OK",
"common.ui.notify.toaster.stopCountdownButtonTooltip": "停止",
"common.ui.notify.toaster.timeRemainingInSecondsLabel": "{timeRemaining}s",
"common.ui.notify.toaster.unavailableServerErrorMessage": "HTTP リクエストが接続に失敗しました。Kibana サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。",
"common.ui.paginateControls.pageSizeLabel": "ページサイズ",
"common.ui.paginateControls.scrollTopButtonLabel": "最上部に移動",
@ -10723,4 +10714,4 @@
"xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "ログテキストが必要です。",
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
}
}
}

View file

@ -298,8 +298,6 @@
"common.ui.directives.fieldNameIcons.stringFieldAriaLabel": "字符串字段",
"common.ui.directives.fieldNameIcons.unknownFieldAriaLabel": "未知字段",
"common.ui.directives.paginate.size.allDropDownOptionLabel": "全部",
"common.ui.directives.truncated.showLessLinkText": "更少",
"common.ui.directives.truncated.showMoreLinkText": "更多",
"common.ui.dualRangeControl.mustSetBothErrorMessage": "下限值和上限值都须设置",
"common.ui.dualRangeControl.outsideOfRangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内",
"common.ui.dualRangeControl.upperValidErrorMessage": "上限值必须大于或等于下限值",
@ -507,13 +505,6 @@
"common.ui.notify.fatalError.unavailableServerErrorMessage": "HTTP 请求无法连接。请检查 Kibana 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。",
"common.ui.notify.toaster.errorMessage": "错误:{errorMessage}\n {errorStack}",
"common.ui.notify.toaster.errorStatusMessage": "错误 {errStatus} {errStatusText}{errMessage}",
"common.ui.notify.toaster.errorTitle": "错误",
"common.ui.notify.toaster.fixItButtonLabel": "解决",
"common.ui.notify.toaster.lessInfoButtonLabel": "更少信息",
"common.ui.notify.toaster.moreInfoButtonLabel": "更多信息",
"common.ui.notify.toaster.okButtonLabel": "确定",
"common.ui.notify.toaster.stopCountdownButtonTooltip": "停止",
"common.ui.notify.toaster.timeRemainingInSecondsLabel": "{timeRemaining} 秒",
"common.ui.notify.toaster.unavailableServerErrorMessage": "HTTP 请求无法连接。请检查 Kibana 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。",
"common.ui.paginateControls.pageSizeLabel": "页面大小",
"common.ui.paginateControls.scrollTopButtonLabel": "滚动至顶部",
@ -10723,4 +10714,4 @@
"xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "“日志文本”必填。",
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
}
}
}