Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dashboard: Telemetry Settings Opt-In Checkbox #4330

Merged
merged 8 commits into from
Sep 7, 2020
5 changes: 4 additions & 1 deletion assets/src/dashboard/app/api/apiProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ export const ApiContext = createContext({ state: {}, actions: {} });
export default function ApiProvider({ children }) {
const { api, editStoryURL, assetsURL } = useConfig();

const { users, api: usersApi } = useUsersApi(dataAdapter, {
const { users, currentUser, api: usersApi } = useUsersApi(dataAdapter, {
userApi: api.users,
currentUserApi: api.currentUser,
});

const { templates, api: templateApi } = useTemplateApi(dataAdapter, {
Expand Down Expand Up @@ -70,6 +71,7 @@ export default function ApiProvider({ children }) {
stories,
templates,
users,
currentUser,
},
actions: {
mediaApi,
Expand All @@ -86,6 +88,7 @@ export default function ApiProvider({ children }) {
stories,
templates,
users,
currentUser,
mediaApi,
settingsApi,
storyApi,
Expand Down
42 changes: 39 additions & 3 deletions assets/src/dashboard/app/api/useUserApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import { USERS_PER_REQUEST } from '../../constants';
import groupBy from '../../utils/groupBy';
import fetchAllFromTotalPages from './fetchAllFromPages';

export default function useUserApi(dataAdapter, { userApi }) {
export default function useUserApi(dataAdapter, { userApi, currentUserApi }) {
const [users, setUsers] = useState({});
const [currentUser, setCurrentUser] = useState({});
const [isUpdating, setIsUpdating] = useState(false);
const fetchUsers = useCallback(async () => {
try {
const response = await dataAdapter.get(
Expand Down Expand Up @@ -58,15 +60,49 @@ export default function useUserApi(dataAdapter, { userApi }) {
}
}, [dataAdapter, userApi]);

const fetchCurrentUser = useCallback(async () => {
try {
setCurrentUser(await dataAdapter.get(currentUserApi));
} catch (e) {
setCurrentUser({});
}
}, [dataAdapter, currentUserApi]);

const toggleWebStoriesTrackingOptIn = useCallback(async () => {
setIsUpdating(true);
try {
setCurrentUser(
await dataAdapter.post(currentUserApi, {
data: {
meta: {
web_stories_tracking_optin: !currentUser.meta
.web_stories_tracking_optin,
},
},
})
);
} finally {
setIsUpdating(false);
}
}, [dataAdapter, currentUser, currentUserApi]);

useEffect(() => {
fetchUsers();
}, [fetchUsers]);

return useMemo(
() => ({
api: { fetchUsers },
api: { fetchUsers, fetchCurrentUser, toggleWebStoriesTrackingOptIn },
users,
currentUser: { data: currentUser, isUpdating },
}),
[fetchUsers, users]
[
fetchUsers,
fetchCurrentUser,
toggleWebStoriesTrackingOptIn,
users,
currentUser,
isUpdating,
]
);
}
5 changes: 5 additions & 0 deletions assets/src/dashboard/app/views/editorSettings/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ export const FinePrintHelperText = styled.p`
color: ${({ theme }) => theme.colors.gray200};
`;

export const FormLabel = styled.span`
${TypographyPresets.ExtraSmall};
color: ${({ theme }) => theme.colors.gray400};
`;

export const Error = styled.p`
${TypographyPresets.ExtraSmall};
padding-bottom: 10px;
Expand Down
19 changes: 18 additions & 1 deletion assets/src/dashboard/app/views/editorSettings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ import { PageHeading } from '../shared';
import GoogleAnalyticsSettings from './googleAnalytics';
import { Main, Wrapper } from './components';
import PublisherLogoSettings from './publisherLogo';
import TelemetrySettings from './telemetry';

const ACTIVE_DIALOG_REMOVE_LOGO = 'REMOVE_LOGO';

function EditorSettings() {
const {
currentUser,
fetchCurrentUser,
fetchSettings,
updateSettings,
toggleWebStoriesTrackingOptIn,
googleAnalyticsId,
fetchMediaById,
uploadMedia,
Expand All @@ -55,6 +59,7 @@ function EditorSettings() {
actions: {
settingsApi: { fetchSettings, updateSettings },
mediaApi: { fetchMediaById, uploadMedia },
usersApi: { fetchCurrentUser, toggleWebStoriesTrackingOptIn },
},
state: {
settings: {
Expand All @@ -63,6 +68,7 @@ function EditorSettings() {
publisherLogoIds,
},
media: { isLoading: isMediaLoading, mediaById, newlyCreatedMediaIds },
currentUser,
},
}) => ({
fetchSettings,
Expand All @@ -75,6 +81,9 @@ function EditorSettings() {
mediaById,
newlyCreatedMediaIds,
publisherLogoIds,
fetchCurrentUser,
toggleWebStoriesTrackingOptIn,
currentUser,
})
);

Expand All @@ -99,7 +108,8 @@ function EditorSettings() {

useEffect(() => {
fetchSettings();
}, [fetchSettings]);
fetchCurrentUser();
}, [fetchCurrentUser, fetchSettings]);

useEffect(() => {
if (newlyCreatedMediaIds.length > 0) {
Expand Down Expand Up @@ -201,6 +211,13 @@ function EditorSettings() {
isLoading={isMediaLoading}
uploadError={mediaError}
/>
<TelemetrySettings
disabled={currentUser.isUpdating}
onCheckboxSelected={toggleWebStoriesTrackingOptIn}
selected={Boolean(
currentUser.data.meta?.web_stories_tracking_optin
)}
/>
</Main>
</Layout.Scrollable>
</Wrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,28 @@ describe('Settings View', () => {

expect(UpdatedPublisherLogos.length).toBe(initialPublisherLogosLength - 1);
});

it('should render the telemetry settings checkbox', async () => {
const settingsView = await fixture.screen.getByTestId('editor-settings');

const TelemetrySettingsCheckbox = within(settingsView).queryAllByTestId(
/^telemetry-settings-checkbox/
);

expect(TelemetrySettingsCheckbox).toBeTruthy();
});

it('should toggle the value and call the API provider when the tracking opt in box is clicked', async () => {
const settingsView = await fixture.screen.getByTestId('editor-settings');

const TelemetrySettingsCheckbox = within(settingsView).queryAllByTestId(
/^telemetry-settings-checkbox/
);

expect(TelemetrySettingsCheckbox[0].checked).toBeTrue();

await fixture.events.click(TelemetrySettingsCheckbox[0]);

expect(TelemetrySettingsCheckbox[0].checked).toBeFalse();
});
});
95 changes: 95 additions & 0 deletions assets/src/dashboard/app/views/editorSettings/telemetry/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2020 Google LLC
*
* Licensed 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
*
* https://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.
*/

/**
* External dependencies
*/
import * as React from 'react';
import propTypes from 'prop-types';
import styled from 'styled-components';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { SettingForm, SettingHeading, FormLabel } from '../components';

const CheckBox = styled.input.attrs({
type: 'checkbox',
id: 'telemetry-opt-in',
})`
height: 18px;
width: 18px;
margin: 0 12px 0 0;
flex: 1 0 18px;
`;

const Label = styled.label.attrs({ htmlFor: 'telemetry-opt-in' })`
display: flex;
justify-content: flex-start;
`;

export default function TelemetrySettings({
selected,
onCheckboxSelected,
disabled,
}) {
const checked = Boolean(selected);
return (
<SettingForm>
<div>
<SettingHeading>
{__('Data Sharing Opt-in', 'web-stories')}
</SettingHeading>
</div>
<div>
<Label>
<CheckBox
data-testid="telemetry-settings-checkbox"
disabled={disabled}
onChange={onCheckboxSelected}
checked={checked}
/>
<FormLabel aria-checked={checked}>
{__(
'Help us improve the Web Stories for WordPress plugin by allowing tracking of usage stats. All data are treated in accordance with Google Privacy Policy.',
'web-stories'
)}
&nbsp;
<a
href={__('https://policies.google.com/privacy', 'web-stories')}
rel="noreferrer"
target="_blank"
>
{__('Learn more', 'web-stories')}
{'.'}
</a>
</FormLabel>
</Label>
</div>
</SettingForm>
);
}

TelemetrySettings.propTypes = {
disabled: propTypes.bool.isRequired,
selected: propTypes.bool.isRequired,
onCheckboxSelected: propTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2020 Google LLC
*
* Licensed 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
*
* https://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.
*/
/**
* External dependencies
*/
import { action } from '@storybook/addon-actions';
import { boolean } from '@storybook/addon-knobs';

/**
* Internal dependencies
*/
import TelemetrySettings from '../';

export default {
title: 'Dashboard/Views/EditorSettings/Telemetry',
component: TelemetrySettings,
};

export const _default = () => {
return (
<TelemetrySettings
disabled={boolean('disabled', true)}
selected={boolean('userOptIn', true)}
onCheckboxSelected={action('onCheckboxSelected fired')}
/>
);
};
Loading