testomatio-editor-blocks 0.4.10 → 0.4.12

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.
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createReactBlockSpec } from "@blocknote/react";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { useSnippetAutocomplete } from "../snippetAutocomplete";
5
- function SnippetDropdown({ value, placeholder, suggestions, onSelect }) {
5
+ function SnippetDropdown({ value, placeholder, suggestions, selectedId, onSelect }) {
6
6
  const [isOpen, setIsOpen] = useState(false);
7
7
  const [search, setSearch] = useState("");
8
8
  const containerRef = useRef(null);
@@ -34,11 +34,14 @@ function SnippetDropdown({ value, placeholder, suggestions, onSelect }) {
34
34
  const handleSearchChange = useCallback((event) => {
35
35
  setSearch(event.target.value);
36
36
  }, []);
37
- return (_jsxs("div", { className: "bn-snippet-dropdown", ref: containerRef, children: [_jsxs("button", { type: "button", className: "bn-snippet-dropdown__trigger", onClick: () => setIsOpen((prev) => !prev), children: [_jsx("span", { className: "bn-snippet-dropdown__text", children: value || placeholder }), _jsx("svg", { className: "bn-snippet-dropdown__chevron", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })] }), isOpen && (_jsxs("div", { className: "bn-snippet-dropdown__panel", role: "listbox", children: [_jsxs("div", { className: "bn-snippet-dropdown__search", children: [_jsx("svg", { className: "bn-snippet-dropdown__search-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M15.5 14H14.71L14.43 13.73C15.41 12.59 16 11.11 16 9.5C16 5.91 13.09 3 9.5 3C5.91 3 3 5.91 3 9.5C3 13.09 5.91 16 9.5 16C11.11 16 12.59 15.41 13.73 14.43L14 14.71V15.5L19 20.49L20.49 19L15.5 14ZM9.5 14C7.01 14 5 11.99 5 9.5C5 7.01 7.01 5 9.5 5C11.99 5 14 7.01 14 9.5C14 11.99 11.99 14 9.5 14Z", fill: "currentColor" }) }), _jsx("input", { ref: searchRef, type: "text", className: "bn-snippet-dropdown__search-input", placeholder: "Search", value: search, onChange: handleSearchChange })] }), _jsxs("div", { className: "bn-snippet-dropdown__list", children: [filtered.map((suggestion) => (_jsx("button", { type: "button", role: "option", className: "bn-snippet-dropdown__item", onMouseDown: (event) => {
38
- event.preventDefault();
39
- onSelect(suggestion);
40
- setIsOpen(false);
41
- }, tabIndex: -1, children: suggestion.title }, suggestion.id))), filtered.length === 0 && (_jsx("div", { className: "bn-snippet-dropdown__empty", children: "No snippets found" }))] })] }))] }));
37
+ return (_jsxs("div", { className: "bn-snippet-dropdown", ref: containerRef, children: [_jsxs("button", { type: "button", className: "bn-snippet-dropdown__trigger", onClick: () => setIsOpen((prev) => !prev), children: [_jsx("span", { className: "bn-snippet-dropdown__text", children: value || placeholder }), _jsx("svg", { className: "bn-snippet-dropdown__chevron", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })] }), isOpen && (_jsxs("div", { className: "bn-snippet-dropdown__panel", role: "listbox", children: [_jsx("div", { className: "bn-snippet-dropdown__search-wrapper", children: _jsxs("div", { className: "bn-snippet-dropdown__search", children: [_jsx("svg", { className: "bn-snippet-dropdown__search-icon", width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M12.917 11.667h-.659l-.233-.225a5.417 5.417 0 0 0 1.308-3.525 5.417 5.417 0 1 0-5.416 5.416 5.417 5.417 0 0 0 3.525-1.308l.225.233v.659l4.166 4.158 1.242-1.242-4.158-4.166Zm-5 0a3.745 3.745 0 0 1-3.75-3.75 3.745 3.745 0 0 1 3.75-3.75 3.745 3.745 0 0 1 3.75 3.75 3.745 3.745 0 0 1-3.75 3.75Z", fill: "currentColor" }) }), _jsx("input", { ref: searchRef, type: "text", className: "bn-snippet-dropdown__search-input", placeholder: "Search", value: search, onChange: handleSearchChange })] }) }), _jsxs("div", { className: "bn-snippet-dropdown__list", children: [filtered.map((suggestion) => {
38
+ const isSelected = suggestion.id === selectedId;
39
+ return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, className: `bn-snippet-dropdown__item${isSelected ? " bn-snippet-dropdown__item--selected" : ""}`, onMouseDown: (event) => {
40
+ event.preventDefault();
41
+ onSelect(suggestion);
42
+ setIsOpen(false);
43
+ }, tabIndex: -1, children: [_jsx("span", { className: "bn-snippet-dropdown__item-title", children: suggestion.title }), isSelected && (_jsx("svg", { className: "bn-snippet-dropdown__item-check", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17Z", fill: "currentColor" }) }))] }, suggestion.id));
44
+ }), filtered.length === 0 && (_jsx("div", { className: "bn-snippet-dropdown__empty", children: "No snippets found" }))] })] }))] }));
42
45
  }
43
46
  export const snippetBlock = createReactBlockSpec({
44
47
  type: "snippet",
@@ -81,11 +84,24 @@ export const snippetBlock = createReactBlockSpec({
81
84
  });
82
85
  }, [block.id, editor]);
83
86
  const handleFieldFocus = useCallback(() => {
84
- editor.setSelection(block.id, block.id);
87
+ var _a, _b, _c;
88
+ const selection = editor.getSelection();
89
+ const blocks = (_a = selection === null || selection === void 0 ? void 0 : selection.blocks) !== null && _a !== void 0 ? _a : [];
90
+ const firstId = (_b = blocks[0]) === null || _b === void 0 ? void 0 : _b.id;
91
+ const lastId = (_c = blocks[blocks.length - 1]) === null || _c === void 0 ? void 0 : _c.id;
92
+ if (firstId === block.id && lastId === block.id) {
93
+ return;
94
+ }
95
+ try {
96
+ editor.setSelection(block.id, block.id);
97
+ }
98
+ catch {
99
+ //
100
+ }
85
101
  }, [editor, block.id]);
86
102
  if (!hasSnippets) {
87
103
  return (_jsx("div", { className: "bn-teststep bn-snippet", "data-block-id": block.id, children: _jsx("p", { className: "bn-snippet__empty", children: "No snippets in this project." }) }));
88
104
  }
89
- return (_jsxs("div", { className: "bn-teststep bn-snippet", "data-block-id": block.id, onFocus: handleFieldFocus, children: [_jsxs("div", { className: "bn-snippet__header", children: [_jsx("span", { className: "bn-snippet__label", children: "Snippet" }), _jsx(SnippetDropdown, { value: snippetTitle, placeholder: "Select Snippet", suggestions: snippetSuggestions, onSelect: handleSnippetSelect })] }), isSnippetSelected && (_jsx("div", { className: "bn-snippet__content", children: snippetData }))] }));
105
+ return (_jsxs("div", { className: "bn-teststep bn-snippet", "data-block-id": block.id, onFocus: handleFieldFocus, children: [_jsxs("div", { className: "bn-snippet__header", children: [_jsx("span", { className: "bn-snippet__label", children: "Snippet" }), _jsx(SnippetDropdown, { value: snippetTitle, placeholder: "Select Snippet", suggestions: snippetSuggestions, selectedId: snippetId, onSelect: handleSnippetSelect })] }), isSnippetSelected && snippetData && (_jsx("div", { className: "bn-snippet__content", dangerouslySetInnerHTML: { __html: snippetData } }))] }));
90
106
  },
91
107
  });
@@ -224,6 +224,10 @@ html.dark .bn-snippet-dropdown__panel {
224
224
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
225
225
  }
226
226
 
227
+ html.dark .bn-snippet-dropdown__search-wrapper {
228
+ border-bottom-color: #404040;
229
+ }
230
+
227
231
  html.dark .bn-snippet-dropdown__search {
228
232
  border-color: #404040;
229
233
  }
@@ -240,8 +244,12 @@ html.dark .bn-snippet-dropdown__search-input::placeholder {
240
244
  color: #525252;
241
245
  }
242
246
 
243
- html.dark .bn-snippet-dropdown__list {
244
- border-top-color: #404040;
247
+ html.dark .bn-snippet-dropdown__item--selected {
248
+ background: #333333;
249
+ }
250
+
251
+ html.dark .bn-snippet-dropdown__item-check {
252
+ color: #fafafa;
245
253
  }
246
254
 
247
255
  html.dark .bn-snippet-dropdown__chevron {
@@ -609,7 +617,7 @@ html.dark .bn-step-image-preview__content {
609
617
  position: absolute;
610
618
  left: 0;
611
619
  top: calc(100% + 4px);
612
- width: 260px;
620
+ width: 280px;
613
621
  background: white;
614
622
  border: 1px solid #e5e5e5;
615
623
  border-radius: 8px;
@@ -620,13 +628,17 @@ html.dark .bn-step-image-preview__content {
620
628
  padding: 8px 0;
621
629
  }
622
630
 
631
+ .bn-snippet-dropdown__search-wrapper {
632
+ padding: 4px 16px 12px;
633
+ border-bottom: 1px solid #e5e5e5;
634
+ }
635
+
623
636
  .bn-snippet-dropdown__search {
624
637
  display: flex;
625
638
  align-items: center;
626
639
  gap: 8px;
627
640
  height: 32px;
628
641
  padding: 0 12px;
629
- margin: 0 16px 12px;
630
642
  border: 1px solid #d4d4d4;
631
643
  border-radius: 6px;
632
644
  transition: border-color 120ms ease;
@@ -638,8 +650,8 @@ html.dark .bn-step-image-preview__content {
638
650
 
639
651
  .bn-snippet-dropdown__search-icon {
640
652
  flex-shrink: 0;
641
- width: 24px;
642
- height: 24px;
653
+ width: 20px;
654
+ height: 20px;
643
655
  color: #a4a4a4;
644
656
  }
645
657
 
@@ -663,7 +675,6 @@ html.dark .bn-step-image-preview__content {
663
675
  .bn-snippet-dropdown__list {
664
676
  max-height: 252px;
665
677
  overflow-y: auto;
666
- border-top: 1px solid #e5e5e5;
667
678
  }
668
679
 
669
680
  .bn-snippet-dropdown__item {
@@ -682,9 +693,25 @@ html.dark .bn-step-image-preview__content {
682
693
  color: var(--text-primary);
683
694
  text-align: left;
684
695
  cursor: pointer;
685
- white-space: nowrap;
696
+ }
697
+
698
+ .bn-snippet-dropdown__item-title {
699
+ flex: 1;
700
+ min-width: 0;
686
701
  overflow: hidden;
687
702
  text-overflow: ellipsis;
703
+ white-space: nowrap;
704
+ }
705
+
706
+ .bn-snippet-dropdown__item-check {
707
+ flex-shrink: 0;
708
+ width: 16px;
709
+ height: 16px;
710
+ color: var(--text-primary);
711
+ }
712
+
713
+ .bn-snippet-dropdown__item--selected {
714
+ background: #fafafa;
688
715
  }
689
716
 
690
717
  .bn-snippet-dropdown__item:hover {
@@ -1253,7 +1280,7 @@ html.dark .bn-step-image-preview__content {
1253
1280
  }
1254
1281
 
1255
1282
  .bn-step-suggestion__title {
1256
- font-weight: 600;
1283
+ font-weight: 400;
1257
1284
  font-size: 0.95rem;
1258
1285
  }
1259
1286
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testomatio-editor-blocks",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
4
4
  "description": "Custom BlockNote schema, markdown conversion helpers, and UI for Testomatio-style test cases and steps.",
5
5
  "type": "module",
6
6
  "main": "./package/index.js",
@@ -7,10 +7,11 @@ type SnippetDropdownProps = {
7
7
  value: string;
8
8
  placeholder: string;
9
9
  suggestions: SnippetSuggestion[];
10
+ selectedId: string;
10
11
  onSelect: (suggestion: SnippetSuggestion) => void;
11
12
  };
12
13
 
13
- function SnippetDropdown({ value, placeholder, suggestions, onSelect }: SnippetDropdownProps) {
14
+ function SnippetDropdown({ value, placeholder, suggestions, selectedId, onSelect }: SnippetDropdownProps) {
14
15
  const [isOpen, setIsOpen] = useState(false);
15
16
  const [search, setSearch] = useState("");
16
17
  const containerRef = useRef<HTMLDivElement>(null);
@@ -61,36 +62,47 @@ function SnippetDropdown({ value, placeholder, suggestions, onSelect }: SnippetD
61
62
  </button>
62
63
  {isOpen && (
63
64
  <div className="bn-snippet-dropdown__panel" role="listbox">
64
- <div className="bn-snippet-dropdown__search">
65
- <svg className="bn-snippet-dropdown__search-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
66
- <path d="M15.5 14H14.71L14.43 13.73C15.41 12.59 16 11.11 16 9.5C16 5.91 13.09 3 9.5 3C5.91 3 3 5.91 3 9.5C3 13.09 5.91 16 9.5 16C11.11 16 12.59 15.41 13.73 14.43L14 14.71V15.5L19 20.49L20.49 19L15.5 14ZM9.5 14C7.01 14 5 11.99 5 9.5C5 7.01 7.01 5 9.5 5C11.99 5 14 7.01 14 9.5C14 11.99 11.99 14 9.5 14Z" fill="currentColor"/>
67
- </svg>
68
- <input
69
- ref={searchRef}
70
- type="text"
71
- className="bn-snippet-dropdown__search-input"
72
- placeholder="Search"
73
- value={search}
74
- onChange={handleSearchChange}
75
- />
65
+ <div className="bn-snippet-dropdown__search-wrapper">
66
+ <div className="bn-snippet-dropdown__search">
67
+ <svg className="bn-snippet-dropdown__search-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
68
+ <path d="M12.917 11.667h-.659l-.233-.225a5.417 5.417 0 0 0 1.308-3.525 5.417 5.417 0 1 0-5.416 5.416 5.417 5.417 0 0 0 3.525-1.308l.225.233v.659l4.166 4.158 1.242-1.242-4.158-4.166Zm-5 0a3.745 3.745 0 0 1-3.75-3.75 3.745 3.745 0 0 1 3.75-3.75 3.745 3.745 0 0 1 3.75 3.75 3.745 3.745 0 0 1-3.75 3.75Z" fill="currentColor"/>
69
+ </svg>
70
+ <input
71
+ ref={searchRef}
72
+ type="text"
73
+ className="bn-snippet-dropdown__search-input"
74
+ placeholder="Search"
75
+ value={search}
76
+ onChange={handleSearchChange}
77
+ />
78
+ </div>
76
79
  </div>
77
80
  <div className="bn-snippet-dropdown__list">
78
- {filtered.map((suggestion) => (
79
- <button
80
- type="button"
81
- key={suggestion.id}
82
- role="option"
83
- className="bn-snippet-dropdown__item"
84
- onMouseDown={(event) => {
85
- event.preventDefault();
86
- onSelect(suggestion);
87
- setIsOpen(false);
88
- }}
89
- tabIndex={-1}
90
- >
91
- {suggestion.title}
92
- </button>
93
- ))}
81
+ {filtered.map((suggestion) => {
82
+ const isSelected = suggestion.id === selectedId;
83
+ return (
84
+ <button
85
+ type="button"
86
+ key={suggestion.id}
87
+ role="option"
88
+ aria-selected={isSelected}
89
+ className={`bn-snippet-dropdown__item${isSelected ? " bn-snippet-dropdown__item--selected" : ""}`}
90
+ onMouseDown={(event) => {
91
+ event.preventDefault();
92
+ onSelect(suggestion);
93
+ setIsOpen(false);
94
+ }}
95
+ tabIndex={-1}
96
+ >
97
+ <span className="bn-snippet-dropdown__item-title">{suggestion.title}</span>
98
+ {isSelected && (
99
+ <svg className="bn-snippet-dropdown__item-check" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
100
+ <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17Z" fill="currentColor"/>
101
+ </svg>
102
+ )}
103
+ </button>
104
+ );
105
+ })}
94
106
  {filtered.length === 0 && (
95
107
  <div className="bn-snippet-dropdown__empty">No snippets found</div>
96
108
  )}
@@ -148,7 +160,18 @@ export const snippetBlock = createReactBlockSpec(
148
160
  );
149
161
 
150
162
  const handleFieldFocus = useCallback(() => {
151
- editor.setSelection(block.id, block.id);
163
+ const selection = editor.getSelection();
164
+ const blocks = selection?.blocks ?? [];
165
+ const firstId = blocks[0]?.id;
166
+ const lastId = blocks[blocks.length - 1]?.id;
167
+ if (firstId === block.id && lastId === block.id) {
168
+ return;
169
+ }
170
+ try {
171
+ editor.setSelection(block.id, block.id);
172
+ } catch {
173
+ //
174
+ }
152
175
  }, [editor, block.id]);
153
176
 
154
177
  if (!hasSnippets) {
@@ -167,11 +190,12 @@ export const snippetBlock = createReactBlockSpec(
167
190
  value={snippetTitle}
168
191
  placeholder="Select Snippet"
169
192
  suggestions={snippetSuggestions}
193
+ selectedId={snippetId}
170
194
  onSelect={handleSnippetSelect}
171
195
  />
172
196
  </div>
173
- {isSnippetSelected && (
174
- <div className="bn-snippet__content">{snippetData}</div>
197
+ {isSnippetSelected && snippetData && (
198
+ <div className="bn-snippet__content" dangerouslySetInnerHTML={{ __html: snippetData }} />
175
199
  )}
176
200
  </div>
177
201
  );
@@ -224,6 +224,10 @@ html.dark .bn-snippet-dropdown__panel {
224
224
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
225
225
  }
226
226
 
227
+ html.dark .bn-snippet-dropdown__search-wrapper {
228
+ border-bottom-color: #404040;
229
+ }
230
+
227
231
  html.dark .bn-snippet-dropdown__search {
228
232
  border-color: #404040;
229
233
  }
@@ -240,8 +244,12 @@ html.dark .bn-snippet-dropdown__search-input::placeholder {
240
244
  color: #525252;
241
245
  }
242
246
 
243
- html.dark .bn-snippet-dropdown__list {
244
- border-top-color: #404040;
247
+ html.dark .bn-snippet-dropdown__item--selected {
248
+ background: #333333;
249
+ }
250
+
251
+ html.dark .bn-snippet-dropdown__item-check {
252
+ color: #fafafa;
245
253
  }
246
254
 
247
255
  html.dark .bn-snippet-dropdown__chevron {
@@ -609,7 +617,7 @@ html.dark .bn-step-image-preview__content {
609
617
  position: absolute;
610
618
  left: 0;
611
619
  top: calc(100% + 4px);
612
- width: 260px;
620
+ width: 280px;
613
621
  background: white;
614
622
  border: 1px solid #e5e5e5;
615
623
  border-radius: 8px;
@@ -620,13 +628,17 @@ html.dark .bn-step-image-preview__content {
620
628
  padding: 8px 0;
621
629
  }
622
630
 
631
+ .bn-snippet-dropdown__search-wrapper {
632
+ padding: 4px 16px 12px;
633
+ border-bottom: 1px solid #e5e5e5;
634
+ }
635
+
623
636
  .bn-snippet-dropdown__search {
624
637
  display: flex;
625
638
  align-items: center;
626
639
  gap: 8px;
627
640
  height: 32px;
628
641
  padding: 0 12px;
629
- margin: 0 16px 12px;
630
642
  border: 1px solid #d4d4d4;
631
643
  border-radius: 6px;
632
644
  transition: border-color 120ms ease;
@@ -638,8 +650,8 @@ html.dark .bn-step-image-preview__content {
638
650
 
639
651
  .bn-snippet-dropdown__search-icon {
640
652
  flex-shrink: 0;
641
- width: 24px;
642
- height: 24px;
653
+ width: 20px;
654
+ height: 20px;
643
655
  color: #a4a4a4;
644
656
  }
645
657
 
@@ -663,7 +675,6 @@ html.dark .bn-step-image-preview__content {
663
675
  .bn-snippet-dropdown__list {
664
676
  max-height: 252px;
665
677
  overflow-y: auto;
666
- border-top: 1px solid #e5e5e5;
667
678
  }
668
679
 
669
680
  .bn-snippet-dropdown__item {
@@ -682,9 +693,25 @@ html.dark .bn-step-image-preview__content {
682
693
  color: var(--text-primary);
683
694
  text-align: left;
684
695
  cursor: pointer;
685
- white-space: nowrap;
696
+ }
697
+
698
+ .bn-snippet-dropdown__item-title {
699
+ flex: 1;
700
+ min-width: 0;
686
701
  overflow: hidden;
687
702
  text-overflow: ellipsis;
703
+ white-space: nowrap;
704
+ }
705
+
706
+ .bn-snippet-dropdown__item-check {
707
+ flex-shrink: 0;
708
+ width: 16px;
709
+ height: 16px;
710
+ color: var(--text-primary);
711
+ }
712
+
713
+ .bn-snippet-dropdown__item--selected {
714
+ background: #fafafa;
688
715
  }
689
716
 
690
717
  .bn-snippet-dropdown__item:hover {
@@ -1253,7 +1280,7 @@ html.dark .bn-step-image-preview__content {
1253
1280
  }
1254
1281
 
1255
1282
  .bn-step-suggestion__title {
1256
- font-weight: 600;
1283
+ font-weight: 400;
1257
1284
  font-size: 0.95rem;
1258
1285
  }
1259
1286