diff --git a/ghost/admin/app/components/gh-navitem-url-input.js b/ghost/admin/app/components/gh-navitem-url-input.js
index 502aedd19d..97278bbcc4 100644
--- a/ghost/admin/app/components/gh-navitem-url-input.js
+++ b/ghost/admin/app/components/gh-navitem-url-input.js
@@ -26,8 +26,8 @@ export default TextField.extend({
return this.get('baseUrl') === this.get('value');
}),
- fakePlaceholder: computed('isBaseUrl', 'hasFocus', function () {
- return this.get('isBaseUrl') && this.get('last') && !this.get('hasFocus');
+ fakePlaceholder: computed('isBaseUrl', 'hasFocus', 'isNew', function () {
+ return this.get('isBaseUrl') && this.get('isNew') && !this.get('hasFocus');
}),
didReceiveAttrs() {
@@ -72,9 +72,10 @@ export default TextField.extend({
},
keyPress(event) {
+ this.attrs.clearErrors();
+
// enter key
if (event.keyCode === 13) {
- event.preventDefault();
this.notifyUrlChanged();
}
diff --git a/ghost/admin/app/components/gh-navitem.js b/ghost/admin/app/components/gh-navitem.js
index 9f0b3ec283..341ea1a9da 100644
--- a/ghost/admin/app/components/gh-navitem.js
+++ b/ghost/admin/app/components/gh-navitem.js
@@ -1,15 +1,18 @@
import Ember from 'ember';
-import ValidationStateMixin from 'ghost/mixins/validation-state';
+import ValidationState from 'ghost/mixins/validation-state';
+import SortableItem from 'ember-sortable/mixins/sortable-item';
-const {Component, computed} = Ember;
-const {readOnly} = computed;
+const {Component, computed, run} = Ember;
+const {alias, readOnly} = computed;
-export default Component.extend(ValidationStateMixin, {
+export default Component.extend(ValidationState, SortableItem, {
classNames: 'gh-blognav-item',
- classNameBindings: ['errorClass'],
+ classNameBindings: ['errorClass', 'navItem.isNew::gh-blognav-item--sortable'],
- attributeBindings: ['order:data-order'],
- order: readOnly('navItem.order'),
+ new: false,
+ handle: '.gh-blognav-grab',
+
+ model: alias('navItem'),
errors: readOnly('navItem.errors'),
errorClass: computed('hasError', function () {
@@ -20,12 +23,12 @@ export default Component.extend(ValidationStateMixin, {
keyPress(event) {
// enter key
- if (event.keyCode === 13) {
+ if (event.keyCode === 13 && this.get('navItem.isNew')) {
event.preventDefault();
- this.send('addItem');
+ run.scheduleOnce('actions', this, function () {
+ this.send('addItem');
+ });
}
-
- this.get('navItem.errors').clear();
},
actions: {
@@ -39,6 +42,14 @@ export default Component.extend(ValidationStateMixin, {
updateUrl(value) {
this.sendAction('updateUrl', value, this.get('navItem'));
+ },
+
+ clearLabelErrors() {
+ this.get('navItem.errors').remove('label');
+ },
+
+ clearUrlErrors() {
+ this.get('navItem.errors').remove('url');
}
}
});
diff --git a/ghost/admin/app/controllers/settings/navigation.js b/ghost/admin/app/controllers/settings/navigation.js
index 0e7a477f30..4ba1256054 100644
--- a/ghost/admin/app/controllers/settings/navigation.js
+++ b/ghost/admin/app/controllers/settings/navigation.js
@@ -8,8 +8,7 @@ const {
RSVP,
computed,
inject: {service},
- isBlank,
- observer
+ isBlank
} = Ember;
const {Errors} = DS;
const emberA = Ember.A;
@@ -17,7 +16,7 @@ const emberA = Ember.A;
export const NavItem = Ember.Object.extend(ValidationEngine, {
label: '',
url: '',
- last: false,
+ isNew: false,
validationType: 'navItem',
@@ -44,6 +43,8 @@ export default Controller.extend(SettingsSaveMixin, {
config: service(),
notifications: service(),
+ newNavItem: null,
+
blogUrl: computed('config.blogUrl', function () {
let url = this.get('config.blogUrl');
@@ -51,8 +52,7 @@ export default Controller.extend(SettingsSaveMixin, {
}),
navigationItems: computed('model.navigation', function () {
- let lastItem,
- navItems;
+ let navItems;
try {
navItems = JSON.parse(this.get('model.navigation') || [{}]);
@@ -64,38 +64,27 @@ export default Controller.extend(SettingsSaveMixin, {
return NavItem.create(item);
});
- lastItem = navItems.get('lastObject');
- if (!lastItem || lastItem.get('isComplete')) {
- navItems.addObject(NavItem.create({last: true}));
- }
-
return navItems;
}),
- updateLastNavItem: observer('navigationItems.[]', function () {
- let navItems = this.get('navigationItems');
-
- navItems.forEach((item, index, items) => {
- if (index === (items.length - 1)) {
- item.set('last', true);
- } else {
- item.set('last', false);
- }
- });
- }),
+ init() {
+ this._super(...arguments);
+ this.set('newNavItem', NavItem.create({isNew: true}));
+ },
save() {
let navItems = this.get('navigationItems');
+ let newNavItem = this.get('newNavItem');
let notifications = this.get('notifications');
- let navSetting,
- validationPromises;
+ let validationPromises = [];
+ let navSetting;
- validationPromises = navItems.map((item) => {
- if (item.get('last') && item.get('isBlank')) {
- return;
- }
+ if (!newNavItem.get('isBlank')) {
+ validationPromises.pushObject(this.send('addItem'));
+ }
- return item.validate();
+ navItems.map((item) => {
+ validationPromises.pushObject(item.validate());
});
return RSVP.all(validationPromises).then(() => {
@@ -103,10 +92,6 @@ export default Controller.extend(SettingsSaveMixin, {
let label = item.get('label').trim();
let url = item.get('url').trim();
- if (item.get('last') && !item.get('isComplete')) {
- return null;
- }
-
return {label, url};
}).compact();
@@ -124,17 +109,22 @@ export default Controller.extend(SettingsSaveMixin, {
});
},
+ addNewNavItem() {
+ let navItems = this.get('navigationItems');
+ let newNavItem = this.get('newNavItem');
+
+ newNavItem.set('isNew', false);
+ navItems.pushObject(newNavItem);
+ this.set('newNavItem', NavItem.create({isNew: true}));
+ },
+
actions: {
addItem() {
- let navItems = this.get('navigationItems');
- let lastItem = navItems.get('lastObject');
+ let newNavItem = this.get('newNavItem');
- if (lastItem) {
- lastItem.validate().then(() => {
- // Add new blank navItem
- navItems.addObject(NavItem.create({last: true}));
- });
- }
+ return newNavItem.validate().then(() => {
+ this.addNewNavItem();
+ });
},
deleteItem(item) {
@@ -147,12 +137,8 @@ export default Controller.extend(SettingsSaveMixin, {
navItems.removeObject(item);
},
- moveItem(index, newIndex) {
- let navItems = this.get('navigationItems');
- let item = navItems.objectAt(index);
-
- navItems.removeAt(index);
- navItems.insertAt(newIndex, item);
+ reorderItems(navItems) {
+ this.set('navigationItems', navItems);
},
updateUrl(url, navItem) {
@@ -161,6 +147,10 @@ export default Controller.extend(SettingsSaveMixin, {
}
navItem.set('url', url);
+ },
+
+ reset() {
+ this.set('newNavItem', NavItem.create({isNew: true}));
}
}
});
diff --git a/ghost/admin/app/mirage/config.js b/ghost/admin/app/mirage/config.js
index f6c0586e26..825a57b6da 100644
--- a/ghost/admin/app/mirage/config.js
+++ b/ghost/admin/app/mirage/config.js
@@ -159,7 +159,7 @@ export default function () {
});
this.put('/settings/', function (db, request) {
- let newSettings = JSON.parse(request.requestBody);
+ let newSettings = JSON.parse(request.requestBody).settings;
db.settings.remove();
db.settings.insert(newSettings);
diff --git a/ghost/admin/app/routes/settings/navigation.js b/ghost/admin/app/routes/settings/navigation.js
index 48d00a43fa..06875e7bf9 100644
--- a/ghost/admin/app/routes/settings/navigation.js
+++ b/ghost/admin/app/routes/settings/navigation.js
@@ -22,6 +22,11 @@ export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
});
},
+ setupController() {
+ this._super(...arguments);
+ this.get('controller').send('reset');
+ },
+
actions: {
save() {
// since shortcuts are run on the route, we have to signal to the components
diff --git a/ghost/admin/app/styles/layouts/settings.css b/ghost/admin/app/styles/layouts/settings.css
index 3631d998e0..ac031b5d10 100644
--- a/ghost/admin/app/styles/layouts/settings.css
+++ b/ghost/admin/app/styles/layouts/settings.css
@@ -83,15 +83,11 @@
background: color(var(--green) lightness(-10%));
}
-.gh-blognav-item:last-child {
+.gh-blognav-item:not(.gh-blognav-item--sortable) {
padding-left: calc(16px + 20px);
/* icon-grab + nav-item padding) */
}
-.gh-blognav-item:last-child .gh-blognav-grab {
- display: none;
-}
-
/* Remove space between inputs on smaller screens */
@media (max-width: 800px) {
.gh-blognav-label {
diff --git a/ghost/admin/app/templates/components/gh-navitem.hbs b/ghost/admin/app/templates/components/gh-navitem.hbs
index ed48c83447..6ecc4ba421 100644
--- a/ghost/admin/app/templates/components/gh-navitem.hbs
+++ b/ghost/admin/app/templates/components/gh-navitem.hbs
@@ -1,4 +1,4 @@
-{{#unless navItem.last}}
+{{#unless navItem.isNew}}
Reorder
@@ -6,16 +6,16 @@
{{#gh-validation-status-container tagName="span" class="gh-blognav-label" errors=navItem.errors property="label" hasValidated=navItem.hasValidated}}
- {{gh-trim-focus-input focus=navItem.last placeholder="Label" value=navItem.label}}
+ {{gh-trim-focus-input focus=navItem.last placeholder="Label" value=navItem.label keyPress=(action "clearLabelErrors")}}
{{gh-error-message errors=navItem.errors property="label"}}
{{/gh-validation-status-container}}
{{#gh-validation-status-container tagName="span" class="gh-blognav-url" errors=navItem.errors property="url" hasValidated=navItem.hasValidated}}
- {{gh-navitem-url-input baseUrl=baseUrl url=navItem.url last=navItem.last change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=navItem.url isNew=navItem.isNew change="updateUrl" clearErrors=(action "clearUrlErrors")}}
{{gh-error-message errors=navItem.errors property="url"}}
{{/gh-validation-status-container}}
-{{#if navItem.last}}
+{{#if navItem.isNew}}
diff --git a/ghost/admin/app/templates/settings/navigation.hbs b/ghost/admin/app/templates/settings/navigation.hbs
index 37de0b354c..bd8ac16b01 100644
--- a/ghost/admin/app/templates/settings/navigation.hbs
+++ b/ghost/admin/app/templates/settings/navigation.hbs
@@ -1,4 +1,4 @@
-{{#gh-navigation moveItem="moveItem"}}
+
diff --git a/ghost/admin/tests/acceptance/settings/navigation-test.js b/ghost/admin/tests/acceptance/settings/navigation-test.js
index 83a68a889d..1c9c774a92 100644
--- a/ghost/admin/tests/acceptance/settings/navigation-test.js
+++ b/ghost/admin/tests/acceptance/settings/navigation-test.js
@@ -60,12 +60,17 @@ describe('Acceptance: Settings - Navigation', function () {
andThen(function () {
expect(currentPath()).to.equal('settings.navigation');
- // test has expected number of rows
- expect($('.gh-blognav-item').length, 'navigation items count').to.equal(3);
+
+ // fixtures contain two nav items, check for three rows as we
+ // should have one extra that's blank
+ expect(
+ find('.gh-blognav-item').length,
+ 'navigation items count'
+ ).to.equal(3);
});
});
- it('saves settings', function () {
+ it('saves navigation settings', function () {
visit('/settings/navigation');
fillIn('.gh-blognav-label:first input', 'Test');
fillIn('.gh-blognav-url:first input', '/test');
@@ -74,14 +79,47 @@ describe('Acceptance: Settings - Navigation', function () {
click('.btn-blue');
andThen(function () {
- // TODO: Test for successful save here once we have a visual
- // indication. For now we know the save happened because
- // Pretender doesn't complain about an unknown URL
+ let [navSetting] = server.db.settings.where({key: 'navigation'});
+
+ expect(navSetting.value).to.equal('[{"label":"Test","url":"/test/"},{"label":"About","url":"/about"}]');
// don't test against .error directly as it will pick up failed
// tests "pre.error" elements
- expect($('span.error').length, 'error fields count').to.equal(0);
- expect($('.gh-alert').length, 'alerts count').to.equal(0);
+ expect(find('span.error').length, 'error fields count').to.equal(0);
+ expect(find('.gh-alert').length, 'alerts count').to.equal(0);
+ expect(find('.response:visible').length, 'validation errors count')
+ .to.equal(0);
+ });
+ });
+
+ it('validates new item correctly on save', function () {
+ visit('/settings/navigation');
+
+ click('.btn-blue');
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-item').length,
+ 'number of nav items after saving with blank new item'
+ ).to.equal(3);
+ });
+
+ fillIn('.gh-blognav-label:last input', 'Test');
+ fillIn('.gh-blognav-url:last input', 'http://invalid domain/');
+ triggerEvent('.gh-blognav-url:last input', 'blur');
+
+ click('.btn-blue');
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-item').length,
+ 'number of nav items after saving with invalid new item'
+ ).to.equal(3);
+
+ expect(
+ find('.gh-blognav-item:last .response:visible').length,
+ 'number of invalid fields in new item'
+ ).to.equal(1);
});
});
@@ -91,14 +129,105 @@ describe('Acceptance: Settings - Navigation', function () {
triggerEvent('.gh-blognav-label:first input', 'blur');
andThen(function () {
- expect($('.gh-blognav-label:first input').val()).to.equal('Test');
+ expect(find('.gh-blognav-label:first input').val()).to.equal('Test');
});
visit('/settings/code-injection');
visit('/settings/navigation');
andThen(function () {
- expect($('.gh-blognav-label:first input').val()).to.equal('Home');
+ expect(find('.gh-blognav-label:first input').val()).to.equal('Home');
+ });
+ });
+
+ it('can add and remove items', function (done) {
+ visit('/settings/navigation');
+
+ click('.gh-blognav-add');
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-label:last .response').is(':visible'),
+ 'blank label has validation error'
+ ).to.be.true;
+
+ expect(
+ find('.gh-blognav-url:last .response').is(':visible'),
+ 'blank url has validation error'
+ ).to.be.true;
+ });
+
+ fillIn('.gh-blognav-label:last input', 'New');
+ triggerEvent('.gh-blognav-label:last input', 'keypress', {});
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-label:last .response').is(':visible'),
+ 'label validation is visible after typing'
+ ).to.be.false;
+
+ expect(
+ find('.gh-blognav-url:last .response').is(':visible'),
+ 'blank url still has validation error'
+ ).to.be.true;
+ });
+
+ fillIn('.gh-blognav-url:last input', '/new');
+ triggerEvent('.gh-blognav-url:last input', 'keypress', {});
+ triggerEvent('.gh-blognav-url:last input', 'blur');
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-url:last .response').is(':visible'),
+ 'url validation is visible after typing'
+ ).to.be.false;
+
+ expect(
+ find('.gh-blognav-url:last input').val()
+ ).to.equal(`${window.location.protocol}//${window.location.host}/new/`);
+ });
+
+ click('.gh-blognav-add');
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-item').length,
+ 'number of nav items after successful add'
+ ).to.equal(4);
+
+ expect(
+ find('.gh-blognav-label:last input').val(),
+ 'new item label value after successful add'
+ ).to.be.blank;
+
+ expect(
+ find('.gh-blognav-url:last input').val(),
+ 'new item url value after successful add'
+ ).to.equal(`${window.location.protocol}//${window.location.host}/`);
+
+ expect(
+ find('.gh-blognav-item .response:visible').length,
+ 'number or validation errors shown after successful add'
+ ).to.equal(0);
+ });
+
+ click('.gh-blognav-item:first .gh-blognav-delete');
+
+ andThen(function () {
+ expect(
+ find('.gh-blognav-item').length,
+ 'number of nav items after successful remove'
+ ).to.equal(3);
+ });
+
+ click('.btn-blue');
+
+ andThen(function () {
+ let [navSetting] = server.db.settings.where({key: 'navigation'});
+
+ expect(navSetting.value).to.equal('[{"label":"About","url":"/about"},{"label":"New","url":"/new/"}]');
+
+ done();
});
});
});
diff --git a/ghost/admin/tests/integration/components/gh-navitem-test.js b/ghost/admin/tests/integration/components/gh-navitem-test.js
index 55dcb4ed93..79986422f9 100644
--- a/ghost/admin/tests/integration/components/gh-navitem-test.js
+++ b/ghost/admin/tests/integration/components/gh-navitem-test.js
@@ -35,8 +35,8 @@ describeComponent(
expect($item.find('.response:visible').length).to.equal(0);
});
- it('doesn\'t show drag handle for last item', function () {
- this.set('navItem', NavItem.create({label: 'Test', url: '/url', last: true}));
+ it('doesn\'t show drag handle for new items', function () {
+ this.set('navItem', NavItem.create({label: 'Test', url: '/url', isNew: true}));
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
let $item = this.$('.gh-blognav-item');
@@ -44,8 +44,8 @@ describeComponent(
expect($item.find('.gh-blognav-grab').length).to.equal(0);
});
- it('shows add button for last item', function () {
- this.set('navItem', NavItem.create({label: 'Test', url: '/url', last: true}));
+ it('shows add button for new items', function () {
+ this.set('navItem', NavItem.create({label: 'Test', url: '/url', isNew: true}));
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
let $item = this.$('.gh-blognav-item');
@@ -70,7 +70,7 @@ describeComponent(
});
it('triggers add action', function () {
- this.set('navItem', NavItem.create({label: 'Test', url: '/url', last: true}));
+ this.set('navItem', NavItem.create({label: 'Test', url: '/url', isNew: true}));
let addActionCallCount = 0;
this.on('add', () => {
diff --git a/ghost/admin/tests/integration/components/gh-navitem-url-input-test.js b/ghost/admin/tests/integration/components/gh-navitem-url-input-test.js
index cdf10932b7..e85de750c6 100644
--- a/ghost/admin/tests/integration/components/gh-navitem-url-input-test.js
+++ b/ghost/admin/tests/integration/components/gh-navitem-url-input-test.js
@@ -23,12 +23,15 @@ describeComponent(
// set defaults
this.set('baseUrl', currentUrl);
this.set('url', '');
- this.set('isLast', false);
+ this.set('isNew', false);
+ this.on('clearErrors', function () {
+ return null;
+ });
});
it('renders correctly with blank url', function () {
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -40,7 +43,7 @@ describeComponent(
it('renders correctly with relative urls', function () {
this.set('url', '/about');
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -53,7 +56,7 @@ describeComponent(
it('renders correctly with absolute urls', function () {
this.set('url', 'https://example.com:2368/#test');
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -74,7 +77,7 @@ describeComponent(
it('deletes base URL on backspace', function () {
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -90,7 +93,7 @@ describeComponent(
it('deletes base URL on delete', function () {
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -109,7 +112,7 @@ describeComponent(
return null;
});
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -128,7 +131,7 @@ describeComponent(
return null;
});
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -153,7 +156,7 @@ describeComponent(
return null;
});
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -175,7 +178,7 @@ describeComponent(
return null;
});
this.render(hbs`
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -187,9 +190,9 @@ describeComponent(
});
it('toggles .fake-placeholder on focus', function () {
- this.set('isLast', true);
+ this.set('isNew', true);
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -208,7 +211,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -224,7 +227,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -245,7 +248,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -268,7 +271,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -301,7 +304,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -330,7 +333,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -349,7 +352,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -375,7 +378,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -403,7 +406,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -436,7 +439,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
@@ -463,7 +466,7 @@ describeComponent(
});
this.render(hbs `
- {{gh-navitem-url-input baseUrl=baseUrl url=url last=isLast change="updateUrl"}}
+ {{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew change="updateUrl" clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
diff --git a/ghost/admin/tests/unit/controllers/settings/navigation-test.js b/ghost/admin/tests/unit/controllers/settings/navigation-test.js
index 62bc90814e..59d5499fb7 100644
--- a/ghost/admin/tests/unit/controllers/settings/navigation-test.js
+++ b/ghost/admin/tests/unit/controllers/settings/navigation-test.js
@@ -31,55 +31,30 @@ describeModule(
expect(ctrl.get('blogUrl')).to.equal('http://localhost:2368/blog/');
});
+ it('init: creates a new navigation item', function () {
+ let ctrl = this.subject();
+
+ run(() => {
+ expect(ctrl.get('newNavItem')).to.exist;
+ expect(ctrl.get('newNavItem.isNew')).to.be.true;
+ });
+ });
+
+ it('blogUrl: captures config and ensures trailing slash', function () {
+ let ctrl = this.subject();
+ ctrl.set('config.blogUrl', 'http://localhost:2368/blog');
+ expect(ctrl.get('blogUrl')).to.equal('http://localhost:2368/blog/');
+ });
+
it('navigationItems: generates list of NavItems', function () {
let ctrl = this.subject();
- let lastItem;
run(() => {
ctrl.set('model', Ember.Object.create({navigation: navSettingJSON}));
- expect(ctrl.get('navigationItems.length')).to.equal(9);
+ expect(ctrl.get('navigationItems.length')).to.equal(8);
expect(ctrl.get('navigationItems.firstObject.label')).to.equal('Home');
expect(ctrl.get('navigationItems.firstObject.url')).to.equal('/');
- expect(ctrl.get('navigationItems.firstObject.last')).to.be.false;
-
- // adds a blank item as last one is complete
- lastItem = ctrl.get('navigationItems.lastObject');
- expect(lastItem.get('label')).to.equal('');
- expect(lastItem.get('url')).to.equal('');
- expect(lastItem.get('last')).to.be.true;
- });
- });
-
- it('navigationItems: adds blank item if navigation setting is empty', function () {
- let ctrl = this.subject();
- let lastItem;
-
- run(() => {
- ctrl.set('model', Ember.Object.create({navigation: null}));
- expect(ctrl.get('navigationItems.length')).to.equal(1);
-
- lastItem = ctrl.get('navigationItems.lastObject');
- expect(lastItem.get('label')).to.equal('');
- expect(lastItem.get('url')).to.equal('');
- });
- });
-
- it('updateLastNavItem: correctly sets "last" properties', function () {
- let ctrl = this.subject();
- let item1,
- item2;
-
- run(() => {
- ctrl.set('model', Ember.Object.create({navigation: navSettingJSON}));
-
- item1 = ctrl.get('navigationItems.lastObject');
- expect(item1.get('last')).to.be.true;
-
- ctrl.get('navigationItems').addObject(Ember.Object.create({label: 'Test', url: '/test'}));
-
- item2 = ctrl.get('navigationItems.lastObject');
- expect(item2.get('last')).to.be.true;
- expect(item1.get('last')).to.be.false;
+ expect(ctrl.get('navigationItems.firstObject.isNew')).to.be.false;
});
});
@@ -168,15 +143,20 @@ describeModule(
expect(ctrl.get('navigationItems.length')).to.equal(1);
+ ctrl.set('newNavItem.label', 'New');
+ ctrl.set('newNavItem.url', '/new');
+
run(() => {
ctrl.send('addItem');
});
expect(ctrl.get('navigationItems.length')).to.equal(2);
- expect(ctrl.get('navigationItems.firstObject.last')).to.be.false;
- expect(ctrl.get('navigationItems.lastObject.label')).to.equal('');
- expect(ctrl.get('navigationItems.lastObject.url')).to.equal('');
- expect(ctrl.get('navigationItems.lastObject.last')).to.be.true;
+ expect(ctrl.get('navigationItems.lastObject.label')).to.equal('New');
+ expect(ctrl.get('navigationItems.lastObject.url')).to.equal('/new');
+ expect(ctrl.get('navigationItems.lastObject.isNew')).to.be.false;
+ expect(ctrl.get('newNavItem.label')).to.be.blank;
+ expect(ctrl.get('newNavItem.url')).to.be.blank;
+ expect(ctrl.get('newNavItem.isNew')).to.be.true;
});
it('action - addItem: doesn\'t insert new item if last object is incomplete', function () {
@@ -205,7 +185,7 @@ describeModule(
});
});
- it('action - moveItem: updates navigationItems list', function () {
+ it('action - reorderItems: updates navigationItems list', function () {
let ctrl = this.subject();
let navItems = [
NavItem.create({label: 'First', url: '/first'}),
@@ -215,7 +195,7 @@ describeModule(
run(() => {
ctrl.set('navigationItems', navItems);
expect(ctrl.get('navigationItems').mapBy('label')).to.deep.equal(['First', 'Second']);
- ctrl.send('moveItem', 1, 0);
+ ctrl.send('reorderItems', navItems.reverseObjects());
expect(ctrl.get('navigationItems').mapBy('label')).to.deep.equal(['Second', 'First']);
});
});