Merge pull request #1431 from mailpoet/background-images

Column Background Images [MAILPOET-1403]
This commit is contained in:
Michelle Shull
2018-07-23 09:33:08 -04:00
committed by GitHub
14 changed files with 570 additions and 314 deletions

View File

@@ -153,7 +153,7 @@ class RoboFile extends \Robo\Tasks {
return $this->_exec('./tasks/transifex_init.sh');
}
function testUnit($opts=['file' => null, 'xml' => false, 'multisite' => false]) {
function testUnit(array $opts=['file' => null, 'xml' => false, 'multisite' => false, 'debug' => false]) {
$this->loadEnv();
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml';
@@ -170,6 +170,10 @@ class RoboFile extends \Robo\Tasks {
$command .= ' --xml';
}
if($opts['debug']) {
$command .= ' --debug';
}
return $this->_exec($command);
}

View File

@@ -0,0 +1,313 @@
/* eslint-disable func-names */
/**
* Media manager behaviour
*
* Adds a media manager integration with the view
*/
define([
'backbone.marionette',
'underscore',
'newsletter_editor/behaviors/BehaviorsLookup',
'jquery'
], function (Marionette, _, BehaviorsLookup, jQuery) {
var BL = BehaviorsLookup;
BL.MediaManagerBehavior = Marionette.Behavior.extend({
ui: {
'select-image': '.mailpoet_field_image_select_image',
'address-input': '.mailpoet_field_image_address'
},
events: {
'click @ui.select-image': 'showMediaManager',
'input @ui.address-input': 'changeAddress'
},
initialize: function () {
if (this.view.options.showImageManager) {
this.showMediaManager();
}
},
changeAddress: function (event) {
var src = jQuery(event.target).val();
var image = new Image();
if (!src && this.options.onSelect) {
this.view[this.options.onSelect]({
src: null,
width: null,
height: null
});
return;
}
image.onload = function () {
if (this.options.onSelect) {
this.view[this.options.onSelect]({
src: src,
width: image.naturalWidth + 'px',
height: image.naturalHeight + 'px'
});
}
}.bind(this);
image.src = src;
},
showMediaManager: function () {
var that = this;
var MediaManager;
var theFrame;
if (this._mediaManager) {
this._mediaManager.resetSelections();
this._mediaManager.open();
return;
}
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
initialize: function () {
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
_.defaults(this.options, {
multiple: true,
editing: false,
state: 'insert'
});
this.createSelection();
this.createStates();
this.bindHandlers();
this.createIframeStates();
// Hide title
this.$el.addClass('hide-title');
},
resetSelections: function () {
this.state().get('selection').reset();
},
createQuery: function (options) {
var query = window.wp.media.query(options);
return query;
},
createStates: function () {
var options = this.options;
// Add the default states.
this.states.add([
// Main states.
new window.wp.media.controller.Library({
id: 'insert',
title: 'Add images',
priority: 20,
toolbar: 'main-insert',
filterable: 'image',
library: this.createQuery(options.library),
multiple: options.multiple ? 'reset' : false,
editable: false,
// If the user isn't allowed to edit fields,
// can they still edit it locally?
allowLocalEdits: false,
// Show the attachment display settings.
displaySettings: false,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: false
})
]);
if (window.wp.media.view.settings.post.featuredImageId) {
this.states.add(new window.wp.media.controller.FeaturedImage());
}
},
bindHandlers: function () {
var handlers;
// from Select
this.on('router:create:browse', this.createRouter, this);
this.on('router:render:browse', this.browseRouter, this);
this.on('content:create:browse', this.browseContent, this);
this.on('content:render:upload', this.uploadContent, this);
this.on('toolbar:create:select', this.createSelectToolbar, this);
this.on('menu:create:gallery', this.createMenu, this);
this.on('toolbar:create:main-insert', this.createToolbar, this);
this.on('toolbar:create:main-gallery', this.createToolbar, this);
this.on('toolbar:create:main-embed', this.mainEmbedToolbar, this);
this.on('updateExcluded', this.browseContent, this);
handlers = {
content: {
embed: 'embedContent',
'edit-selection': 'editSelectionContent'
},
toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
_.each(handlers, function (regionHandlers, region) {
_.each(regionHandlers, function (callback, handler) {
this.on(region + ':render:' + handler, this[callback], this);
}, this);
}, this);
},
uploadContent: function () {
window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
this.$el.addClass('hide-toolbar');
},
// Content
embedContent: function () {
var view = new window.wp.media.view.Embed({
controller: this,
model: this.state()
}).render();
this.content.set(view);
view.url.focus();
},
editSelectionContent: function () {
var state = this.state();
var selection = state.get('selection');
var view;
view = new window.wp.media.view.AttachmentsBrowser({
controller: this,
collection: selection,
selection: selection,
model: state,
sortable: true,
search: false,
dragInfo: true,
AttachmentView: window.wp.media.view.Attachment.EditSelection
}).render();
view.toolbar.set('backToLibrary', {
text: 'Return to library',
priority: -100,
click: function () {
this.controller.content.mode('browse');
}
});
// Browse our library of attachments.
this.content.set(view);
},
// Toolbars
selectionStatusToolbar: function (view) {
var editable = this.state().get('editable');
view.set('selection', new window.wp.media.view.Selection({
controller: this,
collection: this.state().get('selection'),
priority: -40,
// If the selection is editable, pass the callback to
// switch the content mode.
editable: editable && function () {
this.controller.content.mode('edit-selection');
}
}).render());
},
mainInsertToolbar: function (view) {
var controller = this;
this.selectionStatusToolbar(view);
view.set('insert', {
style: 'primary',
priority: 80,
text: 'Select Image',
requires: { selection: true },
click: function () {
var state = controller.state();
var selection = state.get('selection');
controller.close();
state.trigger('insert', selection).reset();
}
});
},
mainEmbedToolbar: function (toolbar) {
var tbar = toolbar;
tbar.view = new window.wp.media.view.Toolbar.Embed({
controller: this,
text: 'Add images'
});
}
});
theFrame = new MediaManager({
id: 'mailpoet-media-manager',
frame: 'select',
title: 'Select image',
editing: false,
multiple: false,
library: {
type: 'image'
},
displaySettings: false,
button: {
text: 'Select'
}
});
this._mediaManager = theFrame;
this._mediaManager.on('insert', function () {
// Append media manager image selections to Images tab
var selection = theFrame.state().get('selection');
selection.each(function (attachment) {
var sizes = attachment.get('sizes');
// Following advice from Becs, the target width should
// be a double of one column width to render well on
// retina screen devices
var targetImageWidth = 1320;
// Pick the width that is closest to target width
var increasingByWidthDifference = _.sortBy(
_.keys(sizes),
function (size) {
return Math.abs(targetImageWidth - sizes[size].width);
}
);
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
var imagesOfBestWidth = _.filter(
_.values(sizes),
function (size) { return size.width === bestWidth; }
);
// Maximize the height if there are multiple images with same width
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
if (that.options.onSelect) {
that.view[that.options.onSelect]({
height: mainSize.height + 'px',
width: mainSize.width + 'px',
src: mainSize.url,
alt: (attachment.get('alt') !== '' && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title')
});
}
});
});
this._mediaManager.open();
},
onBeforeDestroy: function () {
if (typeof this._mediaManager === 'object') {
this._mediaManager.remove();
}
}
});
});

View File

@@ -40,6 +40,10 @@ define([
return this._getDefaults({
type: 'container',
orientation: 'vertical',
image: {
src: null,
display: 'scale'
},
styles: {
block: {
backgroundColor: 'transparent'
@@ -239,16 +243,19 @@ define([
});
Module.ContainerBlockSettingsView = base.BlockSettingsView.extend({
behaviors: _.extend({}, base.BlockSettingsView.prototype.behaviors, {
MediaManagerBehavior: {
onSelect: 'onImageSelect'
}
}),
getTemplate: function () { return window.templates.containerBlockSettings; },
events: function () {
return {
'change .mailpoet_field_container_background_color': _.partial(this.changeColorField, 'styles.block.backgroundColor'),
'click .mailpoet_done_editing': 'close'
'click .mailpoet_done_editing': 'close',
'change .mailpoet_field_display_type': 'changeDisplayType'
};
},
regions: {
columnsSettingsRegion: '.mailpoet_container_columns_settings'
},
initialize: function () {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
@@ -256,8 +263,14 @@ define([
collection: this.model.get('blocks')
});
},
onRender: function () {
this.showChildView('columnsSettingsRegion', this._columnsSettingsView);
changeDisplayType: function (event) {
this.model.get('image').set('display', event.target.value);
this.model.trigger('change');
},
onImageSelect: function (image) {
this.model.set('image.src', image.src);
this.model.trigger('change');
this.render();
}
});

View File

@@ -75,6 +75,11 @@ define([
});
Module.ImageBlockSettingsView = base.BlockSettingsView.extend({
behaviors: _.extend({}, base.BlockSettingsView.prototype.behaviors, {
MediaManagerBehavior: {
onSelect: 'onImageSelect'
}
}),
onRender: function () {
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-full-width'), {
tooltipId: 'tooltip-editor-full-width',
@@ -89,11 +94,9 @@ define([
events: function () {
return {
'input .mailpoet_field_image_link': _.partial(this.changeField, 'link'),
'input .mailpoet_field_image_address': 'changeAddress',
'input .mailpoet_field_image_alt_text': _.partial(this.changeField, 'alt'),
'change .mailpoet_field_image_full_width': _.partial(this.changeBoolCheckboxField, 'fullWidth'),
'change .mailpoet_field_image_alignment': _.partial(this.changeField, 'styles.block.textAlign'),
'click .mailpoet_field_image_select_another_image': 'showMediaManager',
'click .mailpoet_done_editing': 'close',
'input .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
'change .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
@@ -120,284 +123,14 @@ define([
this.$('.mailpoet_field_image_width').val(width);
this.$('.mailpoet_field_image_width_input').val(width);
},
initialize: function (options) {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
if (options.showImageManager) {
this.showMediaManager();
}
},
showMediaManager: function () {
var that = this;
var MediaManager;
var theFrame;
if (this._mediaManager) {
this._mediaManager.resetSelections();
this._mediaManager.open();
return;
}
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
initialize: function () {
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
_.defaults(this.options, {
multiple: true,
editing: false,
state: 'insert'
});
this.createSelection();
this.createStates();
this.bindHandlers();
this.createIframeStates();
// Hide title
this.$el.addClass('hide-title');
},
resetSelections: function () {
this.state().get('selection').reset();
},
createQuery: function (options) {
var query = window.wp.media.query(options);
return query;
},
createStates: function () {
var options = this.options;
// Add the default states.
this.states.add([
// Main states.
new window.wp.media.controller.Library({
id: 'insert',
title: 'Add images',
priority: 20,
toolbar: 'main-insert',
filterable: 'image',
library: this.createQuery(options.library),
multiple: options.multiple ? 'reset' : false,
editable: false,
// If the user isn't allowed to edit fields,
// can they still edit it locally?
allowLocalEdits: false,
// Show the attachment display settings.
displaySettings: false,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: false
})
]);
if (window.wp.media.view.settings.post.featuredImageId) {
this.states.add(new window.wp.media.controller.FeaturedImage());
}
},
bindHandlers: function () {
var handlers;
// from Select
this.on('router:create:browse', this.createRouter, this);
this.on('router:render:browse', this.browseRouter, this);
this.on('content:create:browse', this.browseContent, this);
this.on('content:render:upload', this.uploadContent, this);
this.on('toolbar:create:select', this.createSelectToolbar, this);
this.on('menu:create:gallery', this.createMenu, this);
this.on('toolbar:create:main-insert', this.createToolbar, this);
this.on('toolbar:create:main-gallery', this.createToolbar, this);
this.on('toolbar:create:main-embed', this.mainEmbedToolbar, this);
this.on('updateExcluded', this.browseContent, this);
handlers = {
content: {
embed: 'embedContent',
'edit-selection': 'editSelectionContent'
},
toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
_.each(handlers, function (regionHandlers, region) {
_.each(regionHandlers, function (callback, handler) {
this.on(region + ':render:' + handler, this[callback], this);
}, this);
}, this);
},
uploadContent: function () {
window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
this.$el.addClass('hide-toolbar');
},
// Content
embedContent: function () {
var view = new window.wp.media.view.Embed({
controller: this,
model: this.state()
}).render();
this.content.set(view);
view.url.focus();
},
editSelectionContent: function () {
var state = this.state();
var selection = state.get('selection');
var view;
view = new window.wp.media.view.AttachmentsBrowser({
controller: this,
collection: selection,
selection: selection,
model: state,
sortable: true,
search: false,
dragInfo: true,
AttachmentView: window.wp.media.view.Attachment.EditSelection
}).render();
view.toolbar.set('backToLibrary', {
text: 'Return to library',
priority: -100,
click: function () {
this.controller.content.mode('browse');
}
});
// Browse our library of attachments.
this.content.set(view);
},
// Toolbars
selectionStatusToolbar: function (view) {
var editable = this.state().get('editable');
view.set('selection', new window.wp.media.view.Selection({
controller: this,
collection: this.state().get('selection'),
priority: -40,
// If the selection is editable, pass the callback to
// switch the content mode.
editable: editable && function () {
this.controller.content.mode('edit-selection');
}
}).render());
},
mainInsertToolbar: function (view) {
var controller = this;
this.selectionStatusToolbar(view);
view.set('insert', {
style: 'primary',
priority: 80,
text: 'Select Image',
requires: { selection: true },
click: function () {
var state = controller.state();
var selection = state.get('selection');
controller.close();
state.trigger('insert', selection).reset();
}
});
},
mainEmbedToolbar: function (toolbar) {
var tbar = toolbar;
tbar.view = new window.wp.media.view.Toolbar.Embed({
controller: this,
text: 'Add images'
});
}
});
theFrame = new MediaManager({
id: 'mailpoet-media-manager',
frame: 'select',
title: 'Select image',
editing: false,
multiple: false,
library: {
type: 'image'
},
displaySettings: false,
button: {
text: 'Select'
}
});
this._mediaManager = theFrame;
this._mediaManager.on('insert', function () {
// Append media manager image selections to Images tab
var selection = theFrame.state().get('selection');
selection.each(function (attachment) {
var sizes = attachment.get('sizes');
// Following advice from Becs, the target width should
// be a double of one column width to render well on
// retina screen devices
var targetImageWidth = 1320;
// Pick the width that is closest to target width
var increasingByWidthDifference = _.sortBy(
_.keys(sizes),
function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
);
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
var imagesOfBestWidth = _.filter(
_.values(sizes),
function (size) { return size.width === bestWidth; }
);
// Maximize the height if there are multiple images with same width
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
that.model.set({
height: mainSize.height + 'px',
width: mainSize.width + 'px',
src: mainSize.url,
alt: (attachment.get('alt') !== '' && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title')
});
// Rerender settings view due to changes from outside of settings view
that.render();
});
});
this._mediaManager.open();
},
changeAddress: function (event) {
var src = jQuery(event.target).val();
var image = new Image();
image.onload = function () {
this.model.set({
src: src,
width: image.naturalWidth + 'px',
height: image.naturalHeight + 'px'
});
}.bind(this);
image.src = src;
},
onBeforeDestroy: function () {
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
if (typeof this._mediaManager === 'object') {
this._mediaManager.remove();
onImageSelect: function (image) {
if (image.src === null) {
this.model.set({ src: '' });
} else {
this.model.set(image);
}
// Rerender settings view due to changes from outside of settings view
this.render();
}
});

View File

@@ -2,14 +2,14 @@
namespace MailPoet\Newsletter\Renderer\Columns;
class Renderer {
function render($column_styles, $columns_count, $columns_data) {
function render($column_styles, $column_image, $columns_count, $columns_data) {
$styles = $column_styles['block'];
$width = ColumnsHelper::columnWidth($columns_count);
$class = ColumnsHelper::columnClass($columns_count);
$alignment = ColumnsHelper::columnAlignment($columns_count);
$template = ($columns_count === 1) ?
$this->getOneColumnTemplate($styles, $class) :
$this->getMultipleColumnsTemplate($styles, $width, $alignment, $class);
$this->getOneColumnTemplate($styles, $column_image, $class) :
$this->getMultipleColumnsTemplate($styles, $column_image, $width, $alignment, $class);
$result = array_map(function($content) use ($template) {
return $template['content_start'] . $content . $template['content_end'];
}, $columns_data);
@@ -20,16 +20,16 @@ class Renderer {
return $result;
}
function getOneColumnTemplate($styles, $class) {
$background_color = $this->getBackgroundColor($styles);
function getOneColumnTemplate($styles, $image, $class) {
$background_css = $this->getBackgroundCss($styles, $image);
$template['content_start'] = '
<tr>
<td class="mailpoet_content" align="center" style="border-collapse:collapse">
<td class="mailpoet_content" align="center" style="border-collapse:collapse;' . $background_css . '">
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
<td style="padding-left:0;padding-right:0">
<table width="100%" border="0" cellpadding="0" cellspacing="0" class="mailpoet_' . $class . '" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;table-layout:fixed;margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;' . $background_color . '">
<table width="100%" border="0" cellpadding="0" cellspacing="0" class="mailpoet_' . $class . '" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;table-layout:fixed;margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;">
<tbody>';
$template['content_end'] = '
</tbody>
@@ -43,11 +43,11 @@ class Renderer {
return $template;
}
function getMultipleColumnsTemplate($styles, $width, $alignment, $class) {
$background_color = $this->getBackgroundColor($styles);
function getMultipleColumnsTemplate($styles, $image, $width, $alignment, $class) {
$background_css = $this->getBackgroundCss($styles, $image);
$template['container_start'] = '
<tr>
<td class="mailpoet_content-' . $class . '" align="left" style="border-collapse:collapse;' . $background_color . '">
<td class="mailpoet_content-' . $class . '" align="left" style="border-collapse:collapse;' . $background_css . '">
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
@@ -78,11 +78,21 @@ class Renderer {
return $template;
}
function getBackgroundColor($styles) {
if(!isset($styles['backgroundColor'])) return false;
$background_color = $styles['backgroundColor'];
return ($background_color !== 'transparent') ?
sprintf('background-color:%s!important;" bgcolor="%s', $background_color, $background_color) :
false;
private function getBackgroundCss($styles, $image) {
if($image !== null && $image['src'] !== null) {
$background_color = isset($styles['backgroundColor']) && $styles['backgroundColor'] !== 'transparent' ? $styles['backgroundColor'] : '#ffffff';
$repeat = $image['display'] === 'tile' ? 'repeat' : 'no-repeat';
$size = $image['display'] === 'scale' ? 'cover' : 'contain';
return sprintf(
'background: %s url(%s) %s center/%s;background-color: %s;background-image: url(%s);background-repeat: %s;background-position: center;background-size: %s;',
$background_color, $image['src'], $repeat, $size, $background_color, $image['src'], $repeat, $size
);
} else {
if(!isset($styles['backgroundColor'])) return false;
$background_color = $styles['backgroundColor'];
return ($background_color !== 'transparent') ?
sprintf('background-color:%s!important;" bgcolor="%s', $background_color, $background_color) :
false;
}
}
}

View File

@@ -102,8 +102,10 @@ class Renderer {
$content_block,
$column_count
);
$content_block_image = isset($content_block['image'])?$content_block['image']:null;
return $_this->columns_renderer->render(
$content_block['styles'],
$content_block_image,
$column_count,
$column_data
);

View File

@@ -29,6 +29,10 @@ define([
expect(model.get('styles.block.backgroundColor')).to.match(/^(#[abcdef0-9]{6})|transparent$/);
});
it('has a image display style', function () {
expect(model.get('image.display')).to.equal('scale');
});
it('has a collection of blocks', function () {
expect(model.get('blocks')).to.be.instanceof(Backbone.Collection);
});
@@ -42,6 +46,10 @@ define([
block: {
backgroundColor: '#123456'
}
},
image: {
src: null,
display: 'scale'
}
}
}
@@ -49,6 +57,7 @@ define([
innerModel = new (ContainerBlock.ContainerBlockModel)();
expect(innerModel.get('styles.block.backgroundColor')).to.equal('#123456');
expect(innerModel.get('image.display')).to.equal('scale');
});
it('do not update blockDefaults.container when changed', function () {
@@ -130,7 +139,22 @@ define([
describe('once rendered', function () {
describe('on root level', function () {
var model = new (ContainerBlock.ContainerBlockModel)();
var imageSrc = 'http://example.org/someNewImage.png';
var model = new (ContainerBlock.ContainerBlockModel)({
type: 'container',
orientation: 'vertical',
image: {
src: imageSrc,
display: 'scale',
width: 123,
height: 456
},
styles: {
block: {
backgroundColor: 'transparent'
}
}
});
var view;
beforeEach(function () {
@@ -159,6 +183,15 @@ define([
it('has a duplication tool', function () {
expect(view.$('.mailpoet_duplicate_block')).to.have.length(1);
});
it('has a background image set', function () {
var style = view.$('style').text();
expect(style).contains('.mailpoet_editor_view_' + view.cid);
expect(style).contains('background-color: #ffffff !important;');
expect(style).contains('background-image: url(http://example.org/someNewImage.png);');
expect(style).contains('background-position: center;');
expect(style).contains('background-size: cover;');
});
});
describe.skip('on non-root levels', function () {
@@ -211,11 +244,13 @@ define([
describe('once rendered', function () {
var model;
var view;
var newSrc = 'http://example.org/someNewImage.png';
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication);
model = new (ContainerBlock.ContainerBlockModel)();
view = new (ContainerBlock.ContainerBlockSettingsView)({ model: model });
view.render();
});
it('updates the model when background color changes', function () {
@@ -223,6 +258,23 @@ define([
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
});
it('updates the model background image display type changes', function () {
view.$('.mailpoet_field_display_type:nth(2)').attr('checked', true).change();
expect(model.get('image.display')).to.equal('tile');
});
it('updates the model when background image src changes', function () {
global.stubImage(123, 456);
view.$('.mailpoet_field_image_address').val(newSrc).trigger('input');
expect(model.get('image.src')).to.equal(newSrc);
});
it('updates the model when background image src is deleted', function () {
global.stubImage(123, 456);
view.$('.mailpoet_field_image_address').val('').trigger('input');
expect(model.get('image.src')).to.equal(null);
});
it.skip('closes the sidepanel after "Done" is clicked', function () {
var mock = sinon.mock().once();
global.MailPoet.Modal.cancel = mock;

View File

@@ -59,6 +59,7 @@ class RendererTest extends \MailPoetTest {
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
null,
count($column_content),
$column_content)
);
@@ -81,6 +82,7 @@ class RendererTest extends \MailPoetTest {
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
null,
count($column_content),
$column_content)
);
@@ -104,6 +106,7 @@ class RendererTest extends \MailPoetTest {
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
null,
count($column_content),
$column_content)
);
@@ -113,6 +116,82 @@ class RendererTest extends \MailPoetTest {
expect($rendered_column_content)->equals($column_content);
}
function testItRendersScaledColumnBackgroundImage() {
$column_content = ['one'];
$column_styles = ['block' => ['backgroundColor' => "#999999"]];
$column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'scale', 'width' => '1000px', 'height' => '500px'];
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
$column_image,
count($column_content),
$column_content)
);
$column_css = $DOM('td.mailpoet_content')[0]->attr('style');
expect($column_css)->contains('background: #999999 url(https://example.com/image.jpg) no-repeat center/cover;');
expect($column_css)->contains('background-color: #999999;');
expect($column_css)->contains('background-image: url(https://example.com/image.jpg);');
expect($column_css)->contains('background-repeat: no-repeat;');
expect($column_css)->contains('background-position: center;');
expect($column_css)->contains('background-size: cover;');
}
function testItRendersFitColumnBackgroundImage() {
$column_content = ['one'];
$column_styles = ['block' => ['backgroundColor' => "#999999"]];
$column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'fit', 'width' => '1000px', 'height' => '500px'];
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
$column_image,
count($column_content),
$column_content)
);
$column_css = $DOM('td.mailpoet_content')[0]->attr('style');
expect($column_css)->contains('background: #999999 url(https://example.com/image.jpg) no-repeat center/contain;');
expect($column_css)->contains('background-color: #999999;');
expect($column_css)->contains('background-image: url(https://example.com/image.jpg);');
expect($column_css)->contains('background-repeat: no-repeat;');
expect($column_css)->contains('background-position: center;');
expect($column_css)->contains('background-size: contain;');
}
function testItRendersTiledColumnBackgroundImage() {
$column_content = ['one'];
$column_styles = ['block' => ['backgroundColor' => "#999999"]];
$column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'tile', 'width' => '1000px', 'height' => '500px'];
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
$column_image,
count($column_content),
$column_content)
);
$column_css = $DOM('td.mailpoet_content')[0]->attr('style');
expect($column_css)->contains('background: #999999 url(https://example.com/image.jpg) repeat center/contain;');
expect($column_css)->contains('background-color: #999999;');
expect($column_css)->contains('background-image: url(https://example.com/image.jpg);');
expect($column_css)->contains('background-repeat: repeat;');
expect($column_css)->contains('background-position: center;');
expect($column_css)->contains('background-size: contain;');
}
function testItRendersFallbackColumnBackgroundColorForBackgroundImage() {
$column_content = ['one'];
$column_styles = ['block' => ['backgroundColor' => 'transparent']];
$column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'tile', 'width' => '1000px', 'height' => '500px'];
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
$column_image,
count($column_content),
$column_content)
);
$column_css = $DOM('td.mailpoet_content')[0]->attr('style');
expect($column_css)->contains('background: #ffffff url(https://example.com/image.jpg) repeat center/contain;');
expect($column_css)->contains('background-color: #ffffff;');
}
function testItRendersHeader() {
$newsletter = $this->newsletter['body'];
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][0];

View File

@@ -1113,6 +1113,10 @@
},
},
container: {
image: {
src: null,
display: 'scale',
},
styles: {
block: {
backgroundColor: 'transparent',

View File

@@ -1,8 +1,22 @@
{{#ifCond model.styles.block.backgroundColor '!=' 'transparent'}}
<style type="text/css">
.mailpoet_editor_view_{{ viewCid }} { background-color: {{ model.styles.block.backgroundColor }}; }
.mailpoet_editor_view_{{ viewCid }} .mailpoet_container { background-color: {{ model.styles.block.backgroundColor }}; }
</style>
{{/ifCond}}
{{#if model.image.src}}
<style type="text/css">
.mailpoet_editor_view_{{ viewCid }} {
background-color: {{#ifCond model.styles.block.backgroundColor '!=' 'transparent'}}{{ model.styles.block.backgroundColor }}{{else}}#ffffff{{/ifCond}} !important;
background-image: url({{ model.image.src }});
background-position: center;
background-repeat: {{#ifCond model.image.display '==' 'tile'}}repeat{{else}}no-repeat{{/ifCond}};
background-size: {{#ifCond model.image.display '==' 'scale'}}cover{{else}}contain{{/ifCond}};
}
.mailpoet_editor_view_{{ viewCid }} .mailpoet_container { background: transparent; }
</style>
{{else}}
{{#ifCond model.styles.block.backgroundColor '!=' 'transparent'}}
<style type="text/css">
.mailpoet_editor_view_{{ viewCid }} { background-color: {{ model.styles.block.backgroundColor }}; }
.mailpoet_editor_view_{{ viewCid }} .mailpoet_container { background-color: {{ model.styles.block.backgroundColor }}; }
</style>
{{/ifCond}}
{{/if}}
<div class="mailpoet_container {{#ifCond model.orientation '===' 'horizontal'}}mailpoet_container_horizontal{{/ifCond}}{{#ifCond model.orientation '===' 'vertical'}}mailpoet_container_vertical{{/ifCond}}"></div>
<div class="mailpoet_tools"></div><div class="mailpoet_block_highlight">

View File

@@ -4,11 +4,41 @@
<div class="mailpoet_form_field_input_option">
<input type="text" name="background-color" class="mailpoet_field_container_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
</div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background color') %></div>
</label>
</div>
<div class="mailpoet_container_columns_settings"></div>
<div class="mailpoet_form_field">
<label>
<div class="mailpoet_form_field_title"><%= __('Background image') %></div>
<div class="mailpoet_form_field_input_option">
<input type="text" name="src" class="mailpoet_input mailpoet_field_image_address" value="{{ model.image.src }}" placeholder="http://" /><br />
</div>
</label>
</div>
<div class="mailpoet_form_field">
<input type="button" name="select-image" class="button button-secondary mailpoet_button_full mailpoet_field_image_select_image" value="{{#if model.image.src}}<%= __('Select another image') | escape('html_attr') %>{{else}}<%= __('Select image') | escape('html_attr') %>{{/if}}" />
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Display options') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="display_type" class="mailpoet_field_display_type" value="scale" {{#ifCond model.image.display '===' 'scale'}}CHECKED{{/ifCond}}/>
<%= __('Scale') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="display_type" class="mailpoet_field_display_type" value="fit" {{#ifCond model.image.display '===' 'fit'}}CHECKED{{/ifCond}}/>
<%= __('Fit') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="display_type" class="mailpoet_field_display_type" value="tile" {{#ifCond model.image.display '===' 'tile'}}CHECKED{{/ifCond}}/>
<%= __('Tile') %>
</label>
</div>
</div>
<div class="mailpoet_form_field">
<input type="button" class="button button-primary mailpoet_done_editing" value="<%= __('Done') | escape('html_attr') %>" />

View File

@@ -2,7 +2,7 @@
<div class="mailpoet_content" style="{{#ifCond model.styles.block.textAlign '==' 'left'}}margin: 0 auto 0 0; {{/ifCond}}{{#ifCond model.styles.block.textAlign '==' 'center'}}margin: auto; {{/ifCond}}{{#ifCond model.styles.block.textAlign '==' 'right'}}margin: 0 0 0 auto; {{/ifCond}}width: {{model.width}}">
<div class="mailpoet_image">
<a href="{{ model.link }}" onClick="return false;">
<img src="{{#ifCond model.src '!=' ''}}{{ model.src }}{{ else }}{{ imageMissingSrc }}{{/ifCond}}" alt="{{ model.alt }}" onerror="if(this.src != '{{ imageMissingSrc }}') {this.src = '{{ imageMissingSrc }}'; this.style.width='auto';}" width="{{model.width}}" />
<img src="{{#ifCond model.src '!=' ''}}{{ model.src }}{{ else }}{{ imageMissingSrc }}{{/ifCond}}" alt="{{ model.alt }}" onerror="if(this.src != '{{ imageMissingSrc }}') {this.src = '{{ imageMissingSrc }}';}" width="{{model.width}}" />
</a>
<div class="mailpoet_image_resize_handle_container">
<div class="mailpoet_image_resize_handle">

View File

@@ -80,7 +80,7 @@
</div>
<hr />
<div class="mailpoet_form_field">
<input type="button" name="select-another-image" class="button button-secondary mailpoet_button_full mailpoet_field_image_select_another_image" value="<%= __('Select another image') | escape('html_attr') %>" />
<input type="button" name="select-image" class="button button-secondary mailpoet_button_full mailpoet_field_image_select_image" value="<%= __('Select another image') | escape('html_attr') %>" />
</div>
<div class="mailpoet_form_field">

View File

@@ -310,6 +310,7 @@ var adminConfig = {
'newsletter_editor/behaviors/DraggableBehavior.js',
'newsletter_editor/behaviors/HighlightContainerBehavior.js',
'newsletter_editor/behaviors/HighlightEditingBehavior.js',
'newsletter_editor/behaviors/MediaManagerBehavior.js',
'newsletter_editor/behaviors/ResizableBehavior.js',
'newsletter_editor/behaviors/SortableBehavior.js',
'newsletter_editor/behaviors/ShowSettingsBehavior.js',
@@ -414,6 +415,7 @@ var testConfig = {
'newsletter_editor/behaviors/DraggableBehavior.js',
'newsletter_editor/behaviors/HighlightContainerBehavior.js',
'newsletter_editor/behaviors/HighlightEditingBehavior.js',
'newsletter_editor/behaviors/MediaManagerBehavior.js',
'newsletter_editor/behaviors/ResizableBehavior.js',
'newsletter_editor/behaviors/SortableBehavior.js',
'newsletter_editor/behaviors/ShowSettingsBehavior.js',