Skip to content

Commit 428ca5c

Browse files
author
Hwashiang Yu
committed
MC-5386: Anchors Under Each Div if TinyMCE link Is Added to Banner/Slide (Add Notification for When Link Attribute Is Entered)
- Updated banner and slider form with the new url-input template for error messaging - Updated styles for error handling - Updated preview component for new error logic - Added url and message input validation rule - Added custom dialog box for inline editor validation
1 parent 28a01e8 commit 428ca5c

File tree

11 files changed

+278
-6
lines changed

11 files changed

+278
-6
lines changed

app/code/Magento/PageBuilder/view/adminhtml/ui_component/pagebuilder_banner_form.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@
108108
<settings>
109109
<label translate="true">Message Text</label>
110110
<dataScope>message</dataScope>
111+
<validation>
112+
<rule name="validate-no-url" xsi:type="boolean">true</rule>
113+
</validation>
114+
<imports>
115+
<link name="validation.validate-no-url">${$.parentName}.link_url:value</link>
116+
</imports>
111117
</settings>
112118
<formElements>
113119
<wysiwyg>
@@ -117,12 +123,18 @@
117123
</wysiwyg>
118124
</formElements>
119125
</field>
120-
<urlInput name="link_url" sortOrder="20">
126+
<urlInput name="link_url" sortOrder="20" template="Magento_PageBuilder/form/element/url-input">
121127
<settings>
122128
<dataType>text</dataType>
123129
<label translate="true">Link</label>
124130
<dataScope>link_url</dataScope>
125131
<urlTypes class="Magento\Ui\Model\UrlInput\LinksConfigProvider"/>
132+
<validation>
133+
<rule name="validate-message-no-link" xsi:type="boolean">true</rule>
134+
</validation>
135+
<imports>
136+
<link name="validation.validate-message-no-link">${$.parentName}.message:value</link>
137+
</imports>
126138
</settings>
127139
</urlInput>
128140
<field name="show_button" sortOrder="40" formElement="select">

app/code/Magento/PageBuilder/view/adminhtml/ui_component/pagebuilder_slide_form.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@
124124
<settings>
125125
<label translate="true">Message Text</label>
126126
<dataScope>content</dataScope>
127+
<validation>
128+
<rule name="validate-no-url" xsi:type="boolean">true</rule>
129+
</validation>
130+
<imports>
131+
<link name="validation.validate-no-url">${$.parentName}.link_url:value</link>
132+
</imports>
127133
</settings>
128134
<formElements>
129135
<wysiwyg>
@@ -133,12 +139,18 @@
133139
</wysiwyg>
134140
</formElements>
135141
</field>
136-
<urlInput name="link_url" sortOrder="20">
142+
<urlInput name="link_url" sortOrder="20" template="Magento_PageBuilder/form/element/url-input">
137143
<settings>
138144
<dataType>text</dataType>
139145
<label translate="true">Link</label>
140146
<dataScope>link_url</dataScope>
141147
<urlTypes class="Magento\Ui\Model\UrlInput\LinksConfigProvider"/>
148+
<validation>
149+
<rule name="validate-message-no-link" xsi:type="boolean">true</rule>
150+
</validation>
151+
<imports>
152+
<link name="validation.validate-message-no-link">${$.parentName}.content:value</link>
153+
</imports>
142154
</settings>
143155
</urlInput>
144156
<field name="show_button" sortOrder="40" formElement="select">

app/code/Magento/PageBuilder/view/adminhtml/web/css/source/_config.less

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
// * See COPYING.txt for license details.
44
// */
55

6+
.error-border (@color) {
7+
border: 1px solid @color;
8+
overflow: hidden;
9+
}
10+
611
[data-role='modal'] {
712
.action-pagebuilder-cancel,
813
.action-pagebuilder-cancel:hover {
@@ -17,13 +22,28 @@
1722

1823
.admin__field {
1924
&._error {
25+
.url-input-element-linked-element {
26+
input,
27+
.action-select-wrap {
28+
.error-border(@field-error-control__border-color);
29+
}
30+
}
31+
2032
.admin__field-control > .admin__field-design-options > .layout-onion {
21-
border: 1px solid @field-error-control__border-color;
22-
overflow: hidden;
33+
.error-border(@field-error-control__border-color);
34+
}
35+
._error {
36+
.admin__control-wysiwig {
37+
.textarea {
38+
.error-border(@field-error-control__border-color);
39+
}
40+
.mce-tinymce {
41+
.error-border(@field-error-control__border-color);
42+
}
43+
}
2344
}
2445
}
2546
}
26-
2747
._required {
2848
> .admin__field-label {
2949
> label:after {

app/code/Magento/PageBuilder/view/adminhtml/web/js/content-type/banner/preview.js

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/code/Magento/PageBuilder/view/adminhtml/web/js/content-type/slide/preview.js

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/code/Magento/PageBuilder/view/adminhtml/web/js/form/element/validator-rules-mixin.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,30 @@ define([
4444
return (/^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(href)//eslint-disable-line max-len);
4545
}
4646

47+
/**
48+
* Validate that string has an anchor tag
49+
* @param {String} str
50+
* @return {Boolean}
51+
*/
52+
function validateWysiwygHasAnchorTags(str) {
53+
54+
return (/<a.*?>|<\/a>/igm).test(str)//eslint-disable-line max-len);
55+
}
56+
57+
/**
58+
* Validate message field and url field anchor tag is used exclusively by one field
59+
* @param {String} message
60+
* @param {Object} url
61+
* @return {Boolean}
62+
*/
63+
function validateOneAnchorTagField(message, url) {
64+
65+
return !(validateWysiwygHasAnchorTags(message) && ((url.type === 'page' && url.page && url.page.length !== 0)
66+
|| (url.type === 'product' && url.product && url.product.length !== 0)
67+
|| (url.type === 'category' && url.category && url.category.length !== 0)
68+
|| (url.type === 'default' && url.default && url.default.length !== 0)));
69+
}
70+
4771
/**
4872
* Validate a field with an expected data value of type object, like margins_and_padding field
4973
* @param {Function} validator
@@ -144,6 +168,23 @@ define([
144168
},
145169
$.mage.__(requiredInputRule.message)
146170
);
171+
172+
validator.addRule(
173+
'validate-message-no-link',
174+
function (url,message) {
175+
return validateOneAnchorTagField(message, url);
176+
},
177+
$.mage.__('Adding link in both content and outer element is not allowed')
178+
);
179+
180+
validator.addRule(
181+
'validate-no-url',
182+
function (message,url) {
183+
return validateOneAnchorTagField(message, url);
184+
},
185+
$.mage.__('Adding link in both content and outer element is not allowed')
186+
);
187+
147188
validateObjectField(validator, 'validate-number');
148189
validateObjectField(validator, 'less-than-equals-to');
149190
validateObjectField(validator, 'greater-than-equals-to');
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
/**
7+
* Extend the confirmation prompt to allow for an additional checkbox to be displayed. The checkbox enables the user to
8+
* dismiss subsequent prompts of the same type based on their dismissKey.
9+
*
10+
* Once a type is dismissed a cookie is set and all future instances of that prompt are bypassed. This cookie is
11+
* cleared once the user logs out and back in via an observer.
12+
*/
13+
define([
14+
'jquery',
15+
'underscore',
16+
'text!Magento_PageBuilder/template/modal/dismissible-modal-content.html',
17+
'mage/translate',
18+
'Magento_Ui/js/modal/prompt',
19+
'mage/cookies'
20+
], function ($, _, promptContentTmpl) {
21+
'use strict';
22+
23+
$.widget('mage.dismissibleConfirm', $.mage.prompt, {
24+
options: {
25+
promptContentTmpl: promptContentTmpl,
26+
buttons: [{
27+
text: $.mage.__('Revert Link'),
28+
class: 'action-primary action-accept',
29+
30+
/**
31+
* Click handler.
32+
*/
33+
click: function () {
34+
this.closeModal(true);
35+
}
36+
}]
37+
},
38+
});
39+
40+
return function (config) {
41+
return $('<div></div>').html(config.content).dismissibleConfirm(config);
42+
};
43+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!--
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
-->
7+
<!--render select with link types-->
8+
<div class="admin__field url-input-container"
9+
visible="visible"
10+
css="$data.additionalClasses"
11+
attr="'data-index': index">
12+
<label class="admin__field-label" if="$data.label" visible="$data.labelVisible" attr="for: uid">
13+
<span translate="label" attr="'data-config-scope': $data.scopeLabel"/>
14+
</label>
15+
<div class="admin__field-control"
16+
css="'_with-tooltip': $data.tooltip, '_with-reset': $data.showFallbackReset && $data.isDifferedFromDefault">
17+
<div class="type-selector-input-container">
18+
<!--render link types select-->
19+
<render args="typeSelectorTemplate"/>
20+
<!--display field to insert link value based on link type-->
21+
<div ko-scope="getLinkedElementName()" class="url-input-element-linked-element">
22+
<render/>
23+
</div>
24+
</div>
25+
<label class="admin__field-error" visible="error" attr="for: uid" text="error"/>
26+
<!--display container to specify url options(Example: open in new tab)-->
27+
<div render="settingTemplate" if="isDisplayAdditionalSettings"/>
28+
</div>
29+
</div>

app/code/Magento/PageBuilder/view/adminhtml/web/ts/js/content-type/banner/preview.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
import $ from "jquery";
77
import $t from "mage/translate";
88
import events from "Magento_PageBuilder/js/events";
9+
import singleButtonDialog from "Magento_PageBuilder/js/modal/nest-link-dialog";
910
import Config from "../../config";
1011
import HideShowOption from "../../content-type-menu/hide-show-option";
1112
import {OptionsInterface} from "../../content-type-menu/option.d";
1213
import {DataObject} from "../../data-store";
14+
import FieldDefaultsInterface from "../../field-defaults.d";
1315
import Uploader from "../../uploader";
1416
import WysiwygFactory from "../../wysiwyg/factory";
1517
import WysiwygInterface from "../../wysiwyg/wysiwyg-interface";
@@ -243,6 +245,29 @@ export default class Preview extends BasePreview {
243245
const dataStore = this.parent.dataStore.get() as DataObject;
244246
const imageObject = dataStore[this.config.additional_data.uploaderConfig.dataScope][0] || {};
245247
events.trigger(`image:${this.parent.id}:assignAfter`, imageObject);
248+
const message = dataStore.message as string;
249+
const linkUrl = dataStore.link_url as FieldDefaultsInterface;
250+
const aLinkRegex = /<a.*?>|<\/a>/igm;
251+
if (message.match(aLinkRegex) && dataStore.link_url && ((linkUrl.type === "page"
252+
&& linkUrl.page && linkUrl.page.length !== 0) || (linkUrl.type === "product" && linkUrl.product
253+
&& linkUrl.product.length !== 0) || (linkUrl.type === "category" && linkUrl.category
254+
&& linkUrl.category.length !== 0) || (linkUrl.type === "default" && linkUrl.default
255+
&& linkUrl.default.length !== 0))) {
256+
singleButtonDialog({
257+
actions: {
258+
confirm: () => {
259+
const anchorLessMessage = message.replace(aLinkRegex, "");
260+
// Call the parent to remove the child element
261+
this.parent.dataStore.update(anchorLessMessage, "message");
262+
$("#" + this.wysiwyg.elementId).html(anchorLessMessage);
263+
},
264+
},
265+
content: $t("Adding link in both contents and the outer element will result in nesting links. " +
266+
"This may break the structure of the page elements."), // tslint:disable-line:max-line-length
267+
title: $t("Nesting links are not allowed"),
268+
buttonText: "Revert Link",
269+
});
270+
}
246271
});
247272
}
248273

app/code/Magento/PageBuilder/view/adminhtml/web/ts/js/content-type/slide/preview.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import WysiwygInterface from "../../wysiwyg/wysiwyg-interface";
1919
import ContentTypeMountEventParamsInterface from "../content-type-mount-event-params";
2020
import BasePreview from "../preview";
2121
import SliderPreview from "../slider/preview";
22+
import FieldDefaultsInterface from "../../field-defaults";
23+
import singleButtonDialog from "Magento_PageBuilder/js/modal/nest-link-dialog";
2224

2325
/**
2426
* @api
@@ -286,6 +288,29 @@ export default class Preview extends BasePreview {
286288
const dataStore = this.parent.dataStore.get() as DataObject;
287289
const imageObject = dataStore[this.config.additional_data.uploaderConfig.dataScope][0] || {};
288290
events.trigger(`image:${this.parent.id}:assignAfter`, imageObject);
291+
const content = dataStore.content as string;
292+
const linkUrl = dataStore.link_url as FieldDefaultsInterface;
293+
const aLinkRegex = /<a.*?>|<\/a>/igm;
294+
if (content.match(aLinkRegex) && dataStore.link_url && ((linkUrl.type === "page"
295+
&& linkUrl.page && linkUrl.page.length !== 0) || (linkUrl.type === "product" && linkUrl.product
296+
&& linkUrl.product.length !== 0) || (linkUrl.type === "category" && linkUrl.category
297+
&& linkUrl.category.length !== 0) || (linkUrl.type === "default" && linkUrl.default
298+
&& linkUrl.default.length !== 0))) {
299+
singleButtonDialog({
300+
actions: {
301+
confirm: () => {
302+
const anchorLessMessage = content.replace(aLinkRegex, "");
303+
// Call the parent to remove the child element
304+
this.parent.dataStore.update(anchorLessMessage, "content");
305+
$("#" + this.wysiwyg.elementId).html(anchorLessMessage);
306+
},
307+
},
308+
content: $t("Adding link in both contents and the outer element will result in nesting links. " +
309+
"This may break the structure of the page elements."), // tslint:disable-line:max-line-length
310+
title: $t("Nesting links are not allowed"),
311+
buttonText: "Revert Link",
312+
});
313+
}
289314
});
290315

291316
// Remove wysiwyg before assign new instance.

0 commit comments

Comments
 (0)