diff --git a/ghost/admin/app/components/gh-theme-table.js b/ghost/admin/app/components/gh-theme-table.js
index 67eeb947bb..29ea542c8d 100644
--- a/ghost/admin/app/components/gh-theme-table.js
+++ b/ghost/admin/app/components/gh-theme-table.js
@@ -4,21 +4,58 @@ import computed from 'ember-computed';
export default Component.extend({
availableThemes: null,
- activeTheme: null,
- themes: computed('availableThemes', 'activeTheme', function () {
- return this.get('availableThemes').map((t) => {
+ themes: computed('availableThemes', function () {
+ let themes = this.get('availableThemes').map((t) => {
let theme = {};
theme.name = t.name;
theme.label = t.package ? `${t.package.name} - ${t.package.version}` : t.name;
theme.package = t.package;
theme.active = !!t.active;
- theme.isDefault = t.name === 'casper';
- theme.isDeletable = !theme.active && !theme.isDefault;
+ theme.isDeletable = !theme.active;
return theme;
- }).sortBy('label');
+ });
+ let duplicateThemes = [];
+
+ themes.forEach((theme) => {
+ let duplicateLabels = themes.filterBy('label', theme.label);
+
+ if (duplicateLabels.length > 1) {
+ duplicateThemes.pushObject(theme);
+ }
+ });
+
+ duplicateThemes.forEach((theme) => {
+ if (theme.name !== 'casper') {
+ theme.label = `${theme.label} (${theme.name})`;
+ }
+ });
+
+ // "(default)" needs to be added to casper manually as it's always
+ // displayed and would mess up the duplicate checking if added earlier
+ let casper = themes.findBy('name', 'casper');
+ if (casper) {
+ casper.label = `${casper.label} (default)`;
+ casper.isDefault = true;
+ casper.isDeletable = false;
+ }
+
+ // sorting manually because .sortBy('label') has a different sorting
+ // algorithm to [...strings].sort()
+ return themes.sort((themeA, themeB) => {
+ let a = themeA.label.toLowerCase();
+ let b = themeB.label.toLowerCase();
+
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ return 0;
+ });
}).readOnly()
});
diff --git a/ghost/admin/app/templates/components/gh-theme-table.hbs b/ghost/admin/app/templates/components/gh-theme-table.hbs
index ed0205050f..21308aca0c 100644
--- a/ghost/admin/app/templates/components/gh-theme-table.hbs
+++ b/ghost/admin/app/templates/components/gh-theme-table.hbs
@@ -3,7 +3,7 @@
{{#each themes as |theme|}}
- {{theme.label}} {{#if theme.isDefault}}(default){{/if}}
+ {{theme.label}}
{{#if theme.isDeletable}}
diff --git a/ghost/admin/app/templates/settings/general.hbs b/ghost/admin/app/templates/settings/general.hbs
index d08c3c0db2..3f75d65307 100644
--- a/ghost/admin/app/templates/settings/general.hbs
+++ b/ghost/admin/app/templates/settings/general.hbs
@@ -125,7 +125,6 @@
{{gh-theme-table
availableThemes=model.availableThemes
- activeTheme=model.activeTheme
activateTheme=(action "setTheme")
downloadTheme=(action "downloadTheme")
deleteTheme=(action "deleteTheme")}}
diff --git a/ghost/admin/tests/integration/components/gh-theme-table-test.js b/ghost/admin/tests/integration/components/gh-theme-table-test.js
index c8bc872091..f2ce483de8 100644
--- a/ghost/admin/tests/integration/components/gh-theme-table-test.js
+++ b/ghost/admin/tests/integration/components/gh-theme-table-test.js
@@ -23,12 +23,10 @@ describeComponent(
{name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
{name: 'foo'}
]);
- this.set('activeTheme', 'Daring');
this.set('actionHandler', sinon.spy());
this.render(hbs`{{gh-theme-table
availableThemes=availableThemes
- activeTheme=activeTheme
activateTheme=(action actionHandler)
downloadTheme=(action actionHandler)
deleteTheme=(action actionHandler)
@@ -95,13 +93,11 @@ describeComponent(
{name: 'Foo', active: true},
{name: 'Bar'}
]);
- this.set('activeTheme', 'Foo');
this.set('deleteAction', deleteAction);
this.set('actionHandler', actionHandler);
this.render(hbs`{{gh-theme-table
availableThemes=availableThemes
- activeTheme=activeTheme
activateTheme=(action actionHandler)
downloadTheme=(action actionHandler)
deleteTheme=(action deleteAction)
@@ -123,13 +119,11 @@ describeComponent(
{name: 'Foo', active: true},
{name: 'Bar'}
]);
- this.set('activeTheme', 'Foo');
this.set('downloadAction', downloadAction);
this.set('actionHandler', actionHandler);
this.render(hbs`{{gh-theme-table
availableThemes=availableThemes
- activeTheme=activeTheme
activateTheme=(action actionHandler)
downloadTheme=(action downloadAction)
deleteTheme=(action actionHandler)
@@ -151,13 +145,11 @@ describeComponent(
{name: 'Foo', active: true},
{name: 'Bar'}
]);
- this.set('activeTheme', 'Foo');
this.set('activateAction', activateAction);
this.set('actionHandler', actionHandler);
this.render(hbs`{{gh-theme-table
availableThemes=availableThemes
- activeTheme=activeTheme
activateTheme=(action activateAction)
downloadTheme=(action actionHandler)
deleteTheme=(action actionHandler)
@@ -170,5 +162,42 @@ describeComponent(
expect(activateAction.calledOnce).to.be.true;
expect(activateAction.firstCall.args[0].name).to.equal('Bar');
});
+
+ it('displays folder names if there are duplicate package names', function () {
+ this.set('availableThemes', [
+ {name: 'daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
+ {name: 'daring-0.1.5', package: {name: 'Daring', version: '0.1.4'}},
+ {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
+ {name: 'another', package: {name: 'Casper', version: '1.3.1'}},
+ {name: 'mine', package: {name: 'Casper', version: '1.3.1'}},
+ {name: 'foo'}
+ ]);
+ this.set('actionHandler', sinon.spy());
+
+ this.render(hbs`{{gh-theme-table
+ availableThemes=availableThemes
+ activateTheme=(action actionHandler)
+ downloadTheme=(action actionHandler)
+ deleteTheme=(action actionHandler)
+ }}`);
+
+ let packageNames = this.$('.theme-list-item-body .name').map((i, name) => {
+ return $(name).text().trim();
+ }).toArray();
+
+ console.log(packageNames);
+
+ expect(
+ packageNames,
+ 'themes are ordered by label, folder names shown for duplicates'
+ ).to.deep.equal([
+ 'Casper - 1.3.1 (another)',
+ 'Casper - 1.3.1 (default)',
+ 'Casper - 1.3.1 (mine)',
+ 'Daring - 0.1.4 (daring)',
+ 'Daring - 0.1.4 (daring-0.1.5)',
+ 'foo'
+ ]);
+ });
}
);