Skip to content

Commit

Permalink
[Telemetry] Show opt-in changes for OSS users (#50831) (#50940)
Browse files Browse the repository at this point in the history
* WIP: Notice banner for OSS folks

* Add telemetryNotifyUserAboutOptInDefault to injected vars

* add userHasSeenNotice check

* More WIP on banner notice component

* Text changes on screens

* make userHasSeenNotice flag work

* Finalzed splash text + checking new flag

* Consolidating banner calls and saving status of opt-in notice

* Conditionally remove the banner and add some code docs

* Fixing prior welcome tests

* api integration test for user has seen opt in

* change post method to put in ui

* unit test for get_telemetry_notify_user_about_optin_default

* Ignore TS woes

* Adding new tests and snapshots for opt-in banner component

* Notice banner test

* Translation miss

* More opt-in tests

* increase types usage

* roll back core server api change

* update snapshot

* Prop name change + snapshot updates
  • Loading branch information
Joel Griffith authored Nov 18, 2019
1 parent 2caa5bd commit e312175
Show file tree
Hide file tree
Showing 25 changed files with 682 additions and 16 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class Home extends Component {
getServices().getInjected('disableWelcomeScreen') ||
props.localStorage.getItem(KEY_ENABLE_WELCOME) === 'false'
);
const showTelemetryDisclaimer = getServices().getInjected('allowChangingOptInStatus');
const showTelemetryDisclaimer = getServices().getInjected('telemetryNotifyUserAboutOptInDefault');

this.state = {
// If welcome is enabled, we wait for loading to complete
Expand Down Expand Up @@ -231,6 +231,7 @@ export class Home extends Component {
onSkip={this.skipWelcome}
urlBasePath={this.props.urlBasePath}
showTelemetryDisclaimer={this.state.showTelemetryDisclaimer}
onOptInSeen={this.props.onOptInSeen}
/>
);
}
Expand Down Expand Up @@ -269,4 +270,5 @@ Home.propTypes = {
localStorage: PropTypes.object.isRequired,
urlBasePath: PropTypes.string.isRequired,
mlEnabled: PropTypes.bool.isRequired,
onOptInSeen: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export function HomeApp({ directories }) {
savedObjectsClient,
getBasePath,
addBasePath,
telemetryOptInProvider: {
setOptInNoticeSeen,
},
} = getServices();

const isCloudEnabled = getInjected('isCloudEnabled', false);
Expand Down Expand Up @@ -83,6 +86,7 @@ export function HomeApp({ directories }) {
find={savedObjectsClient.find}
localStorage={localStorage}
urlBasePath={getBasePath()}
onOptInSeen={setOptInNoticeSeen}
/>
</Route>
<Route path="/home">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jest.mock('../kibana_services', () => ({
test('should render a Welcome screen with the telemetry disclaimer', () => {
const component = shallow(
// @ts-ignore
<Welcome urlBase="/" onSkip={() => {}} showTelemetryDisclaimer={true} />
<Welcome urlBase="/" onSkip={() => {}} showTelemetryDisclaimer={true} onOptInSeen={() => {}} />
);

expect(component).toMatchSnapshot();
Expand All @@ -45,8 +45,19 @@ test('should render a Welcome screen with no telemetry disclaimer', () => {
// @ts-ignore
const component = shallow(
// @ts-ignore
<Welcome urlBase="/" onSkip={() => {}} showTelemetryDisclaimer={false} />
<Welcome urlBase="/" onSkip={() => {}} showTelemetryDisclaimer={false} onOptInSeen={() => {}} />
);

expect(component).toMatchSnapshot();
});

test('fires opt-in seen when mounted', () => {
const seen = jest.fn();

shallow(
// @ts-ignore
<Welcome urlBase="/" onSkip={() => {}} showTelemetryDisclaimer={true} onOptInSeen={seen} />
);

expect(seen).toHaveBeenCalled();
});
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { SampleDataCard } from './sample_data';
interface Props {
urlBasePath: string;
onSkip: () => void;
onOptInSeen: () => any;
showTelemetryDisclaimer: boolean;
}

Expand Down Expand Up @@ -75,6 +76,7 @@ export class Welcome extends React.Component<Props> {

componentDidMount() {
this.services.trackUiMetric(this.services.METRIC_TYPE.LOADED, 'welcomeScreenMount');
this.props.onOptInSeen();
document.addEventListener('keydown', this.hideOnEsc);
}

Expand Down Expand Up @@ -132,17 +134,17 @@ export class Welcome extends React.Component<Props> {
>
<FormattedMessage
id="kbn.home.dataManagementDisclaimerPrivacyLink"
defaultMessage="Privacy Policy."
defaultMessage="Privacy Statement."
/>
</EuiLink>
<FormattedMessage
id="kbn.home.dataManagementDisableCollection"
defaultMessage=" To disable collection, "
defaultMessage=" To stop collection, "
/>
<EuiLink href="#/management/kibana/settings">
<FormattedMessage
id="kbn.home.dataManagementDisableCollectionLink"
defaultMessage="click here."
defaultMessage="disable usage data here."
/>
</EuiLink>
</EuiTextColor>
Expand Down
1 change: 1 addition & 0 deletions src/legacy/core_plugins/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ const telemetry = (kibana: any) => {
telemetryOptInStatusUrl: config.get('telemetry.optInStatusUrl'),
allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'),
telemetrySendUsageFrom: config.get('telemetry.sendUsageFrom'),
telemetryNotifyUserAboutOptInDefault: false,
};
},
hacks: ['plugins/telemetry/hacks/telemetry_init', 'plugins/telemetry/hacks/telemetry_opt_in'],
Expand Down
3 changes: 3 additions & 0 deletions src/legacy/core_plugins/telemetry/mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"lastVersionChecked": {
"ignore_above": 256,
"type": "keyword"
},
"userHasSeenNotice": {
"type": "boolean"
}
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 React from 'react';
import { EuiButton } from '@elastic/eui';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { OptedInBanner } from './opted_in_notice_banner';

describe('OptInDetailsComponent', () => {
it('renders as expected', () => {
expect(shallowWithIntl(<OptedInBanner onSeenBanner={() => {}} />)).toMatchSnapshot();
});

it('fires the "onSeenBanner" prop when a link is clicked', () => {
const onLinkClick = jest.fn();
const component = shallowWithIntl(<OptedInBanner onSeenBanner={onLinkClick} />);

const button = component.findWhere(n => n.type() === EuiButton);

if (!button) {
throw new Error(`Couldn't find any buttons in opt-in notice`);
}

button.simulate('click');

expect(onLinkClick).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.
*/

/* eslint @elastic/eui/href-or-on-click:0 */

import * as React from 'react';
import { EuiButton, EuiLink, EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

interface Props {
onSeenBanner: () => any;
}

/**
* React component for displaying the Telemetry opt-in notice.
*/
export class OptedInBanner extends React.PureComponent<Props> {
onLinkClick = () => {
this.props.onSeenBanner();
return;
};

render() {
return (
<EuiCallOut title="Help us improve the Elastic Stack">
<FormattedMessage
id="telemetry.telemetryOptedInNoticeDescription"
defaultMessage="To learn about how usage data helps us manage and improve our products and services, see our {privacyStatementLink}. To stop collection, {disableLink}."
values={{
privacyStatementLink: (
<EuiLink
onClick={this.onLinkClick}
href="https://www.elastic.co/legal/privacy-statement"
target="_blank"
rel="noopener"
>
<FormattedMessage
id="telemetry.telemetryOptedInPrivacyStatement"
defaultMessage="Privacy Statement"
/>
</EuiLink>
),
disableLink: (
<EuiLink href="#/management/kibana/settings" onClick={this.onLinkClick}>
<FormattedMessage
id="telemetry.telemetryOptedInDisableUsage"
defaultMessage="disable usage data here"
/>
</EuiLink>
),
}}
/>
<EuiSpacer size="s" />
<EuiButton size="s" onClick={this.props.onSeenBanner}>
<FormattedMessage
id="telemetry.telemetryOptedInDismissMessage"
defaultMessage="Dismiss"
/>
</EuiButton>
</EuiCallOut>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import chrome from 'ui/chrome';

import { fetchTelemetry } from '../fetch_telemetry';
import { renderBanner } from './render_banner';
import { renderOptedInBanner } from './render_notice_banner';
import { shouldShowBanner } from './should_show_banner';
import { shouldShowOptInBanner } from './should_show_opt_in_banner';
import { TelemetryOptInProvider, isUnauthenticated } from '../../services';
import { npStart } from 'ui/new_platform';

Expand All @@ -48,12 +50,16 @@ async function asyncInjectBanner($injector) {
return;
}

const $http = $injector.get('$http');

// determine if the banner should be displayed
if (await shouldShowBanner(telemetryOptInProvider, config)) {
const $http = $injector.get('$http');

renderBanner(telemetryOptInProvider, () => fetchTelemetry($http, { unencrypted: true }));
}

if (await shouldShowOptInBanner(telemetryOptInProvider, config)) {
renderOptedInBanner(telemetryOptInProvider, () => fetchTelemetry($http, { unencrypted: true }));
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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 React from 'react';

import { banners } from 'ui/notify';
import { OptedInBanner } from '../../components/opted_in_notice_banner';

/**
* Render the Telemetry Opt-in notice banner.
*
* @param {Object} telemetryOptInProvider The telemetry opt-in provider.
* @param {Object} _banners Banners singleton, which can be overridden for tests.
*/
export function renderOptedInBanner(telemetryOptInProvider, { _banners = banners } = {}) {
const bannerId = _banners.add({
component: (
<OptedInBanner
onSeenBanner={telemetryOptInProvider.setOptInNoticeSeen}
/>
),
priority: 10000
});

telemetryOptInProvider.setOptInBannerNoticeId(bannerId);
}
Loading

0 comments on commit e312175

Please sign in to comment.