Commit 5c3291ce authored by James Cook's avatar James Cook

chromeos: Add sync status icon to OS sync settings

The behavior is similar to settings-sync-account-control. The default
state is a green circle. The appearance changes depending on the type
of error.

Screenshot: https://screenshot.googleplex.com/Vsq6ea1fCc1

Bug: 1057200
Test: added to browser_tests

Change-Id: I4c1d122b7faa4a31addb8ae354637508a14b3dc0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2135373Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#756329}
parent 40d5da9f
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
<style include="settings-shared"> <style include="settings-shared">
:host { :host {
--shown-avatar-size: 40px; --shown-avatar-size: 40px;
--sync-icon-border-size: 2px;
--sync-icon-size: 16px;
} }
#avatarContainer { #avatarContainer {
...@@ -31,6 +33,45 @@ ...@@ -31,6 +33,45 @@
width: var(--shown-avatar-size); width: var(--shown-avatar-size);
} }
/* Similar to browser settings-sync-account-control styling. */
#syncIconContainer {
align-items: center;
background: var(--google-green-700);
border: var(--sync-icon-border-size) solid white;
border-radius: 50%;
display: flex;
height: var(--sync-icon-size);
position: absolute;
right: -6px;
top: calc(var(--shown-avatar-size) - var(--sync-icon-size) -
var(--sync-icon-border-size));
width: var(--sync-icon-size);
}
:host-context([dir='rtl']) #syncIconContainer {
left: -6px;
right: initial;
}
#syncIconContainer.sync-problem {
background: var(--settings-error-color);
}
#syncIconContainer.sync-paused {
background: var(--google-blue-500);
}
#syncIconContainer.sync-disabled {
background: var(--google-grey-400);
}
#syncIconContainer iron-icon {
fill: white;
height: 12px;
margin: auto;
width: 12px;
}
.settings-box { .settings-box {
border-top: none; border-top: none;
} }
...@@ -56,7 +97,14 @@ ...@@ -56,7 +97,14 @@
<div class="settings-box first two-line"> <div class="settings-box first two-line">
<div id="avatarContainer"> <div id="avatarContainer">
<img id="avatarIcon" alt="" src="[[profileIconUrl]]"> <img id="avatarIcon" alt="" src="[[profileIconUrl]]">
<!-- TODO(jamescook): Sync status overlay icon. --> <div id="syncIconContainer" hidden="[[!osSyncFeatureEnabled]]"
class$="[[getSyncIconStyle_(
syncStatus.hasError, syncStatus.statusAction,
syncStatus.disabled)]]">
<iron-icon icon$="[[getSyncIcon_(
syncStatus.hasError, syncStatus.statusAction,
syncStatus.disabled)]]"></iron-icon>
</div>
</div> </div>
<div class="middle two-line no-min-width"> <div class="middle two-line no-min-width">
<div class="flex text-elide settings-box-text"> <div class="flex text-elide settings-box-text">
......
...@@ -143,6 +143,45 @@ Polymer({ ...@@ -143,6 +143,45 @@ Polymer({
this.profileEmail; this.profileEmail;
}, },
/**
* Returns the CSS class for the sync status icon.
* @return {string}
* @private
*/
getSyncIconStyle_() {
if (this.syncStatus.disabled) {
return 'sync-disabled';
}
if (!this.syncStatus.hasError) {
return 'sync';
}
// Specific error cases below.
if (this.syncStatus.hasUnrecoverableError) {
return 'sync-problem';
}
if (this.syncStatus.statusAction == settings.StatusAction.REAUTHENTICATE) {
return 'sync-paused';
}
return 'sync-problem';
},
/**
* Returns the image to use for the sync status icon. The value must match
* one of iron-icon's settings:(*) icon names.
* @return {string}
* @private
*/
getSyncIcon_() {
switch (this.getSyncIconStyle_()) {
case 'sync-problem':
return 'settings:sync-problem';
case 'sync-paused':
return 'settings:sync-disabled';
default:
return 'cr:sync';
}
},
/** /**
* Handler for when the sync preferences are updated. * Handler for when the sync preferences are updated.
* @private * @private
......
...@@ -60,6 +60,17 @@ function getSyncNothingPrefs() { ...@@ -60,6 +60,17 @@ function getSyncNothingPrefs() {
return getOsSyncPrefs(false); return getOsSyncPrefs(false);
} }
// Returns a SyncStatus representing the default syncing state.
function getDefaultSyncStatus() {
return {
disabled: false,
hasError: false,
hasUnrecoverableError: false,
signedIn: true,
statusAction: settings.StatusAction.NO_ACTION,
};
}
function setupWithFeatureEnabled() { function setupWithFeatureEnabled() {
cr.webUIListenerCallback( cr.webUIListenerCallback(
'os-sync-prefs-changed', /*featureEnabled=*/ true, getSyncAllPrefs()); 'os-sync-prefs-changed', /*featureEnabled=*/ true, getSyncAllPrefs());
...@@ -74,8 +85,9 @@ function setupWithFeatureDisabled() { ...@@ -74,8 +85,9 @@ function setupWithFeatureDisabled() {
} }
suite('OsSyncControlsTest', function() { suite('OsSyncControlsTest', function() {
let syncControls = null;
let browserProxy = null; let browserProxy = null;
let syncControls = null;
let syncIconContainer = null;
setup(function() { setup(function() {
browserProxy = new TestOsSyncBrowserProxy(); browserProxy = new TestOsSyncBrowserProxy();
...@@ -83,11 +95,14 @@ suite('OsSyncControlsTest', function() { ...@@ -83,11 +95,14 @@ suite('OsSyncControlsTest', function() {
PolymerTest.clearBody(); PolymerTest.clearBody();
syncControls = document.createElement('os-sync-controls'); syncControls = document.createElement('os-sync-controls');
syncControls.syncStatus = {hasError: false}; syncControls.syncStatus = getDefaultSyncStatus();
syncControls.profileName = 'John Cena'; syncControls.profileName = 'John Cena';
syncControls.profileEmail = 'john.cena@gmail.com'; syncControls.profileEmail = 'john.cena@gmail.com';
syncControls.profileIconUrl = 'data:image/png;base64,abc123'; syncControls.profileIconUrl = 'data:image/png;base64,abc123';
document.body.appendChild(syncControls); document.body.appendChild(syncControls);
// Alias to help with line wrapping in test cases.
syncIconContainer = syncControls.$.syncIconContainer;
}); });
teardown(function() { teardown(function() {
...@@ -104,6 +119,47 @@ suite('OsSyncControlsTest', function() { ...@@ -104,6 +119,47 @@ suite('OsSyncControlsTest', function() {
assertEquals('data:image/png;base64,abc123', syncControls.$.avatarIcon.src); assertEquals('data:image/png;base64,abc123', syncControls.$.avatarIcon.src);
}); });
test('Status icon is visible with feature enabled', function() {
setupWithFeatureEnabled();
assertFalse(syncControls.$.syncIconContainer.hidden);
});
test('Status icon is hidden with feature disabled', function() {
setupWithFeatureDisabled();
assertTrue(syncControls.$.syncIconContainer.hidden);
});
test('Status icon with error', function() {
setupWithFeatureEnabled();
const status = getDefaultSyncStatus();
status.hasError = true;
syncControls.syncStatus = status;
assertTrue(syncIconContainer.classList.contains('sync-problem'));
assertTrue(!!syncControls.$$('[icon="settings:sync-problem"]'));
});
test('Status icon with sync paused for reauthentication', function() {
setupWithFeatureEnabled();
const status = getDefaultSyncStatus();
status.hasError = true;
status.statusAction = settings.StatusAction.REAUTHENTICATE;
syncControls.syncStatus = status;
assertTrue(syncIconContainer.classList.contains('sync-paused'));
assertTrue(!!syncControls.$$('[icon="settings:sync-disabled"]'));
});
test('Status icon with sync disabled', function() {
setupWithFeatureEnabled();
const status = getDefaultSyncStatus();
status.disabled = true;
syncControls.syncStatus = status;
assertTrue(syncIconContainer.classList.contains('sync-disabled'));
assertTrue(!!syncControls.$$('[icon="cr:sync"]'));
});
test('Account name and email with feature enabled', function() { test('Account name and email with feature enabled', function() {
setupWithFeatureEnabled(); setupWithFeatureEnabled();
assertEquals('John Cena', syncControls.$.accountTitle.textContent.trim()); assertEquals('John Cena', syncControls.$.accountTitle.textContent.trim());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment