@nyaruka/temba-components 0.129.8 → 0.129.10
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/CHANGELOG.md +37 -3
- package/demo/data/flows/sample-flow.json +186 -96
- package/demo/test-colorpicker.html +30 -0
- package/dist/temba-components.js +1126 -1111
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/excellent/helpers.js +2 -2
- package/out-tsc/src/excellent/helpers.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +25 -7
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +11 -1
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +133 -290
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +40 -0
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/call_llm.js +56 -3
- package/out-tsc/src/flow/actions/call_llm.js.map +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js.map +1 -1
- package/out-tsc/src/flow/actions/open_ticket.js +65 -3
- package/out-tsc/src/flow/actions/open_ticket.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +75 -0
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/config.js +4 -0
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +227 -0
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js +18 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js +27 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +0 -65
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +63 -117
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/BaseListEditor.js +4 -3
- package/out-tsc/src/form/BaseListEditor.js.map +1 -1
- package/out-tsc/src/form/Checkbox.js +77 -24
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/ColorPicker.js +28 -40
- package/out-tsc/src/form/ColorPicker.js.map +1 -1
- package/out-tsc/src/form/Completion.js +44 -53
- package/out-tsc/src/form/Completion.js.map +1 -1
- package/out-tsc/src/form/Compose.js +7 -8
- package/out-tsc/src/form/Compose.js.map +1 -1
- package/out-tsc/src/form/ContactSearch.js +3 -4
- package/out-tsc/src/form/ContactSearch.js.map +1 -1
- package/out-tsc/src/form/DatePicker.js +29 -36
- package/out-tsc/src/form/DatePicker.js.map +1 -1
- package/out-tsc/src/form/{FormField.js → FieldElement.js} +81 -53
- package/out-tsc/src/form/FieldElement.js.map +1 -0
- package/out-tsc/src/form/FieldRenderer.js +306 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -0
- package/out-tsc/src/form/ImagePicker.js +122 -126
- package/out-tsc/src/form/ImagePicker.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +41 -37
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
- package/out-tsc/src/form/MessageEditor.js +55 -63
- package/out-tsc/src/form/MessageEditor.js.map +1 -1
- package/out-tsc/src/form/TembaSlider.js +3 -3
- package/out-tsc/src/form/TembaSlider.js.map +1 -1
- package/out-tsc/src/form/TemplateEditor.js +3 -3
- package/out-tsc/src/form/TemplateEditor.js.map +1 -1
- package/out-tsc/src/form/TextInput.js +23 -27
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +57 -35
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/form/select/UserSelect.js +8 -9
- package/out-tsc/src/form/select/UserSelect.js.map +1 -1
- package/out-tsc/src/form/select/WorkspaceSelect.js +7 -8
- package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +62 -44
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/ContactFieldEditor.js.map +1 -1
- package/out-tsc/src/markdown.js +13 -11
- package/out-tsc/src/markdown.js.map +1 -1
- package/out-tsc/temba-modules.js +3 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +2 -0
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +148 -0
- package/out-tsc/test/NodeHelper.js.map +1 -0
- package/out-tsc/test/actions/call_llm.test.js +103 -0
- package/out-tsc/test/actions/call_llm.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js +532 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +150 -0
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +150 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_response.test.js +171 -0
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -0
- package/out-tsc/test/temba-add-input-labels.test.js +70 -0
- package/out-tsc/test/temba-add-input-labels.test.js.map +1 -0
- package/out-tsc/test/temba-checkbox.test.js +16 -0
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-field-renderer.test.js +296 -0
- package/out-tsc/test/temba-field-renderer.test.js.map +1 -0
- package/out-tsc/test/temba-integration-markdown.test.js +2 -4
- package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-markdown.test.js +1 -1
- package/out-tsc/test/temba-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +400 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +6 -3
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-slider.test.js +0 -1
- package/out-tsc/test/temba-slider.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +1 -1
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/render/translation-task.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
- package/screenshots/truth/checkbox/checked.png +0 -0
- package/screenshots/truth/checkbox/default.png +0 -0
- package/screenshots/truth/colorpicker/default.png +0 -0
- package/screenshots/truth/colorpicker/focused.png +0 -0
- package/screenshots/truth/colorpicker/initialized.png +0 -0
- package/screenshots/truth/colorpicker/selected.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/send_msg.png +0 -0
- package/screenshots/truth/editor/set_contact_language.png +0 -0
- package/screenshots/truth/editor/set_contact_name.png +0 -0
- package/screenshots/truth/editor/set_run_result.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/context-comparison.png +0 -0
- package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
- package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
- package/screenshots/truth/field-renderer/select-multi.png +0 -0
- package/screenshots/truth/field-renderer/select-no-label.png +0 -0
- package/screenshots/truth/field-renderer/select-with-label.png +0 -0
- package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/text-no-label.png +0 -0
- package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/text-with-label.png +0 -0
- package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
- package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/src/events.ts +12 -6
- package/src/excellent/helpers.ts +2 -2
- package/src/flow/CanvasNode.ts +22 -1
- package/src/flow/Editor.ts +12 -1
- package/src/flow/NodeEditor.ts +186 -374
- package/src/flow/actions/add_input_labels.ts +45 -0
- package/src/flow/actions/call_llm.ts +57 -3
- package/src/flow/actions/call_webhook.ts +1 -1
- package/src/flow/actions/open_ticket.ts +74 -3
- package/src/flow/actions/set_run_result.ts +83 -0
- package/src/flow/config.ts +4 -0
- package/src/flow/nodes/split_by_llm_categorize.ts +277 -0
- package/src/flow/nodes/split_by_ticket.ts +19 -0
- package/src/flow/nodes/wait_for_response.ts +28 -1
- package/src/flow/types.ts +26 -127
- package/src/form/ArrayEditor.ts +79 -139
- package/src/form/BaseListEditor.ts +4 -4
- package/src/form/Checkbox.ts +81 -24
- package/src/form/ColorPicker.ts +31 -43
- package/src/form/Completion.ts +49 -56
- package/src/form/Compose.ts +8 -8
- package/src/form/ContactSearch.ts +3 -4
- package/src/form/DatePicker.ts +32 -38
- package/src/form/{FormField.ts → FieldElement.ts} +108 -55
- package/src/form/FieldRenderer.ts +466 -0
- package/src/form/ImagePicker.ts +107 -110
- package/src/form/KeyValueEditor.ts +43 -39
- package/src/form/MessageEditor.ts +61 -67
- package/src/form/TembaSlider.ts +3 -3
- package/src/form/TemplateEditor.ts +3 -3
- package/src/form/TextInput.ts +26 -29
- package/src/form/select/Select.ts +63 -37
- package/src/form/select/UserSelect.ts +10 -11
- package/src/form/select/WorkspaceSelect.ts +9 -10
- package/src/live/ContactChat.ts +62 -47
- package/src/live/ContactFieldEditor.ts +2 -2
- package/src/markdown.ts +19 -11
- package/src/store/flow-definition.d.ts +5 -2
- package/static/api/labels.json +31 -0
- package/static/api/topics.json +24 -9
- package/static/api/users.json +35 -16
- package/static/css/temba-components.css +3 -3
- package/stress-test.js +18 -13
- package/temba-modules.ts +3 -2
- package/test/ActionHelper.ts +2 -0
- package/test/NodeHelper.ts +184 -0
- package/test/actions/call_llm.test.ts +137 -0
- package/test/nodes/README.md +78 -0
- package/test/nodes/split_by_llm_categorize.test.ts +698 -0
- package/test/nodes/split_by_random.test.ts +177 -0
- package/test/nodes/wait_for_digits.test.ts +176 -0
- package/test/nodes/wait_for_response.test.ts +206 -0
- package/test/temba-add-input-labels.test.ts +87 -0
- package/test/temba-checkbox.test.ts +26 -0
- package/test/temba-field-renderer.test.ts +482 -0
- package/test/temba-integration-markdown.test.ts +2 -4
- package/test/temba-markdown.test.ts +1 -1
- package/test/temba-node-editor.test.ts +496 -0
- package/test/temba-select.test.ts +6 -6
- package/test/temba-slider.test.ts +0 -1
- package/test/temba-webchat.test.ts +1 -1
- package/test-assets/contacts/history.json +7 -20
- package/test-assets/select/llms.json +18 -0
- package/web-dev-mock.mjs +96 -6
- package/web-dev-server.config.mjs +29 -7
- package/out-tsc/src/form/FormElement.js +0 -67
- package/out-tsc/src/form/FormElement.js.map +0 -1
- package/out-tsc/src/form/FormField.js.map +0 -1
- package/out-tsc/test/temba-formfield.test.js +0 -94
- package/out-tsc/test/temba-formfield.test.js.map +0 -1
- package/src/form/FormElement.ts +0 -69
- package/test/temba-flow-editor.test.ts.backup +0 -563
- package/test/temba-formfield.test.ts +0 -121
- package/test/temba-utils-index.test.ts.backup +0 -1737
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
import { html, fixture, expect } from '@open-wc/testing';
|
|
2
|
-
import { Editor } from '../src/flow/Editor';
|
|
3
|
-
import { Plumber } from '../src/flow/Plumber';
|
|
4
|
-
import { stub, restore } from 'sinon';
|
|
5
|
-
|
|
6
|
-
// Register the component
|
|
7
|
-
customElements.define('temba-flow-editor', Editor);
|
|
8
|
-
|
|
9
|
-
describe('Editor', () => {
|
|
10
|
-
let editor: Editor;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
// Reset any stubs
|
|
14
|
-
restore();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
restore();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe('basic functionality', () => {
|
|
22
|
-
it('creates render root as element itself', () => {
|
|
23
|
-
const editor = new Editor();
|
|
24
|
-
expect(editor.createRenderRoot()).to.equal(editor);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('has correct CSS styles defined', () => {
|
|
28
|
-
const styles = Editor.styles;
|
|
29
|
-
expect(styles).to.exist;
|
|
30
|
-
expect(styles.cssText).to.contain('#editor');
|
|
31
|
-
expect(styles.cssText).to.contain('#grid');
|
|
32
|
-
expect(styles.cssText).to.contain('#canvas');
|
|
33
|
-
expect(styles.cssText).to.contain('.plumb-source');
|
|
34
|
-
expect(styles.cssText).to.contain('.plumb-target');
|
|
35
|
-
expect(styles.cssText).to.contain('.plumb-connector');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('creates with default properties', () => {
|
|
39
|
-
editor = new Editor();
|
|
40
|
-
expect(editor.flow).to.be.undefined;
|
|
41
|
-
expect(editor.version).to.be.undefined;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('accepts flow and version properties without getStore call', async () => {
|
|
45
|
-
editor = document.createElement('temba-flow-editor') as Editor;
|
|
46
|
-
editor.flow = 'test-flow-uuid';
|
|
47
|
-
editor.version = '1.0';
|
|
48
|
-
|
|
49
|
-
expect(editor.flow).to.equal('test-flow-uuid');
|
|
50
|
-
expect(editor.version).to.equal('1.0');
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('lifecycle methods', () => {
|
|
55
|
-
it('calls firstUpdated and initializes plumber', async () => {
|
|
56
|
-
editor = await fixture(html`
|
|
57
|
-
<temba-flow-editor>
|
|
58
|
-
<div id="canvas"></div>
|
|
59
|
-
</temba-flow-editor>
|
|
60
|
-
`);
|
|
61
|
-
|
|
62
|
-
// Verify that plumber is initialized
|
|
63
|
-
expect((editor as any).plumber).to.be.instanceOf(Plumber);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('verifies firstUpdated method exists and can be called', () => {
|
|
67
|
-
editor = new Editor();
|
|
68
|
-
|
|
69
|
-
// Mock canvas element
|
|
70
|
-
const mockCanvas = document.createElement('div');
|
|
71
|
-
mockCanvas.id = 'canvas';
|
|
72
|
-
|
|
73
|
-
// Mock querySelector to return our mock canvas
|
|
74
|
-
stub(editor, 'querySelector').returns(mockCanvas);
|
|
75
|
-
|
|
76
|
-
// Verify firstUpdated method exists
|
|
77
|
-
expect(typeof (editor as any).firstUpdated).to.equal('function');
|
|
78
|
-
|
|
79
|
-
// Test that calling firstUpdated doesn't throw (without getStore)
|
|
80
|
-
expect(() => {
|
|
81
|
-
// Only test the plumber initialization part
|
|
82
|
-
(editor as any).plumber = new Plumber(mockCanvas);
|
|
83
|
-
}).to.not.throw();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('handles updated with canvasSize changes', async () => {
|
|
87
|
-
editor = await fixture(html`
|
|
88
|
-
<temba-flow-editor>
|
|
89
|
-
<div id="canvas"></div>
|
|
90
|
-
</temba-flow-editor>
|
|
91
|
-
`);
|
|
92
|
-
|
|
93
|
-
// Simulate canvasSize change
|
|
94
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
95
|
-
const changes = new Map();
|
|
96
|
-
changes.set('canvasSize', true);
|
|
97
|
-
|
|
98
|
-
(editor as any).updated(changes);
|
|
99
|
-
|
|
100
|
-
// Verify the canvasSize was set correctly
|
|
101
|
-
expect((editor as any).canvasSize).to.deep.equal({
|
|
102
|
-
width: 800,
|
|
103
|
-
height: 600
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('handles updated without canvasSize changes', async () => {
|
|
108
|
-
editor = await fixture(html`
|
|
109
|
-
<temba-flow-editor>
|
|
110
|
-
<div id="canvas"></div>
|
|
111
|
-
</temba-flow-editor>
|
|
112
|
-
`);
|
|
113
|
-
|
|
114
|
-
const consoleStub = stub(console, 'log');
|
|
115
|
-
|
|
116
|
-
const changes = new Map();
|
|
117
|
-
changes.set('other', true);
|
|
118
|
-
|
|
119
|
-
(editor as any).updated(changes);
|
|
120
|
-
|
|
121
|
-
expect(consoleStub).to.not.have.been.called;
|
|
122
|
-
|
|
123
|
-
consoleStub.restore();
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
describe('render method', () => {
|
|
128
|
-
it('renders loading when no definition', async () => {
|
|
129
|
-
editor = await fixture(html`
|
|
130
|
-
<temba-flow-editor>
|
|
131
|
-
<div id="canvas"></div>
|
|
132
|
-
</temba-flow-editor>
|
|
133
|
-
`);
|
|
134
|
-
|
|
135
|
-
// Set canvas size to avoid undefined errors
|
|
136
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
137
|
-
await editor.updateComplete;
|
|
138
|
-
|
|
139
|
-
const loadingElement = editor.querySelector('temba-loading');
|
|
140
|
-
expect(loadingElement).to.exist;
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('renders nodes when definition exists', async () => {
|
|
144
|
-
const mockDefinition = {
|
|
145
|
-
nodes: [
|
|
146
|
-
{
|
|
147
|
-
uuid: 'node-1',
|
|
148
|
-
actions: [],
|
|
149
|
-
exits: []
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
uuid: 'node-2',
|
|
153
|
-
actions: [],
|
|
154
|
-
exits: []
|
|
155
|
-
}
|
|
156
|
-
],
|
|
157
|
-
_ui: {
|
|
158
|
-
nodes: {
|
|
159
|
-
'node-1': { position: { left: 100, top: 200 } },
|
|
160
|
-
'node-2': { position: { left: 300, top: 400 } }
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
editor = await fixture(html`
|
|
166
|
-
<temba-flow-editor>
|
|
167
|
-
<div id="canvas"></div>
|
|
168
|
-
</temba-flow-editor>
|
|
169
|
-
`);
|
|
170
|
-
|
|
171
|
-
// Set properties
|
|
172
|
-
(editor as any).definition = mockDefinition;
|
|
173
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
174
|
-
await editor.updateComplete;
|
|
175
|
-
|
|
176
|
-
const flowNodes = editor.querySelectorAll('temba-flow-node');
|
|
177
|
-
expect(flowNodes).to.have.length(2);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('includes style elements in light DOM', async () => {
|
|
181
|
-
editor = await fixture(html`
|
|
182
|
-
<temba-flow-editor>
|
|
183
|
-
<div id="canvas"></div>
|
|
184
|
-
</temba-flow-editor>
|
|
185
|
-
`);
|
|
186
|
-
|
|
187
|
-
// Set canvas size
|
|
188
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
189
|
-
await editor.updateComplete;
|
|
190
|
-
|
|
191
|
-
const styleElements = editor.querySelectorAll('style');
|
|
192
|
-
expect(styleElements.length).to.be.greaterThan(0);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('renders with correct grid dimensions', async () => {
|
|
196
|
-
editor = await fixture(html`
|
|
197
|
-
<temba-flow-editor>
|
|
198
|
-
<div id="canvas"></div>
|
|
199
|
-
</temba-flow-editor>
|
|
200
|
-
`);
|
|
201
|
-
|
|
202
|
-
// Set canvas size to specific dimensions
|
|
203
|
-
(editor as any).canvasSize = { width: 1200, height: 900 };
|
|
204
|
-
await editor.updateComplete;
|
|
205
|
-
|
|
206
|
-
const gridElement = editor.querySelector('#grid');
|
|
207
|
-
expect(gridElement).to.exist;
|
|
208
|
-
|
|
209
|
-
// Check that grid has correct dimensions
|
|
210
|
-
const gridStyle = gridElement.getAttribute('style');
|
|
211
|
-
expect(gridStyle).to.contain('width:1200px');
|
|
212
|
-
expect(gridStyle).to.contain('height:900px');
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('multi-selection functionality', () => {
|
|
217
|
-
let mockDefinition: any;
|
|
218
|
-
|
|
219
|
-
beforeEach(() => {
|
|
220
|
-
mockDefinition = {
|
|
221
|
-
nodes: [
|
|
222
|
-
{
|
|
223
|
-
uuid: 'node-1',
|
|
224
|
-
actions: [],
|
|
225
|
-
exits: []
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
uuid: 'node-2',
|
|
229
|
-
actions: [],
|
|
230
|
-
exits: []
|
|
231
|
-
}
|
|
232
|
-
],
|
|
233
|
-
_ui: {
|
|
234
|
-
nodes: {
|
|
235
|
-
'node-1': { position: { left: 100, top: 200 } },
|
|
236
|
-
'node-2': { position: { left: 300, top: 400 } }
|
|
237
|
-
},
|
|
238
|
-
stickies: {
|
|
239
|
-
'sticky-1': {
|
|
240
|
-
position: { left: 200, top: 100 },
|
|
241
|
-
title: 'Test Sticky',
|
|
242
|
-
body: 'Test content',
|
|
243
|
-
color: 'yellow'
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('initializes with empty selection', async () => {
|
|
251
|
-
editor = await fixture(html`
|
|
252
|
-
<temba-flow-editor>
|
|
253
|
-
<div id="canvas"></div>
|
|
254
|
-
</temba-flow-editor>
|
|
255
|
-
`);
|
|
256
|
-
|
|
257
|
-
expect((editor as any).selectedItems.size).to.equal(0);
|
|
258
|
-
expect((editor as any).isSelecting).to.be.false;
|
|
259
|
-
expect((editor as any).selectionBox).to.be.null;
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('adds visual selection styling to selected items', async () => {
|
|
263
|
-
editor = await fixture(html`
|
|
264
|
-
<temba-flow-editor>
|
|
265
|
-
<div id="canvas"></div>
|
|
266
|
-
</temba-flow-editor>
|
|
267
|
-
`);
|
|
268
|
-
|
|
269
|
-
// Set up mock definition and selection
|
|
270
|
-
(editor as any).definition = mockDefinition;
|
|
271
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
272
|
-
(editor as any).selectedItems = new Set(['node-1']);
|
|
273
|
-
await editor.updateComplete;
|
|
274
|
-
|
|
275
|
-
const selectedNode = editor.querySelector('temba-flow-node[uuid="node-1"]');
|
|
276
|
-
expect(selectedNode).to.exist;
|
|
277
|
-
expect(selectedNode.classList.contains('selected')).to.be.true;
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it('renders selection box when selecting', async () => {
|
|
281
|
-
editor = await fixture(html`
|
|
282
|
-
<temba-flow-editor>
|
|
283
|
-
<div id="canvas"></div>
|
|
284
|
-
</temba-flow-editor>
|
|
285
|
-
`);
|
|
286
|
-
|
|
287
|
-
// Set up selection state
|
|
288
|
-
(editor as any).definition = mockDefinition;
|
|
289
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
290
|
-
(editor as any).isSelecting = true;
|
|
291
|
-
(editor as any).selectionBox = {
|
|
292
|
-
startX: 50,
|
|
293
|
-
startY: 50,
|
|
294
|
-
endX: 150,
|
|
295
|
-
endY: 150
|
|
296
|
-
};
|
|
297
|
-
await editor.updateComplete;
|
|
298
|
-
|
|
299
|
-
const selectionBox = editor.querySelector('.selection-box');
|
|
300
|
-
expect(selectionBox).to.exist;
|
|
301
|
-
|
|
302
|
-
const style = selectionBox.getAttribute('style');
|
|
303
|
-
expect(style).to.contain('left: 50px');
|
|
304
|
-
expect(style).to.contain('top: 50px');
|
|
305
|
-
expect(style).to.contain('width: 100px');
|
|
306
|
-
expect(style).to.contain('height: 100px');
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('does not render selection box when not selecting', async () => {
|
|
310
|
-
editor = await fixture(html`
|
|
311
|
-
<temba-flow-editor>
|
|
312
|
-
<div id="canvas"></div>
|
|
313
|
-
</temba-flow-editor>
|
|
314
|
-
`);
|
|
315
|
-
|
|
316
|
-
(editor as any).definition = mockDefinition;
|
|
317
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
318
|
-
(editor as any).isSelecting = false;
|
|
319
|
-
await editor.updateComplete;
|
|
320
|
-
|
|
321
|
-
const selectionBox = editor.querySelector('.selection-box');
|
|
322
|
-
expect(selectionBox).to.not.exist;
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('handles canvas mouse down for selection start', async () => {
|
|
326
|
-
editor = await fixture(html`
|
|
327
|
-
<temba-flow-editor>
|
|
328
|
-
<div id="canvas"></div>
|
|
329
|
-
</temba-flow-editor>
|
|
330
|
-
`);
|
|
331
|
-
|
|
332
|
-
(editor as any).definition = mockDefinition;
|
|
333
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
334
|
-
|
|
335
|
-
// Pre-select an item
|
|
336
|
-
(editor as any).selectedItems.add('node-1');
|
|
337
|
-
await editor.updateComplete;
|
|
338
|
-
|
|
339
|
-
const canvas = editor.querySelector('#canvas');
|
|
340
|
-
const mockEvent = new MouseEvent('mousedown', {
|
|
341
|
-
clientX: 100,
|
|
342
|
-
clientY: 100,
|
|
343
|
-
bubbles: true
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
// Mock getBoundingClientRect for canvas
|
|
347
|
-
stub(canvas, 'getBoundingClientRect').returns({
|
|
348
|
-
left: 0,
|
|
349
|
-
top: 0,
|
|
350
|
-
width: 800,
|
|
351
|
-
height: 600
|
|
352
|
-
} as DOMRect);
|
|
353
|
-
|
|
354
|
-
// Set event target to canvas
|
|
355
|
-
Object.defineProperty(mockEvent, 'target', {
|
|
356
|
-
value: canvas,
|
|
357
|
-
writable: false
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
(editor as any).handleCanvasMouseDown(mockEvent);
|
|
361
|
-
|
|
362
|
-
// Should clear selection and start selection box
|
|
363
|
-
expect((editor as any).selectedItems.size).to.equal(0);
|
|
364
|
-
expect((editor as any).canvasMouseDown).to.be.true;
|
|
365
|
-
expect((editor as any).selectionBox).to.not.be.null;
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('handles keyboard delete key with confirmation', async () => {
|
|
369
|
-
editor = await fixture(html`
|
|
370
|
-
<temba-flow-editor>
|
|
371
|
-
<div id="canvas"></div>
|
|
372
|
-
</temba-flow-editor>
|
|
373
|
-
`);
|
|
374
|
-
|
|
375
|
-
// Mock window.confirm
|
|
376
|
-
const confirmStub = stub(window, 'confirm');
|
|
377
|
-
confirmStub.returns(true);
|
|
378
|
-
|
|
379
|
-
// Set up selection
|
|
380
|
-
(editor as any).selectedItems.add('node-1');
|
|
381
|
-
|
|
382
|
-
const mockEvent = new KeyboardEvent('keydown', { key: 'Delete' });
|
|
383
|
-
(editor as any).handleKeyDown(mockEvent);
|
|
384
|
-
|
|
385
|
-
expect(confirmStub).to.have.been.calledWith('Are you sure you want to delete 1 item?');
|
|
386
|
-
|
|
387
|
-
confirmStub.restore();
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it('handles escape key to clear selection', async () => {
|
|
391
|
-
editor = await fixture(html`
|
|
392
|
-
<temba-flow-editor>
|
|
393
|
-
<div id="canvas"></div>
|
|
394
|
-
</temba-flow-editor>
|
|
395
|
-
`);
|
|
396
|
-
|
|
397
|
-
// Set up selection
|
|
398
|
-
(editor as any).selectedItems.add('node-1');
|
|
399
|
-
(editor as any).selectedItems.add('node-2');
|
|
400
|
-
|
|
401
|
-
const mockEvent = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
402
|
-
(editor as any).handleKeyDown(mockEvent);
|
|
403
|
-
|
|
404
|
-
expect((editor as any).selectedItems.size).to.equal(0);
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
it('updates selection box coordinates during mouse move', async () => {
|
|
408
|
-
editor = await fixture(html`
|
|
409
|
-
<temba-flow-editor>
|
|
410
|
-
<div id="canvas"></div>
|
|
411
|
-
</temba-flow-editor>
|
|
412
|
-
`);
|
|
413
|
-
|
|
414
|
-
const canvas = editor.querySelector('#canvas');
|
|
415
|
-
|
|
416
|
-
// Mock getBoundingClientRect
|
|
417
|
-
stub(canvas, 'getBoundingClientRect').returns({
|
|
418
|
-
left: 0,
|
|
419
|
-
top: 0,
|
|
420
|
-
width: 800,
|
|
421
|
-
height: 600
|
|
422
|
-
} as DOMRect);
|
|
423
|
-
|
|
424
|
-
// Set up initial selection state
|
|
425
|
-
(editor as any).canvasMouseDown = true;
|
|
426
|
-
(editor as any).selectionBox = {
|
|
427
|
-
startX: 50,
|
|
428
|
-
startY: 50,
|
|
429
|
-
endX: 50,
|
|
430
|
-
endY: 50
|
|
431
|
-
};
|
|
432
|
-
(editor as any).definition = mockDefinition;
|
|
433
|
-
|
|
434
|
-
const mockEvent = new MouseEvent('mousemove', {
|
|
435
|
-
clientX: 150,
|
|
436
|
-
clientY: 150
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
(editor as any).updateSelectionBox(mockEvent);
|
|
440
|
-
|
|
441
|
-
expect((editor as any).selectionBox.endX).to.equal(150);
|
|
442
|
-
expect((editor as any).selectionBox.endY).to.equal(150);
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it('calculates intersections correctly for node selection', async () => {
|
|
446
|
-
editor = await fixture(html`
|
|
447
|
-
<temba-flow-editor>
|
|
448
|
-
<div id="canvas"></div>
|
|
449
|
-
</temba-flow-editor>
|
|
450
|
-
`);
|
|
451
|
-
|
|
452
|
-
(editor as any).definition = mockDefinition;
|
|
453
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
454
|
-
await editor.updateComplete;
|
|
455
|
-
|
|
456
|
-
// Mock node element and its bounding rect
|
|
457
|
-
const nodeElement = editor.querySelector('temba-flow-node[uuid="node-1"]');
|
|
458
|
-
stub(nodeElement, 'getBoundingClientRect').returns({
|
|
459
|
-
width: 200,
|
|
460
|
-
height: 100
|
|
461
|
-
} as DOMRect);
|
|
462
|
-
|
|
463
|
-
// Set selection box that intersects with node-1 (position: left: 100, top: 200)
|
|
464
|
-
(editor as any).selectionBox = {
|
|
465
|
-
startX: 50, // Selection box from 50,150 to 250,250
|
|
466
|
-
startY: 150, // This should intersect node-1 at 100,200 with size 200x100
|
|
467
|
-
endX: 250,
|
|
468
|
-
endY: 250
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
(editor as any).updateSelectedItemsFromBox();
|
|
472
|
-
|
|
473
|
-
expect((editor as any).selectedItems.has('node-1')).to.be.true;
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
describe('canvas initialization', () => {
|
|
478
|
-
<div id="canvas"></div>
|
|
479
|
-
</temba-flow-editor>
|
|
480
|
-
`);
|
|
481
|
-
|
|
482
|
-
(editor as any).canvasSize = { width: 800, height: 600 };
|
|
483
|
-
await editor.updateComplete;
|
|
484
|
-
|
|
485
|
-
const editorElement = editor.querySelector('#editor');
|
|
486
|
-
expect(editorElement).to.exist;
|
|
487
|
-
|
|
488
|
-
const gridElement = editor.querySelector('#grid');
|
|
489
|
-
expect(gridElement).to.exist;
|
|
490
|
-
|
|
491
|
-
const canvasElement = editor.querySelector('#canvas');
|
|
492
|
-
expect(canvasElement).to.exist;
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
describe('property handling', () => {
|
|
497
|
-
it('handles flow property change', async () => {
|
|
498
|
-
editor = await fixture(html`
|
|
499
|
-
<temba-flow-editor>
|
|
500
|
-
<div id="canvas"></div>
|
|
501
|
-
</temba-flow-editor>
|
|
502
|
-
`);
|
|
503
|
-
|
|
504
|
-
// Change flow property
|
|
505
|
-
editor.flow = 'new-flow-uuid';
|
|
506
|
-
await editor.updateComplete;
|
|
507
|
-
|
|
508
|
-
expect(editor.flow).to.equal('new-flow-uuid');
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
it('handles version property change', async () => {
|
|
512
|
-
editor = await fixture(html`
|
|
513
|
-
<temba-flow-editor>
|
|
514
|
-
<div id="canvas"></div>
|
|
515
|
-
</temba-flow-editor>
|
|
516
|
-
`);
|
|
517
|
-
|
|
518
|
-
editor.version = '2.0';
|
|
519
|
-
await editor.updateComplete;
|
|
520
|
-
|
|
521
|
-
expect(editor.version).to.equal('2.0');
|
|
522
|
-
});
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
describe('store integration', () => {
|
|
526
|
-
it('has fromStore decorators for definition and canvasSize', () => {
|
|
527
|
-
editor = new Editor();
|
|
528
|
-
|
|
529
|
-
// Check that the properties exist (they are private but we can verify they exist)
|
|
530
|
-
expect(editor).to.have.property('definition');
|
|
531
|
-
expect(editor).to.have.property('canvasSize');
|
|
532
|
-
});
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
describe('constructor behavior', () => {
|
|
536
|
-
it('calls super in constructor', () => {
|
|
537
|
-
// This mainly verifies the constructor doesn't throw
|
|
538
|
-
expect(() => {
|
|
539
|
-
new Editor();
|
|
540
|
-
}).to.not.throw();
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
describe('canvas initialization', () => {
|
|
545
|
-
it('initializes plumber with canvas element', async () => {
|
|
546
|
-
editor = await fixture(html`
|
|
547
|
-
<temba-flow-editor>
|
|
548
|
-
<div id="canvas"></div>
|
|
549
|
-
</temba-flow-editor>
|
|
550
|
-
`);
|
|
551
|
-
|
|
552
|
-
const plumber = (editor as any).plumber;
|
|
553
|
-
expect(plumber).to.be.instanceOf(Plumber);
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it('handles missing canvas element gracefully', async () => {
|
|
557
|
-
editor = await fixture(html`<temba-flow-editor></temba-flow-editor>`);
|
|
558
|
-
|
|
559
|
-
// Should not throw even without canvas
|
|
560
|
-
expect((editor as any).plumber).to.be.instanceOf(Plumber);
|
|
561
|
-
});
|
|
562
|
-
});
|
|
563
|
-
});
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { html, fixture, expect } from '@open-wc/testing';
|
|
2
|
-
import { FormField } from '../src/form/FormField';
|
|
3
|
-
import { assertScreenshot, getClip } from './utils.test';
|
|
4
|
-
|
|
5
|
-
describe('temba-field', () => {
|
|
6
|
-
it('renders field with plain text errors', async () => {
|
|
7
|
-
const formField: FormField = await fixture(html`
|
|
8
|
-
<temba-field
|
|
9
|
-
label="Test Field"
|
|
10
|
-
name="test"
|
|
11
|
-
.errors=${['This is a plain text error', 'Another error message']}
|
|
12
|
-
>
|
|
13
|
-
<input type="text" />
|
|
14
|
-
</temba-field>
|
|
15
|
-
`);
|
|
16
|
-
|
|
17
|
-
await formField.updateComplete;
|
|
18
|
-
|
|
19
|
-
// Check that errors are rendered
|
|
20
|
-
const errorElements = formField.shadowRoot.querySelectorAll('.alert-error');
|
|
21
|
-
expect(errorElements.length).to.equal(2);
|
|
22
|
-
expect(errorElements[0].textContent.trim()).to.equal(
|
|
23
|
-
'This is a plain text error'
|
|
24
|
-
);
|
|
25
|
-
expect(errorElements[1].textContent.trim()).to.equal(
|
|
26
|
-
'Another error message'
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
await assertScreenshot('formfield/plain-text-errors', getClip(formField));
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('renders field with markdown errors', async () => {
|
|
33
|
-
const formField: FormField = await fixture(html`
|
|
34
|
-
<temba-field
|
|
35
|
-
label="Test Field"
|
|
36
|
-
name="test"
|
|
37
|
-
.errors=${[
|
|
38
|
-
'This is **bold** text',
|
|
39
|
-
'This has a [link](https://example.com)',
|
|
40
|
-
'This is *italic* and **bold** with a [link](https://example.com)'
|
|
41
|
-
]}
|
|
42
|
-
>
|
|
43
|
-
<input type="text" />
|
|
44
|
-
</temba-field>
|
|
45
|
-
`);
|
|
46
|
-
|
|
47
|
-
await formField.updateComplete;
|
|
48
|
-
|
|
49
|
-
// Check that errors are rendered
|
|
50
|
-
const errorElements = formField.shadowRoot.querySelectorAll('.alert-error');
|
|
51
|
-
expect(errorElements.length).to.equal(3);
|
|
52
|
-
|
|
53
|
-
// First error should have bold text
|
|
54
|
-
const firstError = errorElements[0];
|
|
55
|
-
const boldElement = firstError.querySelector('strong');
|
|
56
|
-
expect(boldElement).to.not.be.null;
|
|
57
|
-
expect(boldElement.textContent).to.equal('bold');
|
|
58
|
-
|
|
59
|
-
// Second error should have a link
|
|
60
|
-
const secondError = errorElements[1];
|
|
61
|
-
const linkElement = secondError.querySelector('a');
|
|
62
|
-
expect(linkElement).to.not.be.null;
|
|
63
|
-
expect(linkElement.getAttribute('href')).to.equal('https://example.com');
|
|
64
|
-
expect(linkElement.textContent).to.equal('link');
|
|
65
|
-
|
|
66
|
-
// Third error should have both bold, italic, and link
|
|
67
|
-
const thirdError = errorElements[2];
|
|
68
|
-
const thirdBoldElement = thirdError.querySelector('strong');
|
|
69
|
-
const thirdItalicElement = thirdError.querySelector('em');
|
|
70
|
-
const thirdLinkElement = thirdError.querySelector('a');
|
|
71
|
-
expect(thirdBoldElement).to.not.be.null;
|
|
72
|
-
expect(thirdItalicElement).to.not.be.null;
|
|
73
|
-
expect(thirdLinkElement).to.not.be.null;
|
|
74
|
-
|
|
75
|
-
await assertScreenshot('formfield/markdown-errors', getClip(formField));
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('renders field without errors', async () => {
|
|
79
|
-
const formField: FormField = await fixture(html`
|
|
80
|
-
<temba-field label="Test Field" name="test">
|
|
81
|
-
<input type="text" />
|
|
82
|
-
</temba-field>
|
|
83
|
-
`);
|
|
84
|
-
|
|
85
|
-
await formField.updateComplete;
|
|
86
|
-
|
|
87
|
-
// Check that no errors are rendered
|
|
88
|
-
const errorElements = formField.shadowRoot.querySelectorAll('.alert-error');
|
|
89
|
-
expect(errorElements.length).to.equal(0);
|
|
90
|
-
|
|
91
|
-
await assertScreenshot('formfield/no-errors', getClip(formField));
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('renders in widget-only mode with errors', async () => {
|
|
95
|
-
const formField: FormField = await fixture(html`
|
|
96
|
-
<temba-field
|
|
97
|
-
widget_only
|
|
98
|
-
.errors=${['Widget only **error** with [link](https://example.com)']}
|
|
99
|
-
>
|
|
100
|
-
<input type="text" />
|
|
101
|
-
</temba-field>
|
|
102
|
-
`);
|
|
103
|
-
|
|
104
|
-
await formField.updateComplete;
|
|
105
|
-
|
|
106
|
-
// Check that error is rendered in widget-only mode
|
|
107
|
-
const errorElements = formField.shadowRoot.querySelectorAll('.alert-error');
|
|
108
|
-
expect(errorElements.length).to.equal(1);
|
|
109
|
-
|
|
110
|
-
const errorElement = errorElements[0];
|
|
111
|
-
const boldElement = errorElement.querySelector('strong');
|
|
112
|
-
const linkElement = errorElement.querySelector('a');
|
|
113
|
-
expect(boldElement).to.not.be.null;
|
|
114
|
-
expect(linkElement).to.not.be.null;
|
|
115
|
-
|
|
116
|
-
await assertScreenshot(
|
|
117
|
-
'formfield/widget-only-markdown-errors',
|
|
118
|
-
getClip(formField)
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
});
|