@capillarytech/creatives-library 9.0.4 → 9.0.6-beta.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/v2Components/NewCallTask/index.js +8 -5
- package/v2Containers/FTP/index.js +8 -5
- package/v2Containers/WebPush/Create/components/MessageSection.js +2 -8
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +141 -0
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +3 -0
package/package.json
CHANGED
|
@@ -40,15 +40,18 @@ class NewCallTask extends React.Component { // eslint-disable-line react/prefer-
|
|
|
40
40
|
this.insertAtCursor(`{{${data}}}`);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
setInputRef = (textarea) => {
|
|
44
|
+
this.textArea = textarea;
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
insertAtCursor = (myValue) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
textAreaRef.focus();
|
|
48
|
+
this.textArea.focus();
|
|
49
|
+
const textAreaRef = this.textArea.textAreaRef;
|
|
47
50
|
let pos = textAreaRef.selectionEnd + myValue.length;
|
|
48
51
|
//IE support
|
|
49
52
|
let newMessage = textAreaRef.value + myValue;
|
|
50
53
|
if (document.selection) {
|
|
51
|
-
|
|
54
|
+
this.textArea.focus();
|
|
52
55
|
const sel = document.selection.createRange();
|
|
53
56
|
sel.text = myValue;
|
|
54
57
|
} else if (textAreaRef.selectionStart || textAreaRef.selectionStart === '0') { //MOZILLA and others
|
|
@@ -125,7 +128,7 @@ class NewCallTask extends React.Component { // eslint-disable-line react/prefer-
|
|
|
125
128
|
injectedTags={injectedTags}
|
|
126
129
|
/>
|
|
127
130
|
<CapInput.TextArea
|
|
128
|
-
|
|
131
|
+
setInputRef={this.setInputRef}
|
|
129
132
|
label={formatMessage(messages.messageHeader)}
|
|
130
133
|
onChange={this.updateMessageBody}
|
|
131
134
|
value={messageBody}
|
|
@@ -166,6 +166,10 @@ export class FTP extends React.Component {
|
|
|
166
166
|
this.insertAtCursor(`{{${data}}}`);
|
|
167
167
|
};
|
|
168
168
|
|
|
169
|
+
setInputRef = (textarea) => {
|
|
170
|
+
this.textArea = textarea;
|
|
171
|
+
};
|
|
172
|
+
|
|
169
173
|
getServerSelectionContent = () => {
|
|
170
174
|
const { getFTPServersInProgress } = this.props.FTP || {};
|
|
171
175
|
const { formatMessage } = this.props.intl;
|
|
@@ -253,7 +257,7 @@ export class FTP extends React.Component {
|
|
|
253
257
|
injectedTags={injectedTags || {}}
|
|
254
258
|
/>
|
|
255
259
|
<CapInput.TextArea
|
|
256
|
-
|
|
260
|
+
setInputRef={this.setInputRef}
|
|
257
261
|
label={formatMessage(messages.messageHeader)}
|
|
258
262
|
onChange={this.updateMessageBody}
|
|
259
263
|
value={messageContent}
|
|
@@ -525,14 +529,13 @@ export class FTP extends React.Component {
|
|
|
525
529
|
};
|
|
526
530
|
|
|
527
531
|
insertAtCursor = (myValue) => {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
textAreaRef.focus();
|
|
532
|
+
this.textArea.focus();
|
|
533
|
+
const textAreaRef = this.textArea.textAreaRef;
|
|
531
534
|
let pos = textAreaRef.selectionEnd + myValue.length;
|
|
532
535
|
//IE support
|
|
533
536
|
let newMessage = textAreaRef.value + myValue;
|
|
534
537
|
if (document.selection) {
|
|
535
|
-
|
|
538
|
+
this.textArea.focus();
|
|
536
539
|
const sel = document.selection.createRange();
|
|
537
540
|
sel.text = myValue;
|
|
538
541
|
} else if (textAreaRef.selectionStart || textAreaRef.selectionStart === '0') { //MOZILLA and others
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { FormattedMessage } from 'react-intl';
|
|
4
4
|
import CapInput from '@capillarytech/cap-ui-library/CapInput';
|
|
@@ -73,13 +73,6 @@ export const MessageSection = ({
|
|
|
73
73
|
handleMessageTextAreaRef,
|
|
74
74
|
isAiContentBotDisabled,
|
|
75
75
|
}) => {
|
|
76
|
-
// resolve the underlying DOM
|
|
77
|
-
// <textarea> by id and feed it to the existing ref handler. CapEmojiPicker and
|
|
78
|
-
// the Aira trigger both read `messageTextAreaRef.current` as that DOM node.
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
handleMessageTextAreaRef(document.getElementById('webpush-message-input'));
|
|
81
|
-
}, [handleMessageTextAreaRef]);
|
|
82
|
-
|
|
83
76
|
const renderCharacterCount = () => {
|
|
84
77
|
if (!SHOW_CHARACTER_COUNT) return null;
|
|
85
78
|
|
|
@@ -126,6 +119,7 @@ export const MessageSection = ({
|
|
|
126
119
|
size="default"
|
|
127
120
|
isRequired
|
|
128
121
|
autosize={{ minRows: 3, maxRows: 5 }}
|
|
122
|
+
setInputRef={handleMessageTextAreaRef}
|
|
129
123
|
errorMessage={
|
|
130
124
|
error && (
|
|
131
125
|
<CapError className="webpush-template-message-error">
|
|
@@ -700,6 +700,147 @@ describe('PreviewControls', () => {
|
|
|
700
700
|
});
|
|
701
701
|
});
|
|
702
702
|
|
|
703
|
+
describe('getPopupContainer (getDropdownPopupContainer)', () => {
|
|
704
|
+
const getResolver = (wrapper) =>
|
|
705
|
+
wrapper.find(CapSelect.CapCustomSelect).first().prop('getPopupContainer');
|
|
706
|
+
|
|
707
|
+
let appended;
|
|
708
|
+
|
|
709
|
+
beforeEach(() => {
|
|
710
|
+
appended = [];
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
afterEach(() => {
|
|
714
|
+
// Keep jsdom clean between assertions so containers from one test don't leak
|
|
715
|
+
appended.forEach((node) => {
|
|
716
|
+
if (node && node.parentNode) {
|
|
717
|
+
node.parentNode.removeChild(node);
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
appended = [];
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
const mountInBody = (node) => {
|
|
724
|
+
document.body.appendChild(node);
|
|
725
|
+
appended.push(node);
|
|
726
|
+
return node;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
it('should pass getPopupContainer to every rendered select', () => {
|
|
730
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} showStateDropdown={true} />);
|
|
731
|
+
const selects = wrapper.find(CapSelect.CapCustomSelect);
|
|
732
|
+
expect(selects.length).toBe(3);
|
|
733
|
+
selects.forEach((select) => {
|
|
734
|
+
expect(typeof select.prop('getPopupContainer')).toBe('function');
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it('should return the closest .ant-modal-container ancestor when one exists', () => {
|
|
739
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
740
|
+
const getPopupContainer = getResolver(wrapper);
|
|
741
|
+
|
|
742
|
+
const modalContainer = document.createElement('div');
|
|
743
|
+
modalContainer.className = 'ant-modal-container';
|
|
744
|
+
const triggerNode = document.createElement('span');
|
|
745
|
+
modalContainer.appendChild(triggerNode);
|
|
746
|
+
mountInBody(modalContainer);
|
|
747
|
+
|
|
748
|
+
expect(getPopupContainer(triggerNode)).toBe(modalContainer);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it('should return the closest .ant-modal-content ancestor when one exists', () => {
|
|
752
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
753
|
+
const getPopupContainer = getResolver(wrapper);
|
|
754
|
+
|
|
755
|
+
const modalContent = document.createElement('div');
|
|
756
|
+
modalContent.className = 'ant-modal-content';
|
|
757
|
+
const triggerNode = document.createElement('span');
|
|
758
|
+
modalContent.appendChild(triggerNode);
|
|
759
|
+
mountInBody(modalContent);
|
|
760
|
+
|
|
761
|
+
expect(getPopupContainer(triggerNode)).toBe(modalContent);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
it('should resolve through deeply nested descendants up to the modal ancestor', () => {
|
|
765
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
766
|
+
const getPopupContainer = getResolver(wrapper);
|
|
767
|
+
|
|
768
|
+
const modalContainer = document.createElement('div');
|
|
769
|
+
modalContainer.className = 'ant-modal-container';
|
|
770
|
+
const middle = document.createElement('div');
|
|
771
|
+
const triggerNode = document.createElement('span');
|
|
772
|
+
middle.appendChild(triggerNode);
|
|
773
|
+
modalContainer.appendChild(middle);
|
|
774
|
+
mountInBody(modalContainer);
|
|
775
|
+
|
|
776
|
+
expect(getPopupContainer(triggerNode)).toBe(modalContainer);
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it('should match the trigger node itself when it is the modal container', () => {
|
|
780
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
781
|
+
const getPopupContainer = getResolver(wrapper);
|
|
782
|
+
|
|
783
|
+
const modalContainer = document.createElement('div');
|
|
784
|
+
modalContainer.className = 'ant-modal-content';
|
|
785
|
+
mountInBody(modalContainer);
|
|
786
|
+
|
|
787
|
+
// closest() includes the element itself, so a self-match should be returned
|
|
788
|
+
expect(getPopupContainer(modalContainer)).toBe(modalContainer);
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it('should pick the nearest modal ancestor when modals are nested', () => {
|
|
792
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
793
|
+
const getPopupContainer = getResolver(wrapper);
|
|
794
|
+
|
|
795
|
+
const outerModal = document.createElement('div');
|
|
796
|
+
outerModal.className = 'ant-modal-container';
|
|
797
|
+
const innerModal = document.createElement('div');
|
|
798
|
+
innerModal.className = 'ant-modal-content';
|
|
799
|
+
const triggerNode = document.createElement('span');
|
|
800
|
+
innerModal.appendChild(triggerNode);
|
|
801
|
+
outerModal.appendChild(innerModal);
|
|
802
|
+
mountInBody(outerModal);
|
|
803
|
+
|
|
804
|
+
expect(getPopupContainer(triggerNode)).toBe(innerModal);
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
it('should fall back to document.body when the trigger is not inside a modal', () => {
|
|
808
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
809
|
+
const getPopupContainer = getResolver(wrapper);
|
|
810
|
+
|
|
811
|
+
const wrapperDiv = document.createElement('div');
|
|
812
|
+
wrapperDiv.className = 'some-unrelated-container';
|
|
813
|
+
const triggerNode = document.createElement('span');
|
|
814
|
+
wrapperDiv.appendChild(triggerNode);
|
|
815
|
+
mountInBody(wrapperDiv);
|
|
816
|
+
|
|
817
|
+
expect(getPopupContainer(triggerNode)).toBe(document.body);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('should fall back to document.body for a detached (unattached) trigger node', () => {
|
|
821
|
+
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
|
822
|
+
const getPopupContainer = getResolver(wrapper);
|
|
823
|
+
|
|
824
|
+
const detachedNode = document.createElement('span');
|
|
825
|
+
expect(getPopupContainer(detachedNode)).toBe(document.body);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it('should resolve the same way for the compact layout selects', () => {
|
|
829
|
+
const wrapper = mountWithIntl(
|
|
830
|
+
<PreviewControls {...defaultProps} layoutMode={LAYOUT_MODE.COMPACT} showStateDropdown={true} />,
|
|
831
|
+
);
|
|
832
|
+
const getPopupContainer = getResolver(wrapper);
|
|
833
|
+
|
|
834
|
+
const modalContainer = document.createElement('div');
|
|
835
|
+
modalContainer.className = 'ant-modal-container';
|
|
836
|
+
const triggerNode = document.createElement('span');
|
|
837
|
+
modalContainer.appendChild(triggerNode);
|
|
838
|
+
mountInBody(modalContainer);
|
|
839
|
+
|
|
840
|
+
expect(getPopupContainer(triggerNode)).toBe(modalContainer);
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
|
|
703
844
|
describe('FormattedMessage Integration', () => {
|
|
704
845
|
it('should render FormattedMessage for operatingSystem label', () => {
|
|
705
846
|
const wrapper = mountWithIntl(<PreviewControls {...defaultProps} />);
|
package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap
CHANGED
|
@@ -21,6 +21,7 @@ exports[`PreviewControls Basic Rendering should render correctly with default pr
|
|
|
21
21
|
/>
|
|
22
22
|
</CapLabel>
|
|
23
23
|
<ComponentWithLabel(InjectIntl(CapCustomSelect))
|
|
24
|
+
getPopupContainer={[Function]}
|
|
24
25
|
onChange={[Function]}
|
|
25
26
|
options={
|
|
26
27
|
Array [
|
|
@@ -70,6 +71,7 @@ exports[`PreviewControls Basic Rendering should render correctly with default pr
|
|
|
70
71
|
/>
|
|
71
72
|
</CapLabel>
|
|
72
73
|
<ComponentWithLabel(InjectIntl(CapCustomSelect))
|
|
74
|
+
getPopupContainer={[Function]}
|
|
73
75
|
onChange={[Function]}
|
|
74
76
|
options={
|
|
75
77
|
Array [
|
|
@@ -119,6 +121,7 @@ exports[`PreviewControls Basic Rendering should render correctly with default pr
|
|
|
119
121
|
/>
|
|
120
122
|
</CapLabel>
|
|
121
123
|
<ComponentWithLabel(InjectIntl(CapCustomSelect))
|
|
124
|
+
getPopupContainer={[Function]}
|
|
122
125
|
onChange={[MockFunction]}
|
|
123
126
|
options={
|
|
124
127
|
Array [
|