This repository has been archived by the owner on Nov 28, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refs TryGhost/Ghost#9060 - add `{{gh-psm-template-select}}` component - allows selection of a custom template for a post if the active theme has custom templates - loads themes on render, only hitting the server if not already in the store - disables select if post slug matches a `post-*.hbs` or `page-*.hbs` template - adds `customTemplate` attr to `Post` model - adds `templates` attr to `Theme` model with CPs to pull out custom vs post/page override templates - add `.gh-select.disabled` styles to make disabled selects look visually disabled
- Loading branch information
1 parent
dd307ee
commit 68442c5
Showing
9 changed files
with
384 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import Component from '@ember/component'; | ||
import {computed} from '@ember/object'; | ||
import {inject as injectService} from '@ember/service'; | ||
import {isEmpty} from '@ember/utils'; | ||
import {task} from 'ember-concurrency'; | ||
|
||
export default Component.extend({ | ||
|
||
store: injectService(), | ||
|
||
// public attributes | ||
tagName: '', | ||
post: null, | ||
|
||
// internal properties | ||
activeTheme: null, | ||
|
||
// closure actions | ||
onTemplateSelect() {}, | ||
|
||
// computed properties | ||
customTemplates: computed('activeTheme.customTemplates.[]', function () { | ||
let templates = this.get('activeTheme.customTemplates') || []; | ||
let defaultTemplate = { | ||
filename: '', | ||
name: 'Default' | ||
}; | ||
|
||
return isEmpty(templates) ? templates : [defaultTemplate, ...templates.sortBy('name')]; | ||
}), | ||
|
||
matchedSlugTemplate: computed('post.{page,slug}', 'activeTheme.slugTemplates.[]', function () { | ||
let slug = this.get('post.slug'); | ||
let type = this.get('post.page') ? 'page' : 'post'; | ||
|
||
let [matchedTemplate] = this.get('activeTheme.slugTemplates').filter(function (template) { | ||
return template.for.includes(type) && template.slug === slug; | ||
}); | ||
|
||
return matchedTemplate; | ||
}), | ||
|
||
selectedTemplate: computed('post.customTemplate', 'customTemplates.[]', function () { | ||
let templates = this.get('customTemplates'); | ||
let filename = this.get('post.customTemplate'); | ||
|
||
return templates.findBy('filename', filename); | ||
}), | ||
|
||
// hooks | ||
didInsertElement() { | ||
this._super(...arguments); | ||
this.get('loadActiveTheme').perform(); | ||
}, | ||
|
||
// tasks | ||
loadActiveTheme: task(function* () { | ||
let store = this.get('store'); | ||
let themes = yield store.peekAll('theme'); | ||
|
||
if (isEmpty(themes)) { | ||
themes = yield store.findAll('theme'); | ||
} | ||
|
||
let activeTheme = themes.filterBy('active', true).get('firstObject'); | ||
|
||
this.set('activeTheme', activeTheme); | ||
}), | ||
|
||
actions: { | ||
selectTemplate(template) { | ||
this.onTemplateSelect(template.filename); | ||
} | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{{#if customTemplates}} | ||
<div class="form-group for-select" data-test-custom-template-form> | ||
<label for="author-list">Template</label> | ||
<span class="gh-input-icon gh-icon-user"> | ||
{{inline-svg "file-text-document"}} | ||
<span class="gh-select {{if matchedSlugTemplate "disabled"}}"> | ||
{{one-way-select selectedTemplate | ||
options=customTemplates | ||
optionValuePath="filename" | ||
optionLabelPath="name" | ||
update=(action "selectTemplate") | ||
disabled=matchedSlugTemplate | ||
data-test-select="custom-template"}} | ||
{{inline-svg "arrow-down-small"}} | ||
</span> | ||
</span> | ||
<p>Post URL matches {{matchedSlugTemplate.filename}}</p> | ||
</div> | ||
{{/if}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd'; | ||
import destroyApp from 'ghost-admin/tests/helpers/destroy-app'; | ||
import startApp from 'ghost-admin/tests/helpers/start-app'; | ||
import {afterEach, beforeEach, describe, it} from 'mocha'; | ||
import {authenticateSession} from 'ghost-admin/tests/helpers/ember-simple-auth'; | ||
import {click, fillIn, find, keyEvent, visit} from 'ember-native-dom-helpers'; | ||
import {expect} from 'chai'; | ||
|
||
// keyCodes | ||
const KEY_S = 83; | ||
|
||
describe('Acceptance: Custom Post Templates', function() { | ||
let application; | ||
|
||
beforeEach(function() { | ||
application = startApp(); | ||
|
||
server.loadFixtures('settings'); | ||
|
||
let role = server.create('role', {name: 'Administrator'}); | ||
server.create('user', {roles: [role]}); | ||
|
||
authenticateSession(application); | ||
}); | ||
|
||
afterEach(function() { | ||
destroyApp(application); | ||
}); | ||
|
||
describe('with custom templates', function () { | ||
beforeEach(function () { | ||
server.create('theme', { | ||
active: true, | ||
name: 'example-theme', | ||
package: { | ||
name: 'Example Theme', | ||
version: '0.1' | ||
}, | ||
templates: [ | ||
{ | ||
filename: 'custom-news-bulletin.hbs', | ||
name: 'News Bulletin', | ||
for: ['post', 'page'], | ||
slug: null | ||
}, | ||
{ | ||
filename: 'custom-big-images.hbs', | ||
name: 'Big Images', | ||
for: ['post', 'page'], | ||
slug: null | ||
}, | ||
{ | ||
filename: 'post-one.hbs', | ||
name: 'One', | ||
for: ['post'], | ||
slug: 'one' | ||
}, | ||
{ | ||
filename: 'page-about.hbs', | ||
name: 'About', | ||
for: ['page'], | ||
slug: 'about' | ||
} | ||
] | ||
}); | ||
}); | ||
|
||
it('can change selected template', async function () { | ||
let post = server.create('post', {customTemplate: 'custom-news-bulletin.hbs'}); | ||
|
||
await visit('/editor/1'); | ||
await click('[data-test-psm-trigger]'); | ||
|
||
// template form should be shown | ||
expect(find('[data-test-custom-template-form]')).to.exist; | ||
|
||
// custom template should be selected | ||
let select = find('[data-test-select="custom-template"]'); | ||
expect(select.value, 'selected value').to.equal('custom-news-bulletin.hbs'); | ||
|
||
// templates list should contain default and custom templates in alphabetical order | ||
expect(select.options.length).to.equal(3); | ||
expect(select.options.item(0).value, 'default value').to.equal(''); | ||
expect(select.options.item(0).text, 'default text').to.equal('Default'); | ||
expect(select.options.item(1).value, 'first custom value').to.equal('custom-big-images.hbs'); | ||
expect(select.options.item(1).text, 'first custom text').to.equal('Big Images'); | ||
expect(select.options.item(2).value, 'second custom value').to.equal('custom-news-bulletin.hbs'); | ||
expect(select.options.item(2).text, 'second custom text').to.equal('News Bulletin'); | ||
|
||
// select the default template | ||
await fillIn(select, ''); | ||
|
||
// save then check server record | ||
await keyEvent('.gh-app', 'keydown', KEY_S, { | ||
metaKey: ctrlOrCmd === 'command', | ||
ctrlKey: ctrlOrCmd === 'ctrl' | ||
}); | ||
|
||
expect( | ||
server.db.posts.find(post.id).customTemplate, | ||
'saved custom template' | ||
).to.equal(''); | ||
}); | ||
|
||
it('disables template selector if slug matches slug-based template'); | ||
|
||
it('doesn\'t query themes endpoint unncessarily', async function () { | ||
function themeRequests() { | ||
return server.pretender.handledRequests.filter(function (request) { | ||
return request.url.match(/\/themes\//); | ||
}); | ||
} | ||
|
||
server.create('post', {customTemplate: 'custom-news-bulletin.hbs'}); | ||
|
||
await visit('/editor/1'); | ||
await click('[data-test-psm-trigger]'); | ||
|
||
expect(themeRequests().length, 'after first open').to.equal(1); | ||
|
||
await click('[data-test-psm-trigger]'); // hide | ||
await click('[data-test-psm-trigger]'); // show | ||
|
||
expect(themeRequests().length, 'after second open').to.equal(1); | ||
}); | ||
}); | ||
|
||
describe('without custom templates', function () { | ||
beforeEach(function () { | ||
server.create('theme', { | ||
active: true, | ||
name: 'example-theme', | ||
package: { | ||
name: 'Example Theme', | ||
version: '0.1' | ||
}, | ||
templates: [] | ||
}); | ||
}); | ||
|
||
it('doesn\'t show template selector', async function () { | ||
server.create('post', {customTemplate: 'custom-news-bulletin.hbs'}); | ||
|
||
await visit('/editor/1'); | ||
await click('[data-test-psm-trigger]'); | ||
|
||
// template form should be shown | ||
expect(find('[data-test-custom-template-form]')).to.not.exist; | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.