Skip to content

Commit

Permalink
Handle manual timezone override
Browse files Browse the repository at this point in the history
refs TryGhost/Ghost#7142, TryGhost/Ghost#7143
- moves timezone selection template and logic into a component
- detect if the current `activeTimezone` is not in our pre-defined list of timezones, if it isn't:
  - add a line indicating that there has been a manual override with the current `activeTimezone` value
  - add a blank option to the timezone select list
  • Loading branch information
kevinansfield committed Jul 26, 2016
1 parent 7fc02c1 commit ad7fb3f
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 31 deletions.
61 changes: 61 additions & 0 deletions app/components/gh-timezone-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Component from 'ember-component';
import computed, {mapBy} from 'ember-computed';
import injectService from 'ember-service/inject';
import {invokeAction} from 'ember-invoke-action';

export default Component.extend({
classNames: ['form-group', 'for-select'],

activeTimezone: null,
availableTimezones: null,

clock: injectService(),

availableTimezoneNames: mapBy('availableTimezones', 'name'),

hasTimezoneOverride: computed('activeTimezone', 'availableTimezoneNames', function () {
let activeTimezone = this.get('activeTimezone');
let availableTimezoneNames = this.get('availableTimezoneNames');

return !availableTimezoneNames.contains(activeTimezone);
}),

selectedTimezone: computed('activeTimezone', 'availableTimezones', 'hasTimezoneOverride', function () {
let hasTimezoneOverride = this.get('hasTimezoneOverride');
let activeTimezone = this.get('activeTimezone');
let availableTimezones = this.get('availableTimezones');

if (hasTimezoneOverride) {
return {name: '', label: ''};
}

return availableTimezones
.filterBy('name', activeTimezone)
.get('firstObject');
}),

selectableTimezones: computed('availableTimezones', 'hasTimezoneOverride', function () {
let hasTimezoneOverride = this.get('hasTimezoneOverride');
let availableTimezones = this.get('availableTimezones');

if (hasTimezoneOverride) {
return [{name: '', label: ''}, ...availableTimezones];
}

return availableTimezones;
}),

localTime: computed('hasTimezoneOverride', 'activeTimezone', 'selectedTimezone', 'clock.second', function () {
let hasTimezoneOverride = this.get('hasTimezoneOverride');
let timezone = hasTimezoneOverride ? this.get('activeTimezone') : this.get('selectedTimezone.name');

this.get('clock.second');
return timezone ? moment().tz(timezone).format('HH:mm:ss') : moment().utc().format('HH:mm:ss');
}),

actions: {
setTimezone(timezone) {
invokeAction(this, 'update', timezone);
}
}
});
16 changes: 0 additions & 16 deletions app/controllers/settings/general.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export default Controller.extend(SettingsSaveMixin, {
config: injectService(),
_scratchFacebook: null,
_scratchTwitter: null,
clock: injectService(),

selectedTheme: computed('model.activeTheme', 'themes', function () {
let activeTheme = this.get('model.activeTheme');
Expand All @@ -33,15 +32,6 @@ export default Controller.extend(SettingsSaveMixin, {
return selectedTheme;
}),

selectedTimezone: computed('model.activeTimezone', 'availableTimezones', function () {
let activeTimezone = this.get('model.activeTimezone');
let availableTimezones = this.get('availableTimezones');

return availableTimezones
.filterBy('name', activeTimezone)
.get('firstObject');
}),

logoImageSource: computed('model.logo', function () {
return this.get('model.logo') || '';
}),
Expand All @@ -50,12 +40,6 @@ export default Controller.extend(SettingsSaveMixin, {
return this.get('model.cover') || '';
}),

localTime: computed('selectedTimezone', 'clock.second', function () {
let timezone = this.get('selectedTimezone.name');
this.get('clock.second');
return timezone ? moment().tz(timezone).format('HH:mm:ss') : moment().utc().format('HH:mm:ss');
}),

isDatedPermalinks: computed('model.permalinks', {
set(key, value) {
this.set('model.permalinks', value ? '/:year/:month/:day/:slug/' : '/:slug/');
Expand Down
16 changes: 16 additions & 0 deletions app/templates/components/gh-timezone-select.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<label for="activeTimezone">Timezone</label>
<span class="gh-select" data-select-text="{{selectedTimezone.label}}" tabindex="0">
{{gh-select-native
id="activeTimezone"
name="general[activeTimezone]"
content=selectableTimezones
optionValuePath="name"
optionLabelPath="label"
selection=selectedTimezone
action="setTimezone"
}}
</span>
{{#if hasTimezoneOverride}}
<p>Your timezone has been manually set to {{activeTimezone}}.</p>
{{/if}}
<p>The local time here is currently {{localTime}}</p>
19 changes: 4 additions & 15 deletions app/templates/settings/general.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,10 @@
{{/gh-form-group}}
</div>

<div class="form-group for-select">
<label for="activeTimezone">Timezone</label>
<span class="gh-select" data-select-text="{{selectedTimezone.label}}" tabindex="0">
{{gh-select-native
id="activeTimezone"
name="general[activeTimezone]"
content=availableTimezones
optionValuePath="name"
optionLabelPath="label"
selection=selectedTimezone
action="setTimezone"
}}
</span>
<p>The local time here is currently {{localTime}}</p>
</div>
{{gh-timezone-select
activeTimezone=model.activeTimezone
availableTimezones=availableTimezones
update=(action "setTimezone")}}

<div class="form-group for-checkbox">
<label for="isPrivate">Make this blog private</label>
Expand Down
78 changes: 78 additions & 0 deletions tests/integration/components/gh-timezone-select-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* jshint expr:true */
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import run from 'ember-runloop';
import wait from 'ember-test-helpers/wait';
import sinon from 'sinon';

describeComponent(
'gh-timezone-select',
'Integration: Component: gh-timezone-select',
{
integration: true
},
function() {
beforeEach(function () {
this.set('availableTimezones', [
{name: 'Pacific/Pago_Pago', label: '(GMT -11:00) Midway Island, Samoa'},
{name: 'Etc/UTC', label: '(GMT) UTC'},
{name: 'Pacific/Kwajalein', label: '(GMT +12:00) International Date Line West'}
]);
this.set('activeTimezone', 'Etc/UTC');
});

it('renders', function () {
this.render(hbs`{{gh-timezone-select
availableTimezones=availableTimezones
activeTimezone=activeTimezone}}`);

expect(this.$(), 'top-level elements').to.have.length(1);
expect(this.$('option'), 'number of options').to.have.length(3);
expect(this.$('select').val(), 'selected option value').to.equal('Etc/UTC');
});

it('handles an unknown timezone', function () {
this.set('activeTimezone', 'Europe/London');

this.render(hbs`{{gh-timezone-select
availableTimezones=availableTimezones
activeTimezone=activeTimezone}}`);

// we have an additional blank option at the top
expect(this.$('option'), 'number of options').to.have.length(4);
// blank option is selected
expect(this.$('select').val(), 'selected option value').to.equal('');
// we indicate the manual override
expect(this.$('p').text()).to.match(/Your timezone has been manually set to Europe\/London/);
});

it('triggers update action on change', function (done) {
let update = sinon.spy();
this.set('update', update);

this.render(hbs`{{gh-timezone-select
availableTimezones=availableTimezones
activeTimezone=activeTimezone
update=(action update)}}`);

run(() => {
this.$('select').val('Pacific/Pago_Pago').change();
});

wait().then(() => {
expect(update.calledOnce, 'update was called once').to.be.true;
expect(update.firstCall.args[0].name, 'update was passed new timezone')
.to.equal('Pacific/Pago_Pago');
done();
});
});

// TODO: mock clock service, fake the time, test we have the correct
// local time and it changes alongside selection changes
it('renders local time');
}
);

0 comments on commit ad7fb3f

Please sign in to comment.